Merging gst-libav

This commit is contained in:
Thibault Saunier 2021-09-24 16:15:07 -03:00
commit 97408bc7b6
50 changed files with 164143 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*~
Build
*.user
*.suo
*.ipch
*.sdf
*.opensdf
*.DS_Store

1
.gitlab-ci.yml Normal file
View file

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

1
AUTHORS Normal file
View file

@ -0,0 +1 @@
Ronald Bultje

503
COPYING Normal file
View 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!

14209
ChangeLog Normal file

File diff suppressed because it is too large Load diff

299
NEWS Normal file
View file

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

23
README.md Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

5
docs/index.md Normal file
View file

@ -0,0 +1,5 @@
---
short-description: GStreamer plugins from gst-ffmpeg
...
# FFMPEG plugin

79
docs/meson.build Normal file
View 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
View file

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

174
ext/libav/gstav.c Normal file
View 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
View 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
View 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 (&params);
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, &params);
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
View file

@ -0,0 +1,76 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

133
ext/libav/gstavcodecmap.h Normal file
View 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__ */

View 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

File diff suppressed because it is too large Load diff

962
ext/libav/gstavmux.c Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

94
ext/libav/gstavviddec.h Normal file
View 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

File diff suppressed because it is too large Load diff

72
ext/libav/gstavvidenc.h Normal file
View 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
View 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
View 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
View file

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

217
meson.build Normal file
View 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
View 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)

View 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))

View 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)

View 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)

View 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)

View 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)

View 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);

View 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);

View file

60
tests/check/meson.build Normal file
View 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

Binary file not shown.

BIN
tests/files/591809.wav Normal file

Binary file not shown.

3
tests/meson.build Normal file
View file

@ -0,0 +1,3 @@
if not get_option('tests').disabled() and gstcheck_dep.found()
subdir('check')
endif