This commit is contained in:
Akkariin Meiko
2022-03-12 03:16:09 +08:00
Unverified
parent 12b76e0c7a
commit 27c4ec74a1
10075 changed files with 5122287 additions and 1 deletions
@@ -0,0 +1,2 @@
*~
build*/
+504
View File
@@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+42
View File
@@ -0,0 +1,42 @@
== Gst-Validate
The goal of GstValidate is to be able to detect when elements are not
behaving as expected and report it to the user so he knows how things
are supposed to work inside a GstPipeline. In the end, fixing issues
found by the tool will ensure that all elements behave all together in
the expected way.
The easiest way of using GstValidate is to use one of its command-line
tools, located at tools/ directory. It is also possible to monitor
GstPipelines from any application by using the LD_PRELOAD gstvalidate
lib. The third way of using it is to write your own application that
links and uses libgstvalidate.
== BUILDING
Getting the code:
Releases are available at <URL>, download and extract the tarball. If you
want to use latest git version, do:
git clone <URI>
After cloning or extracting from a tarball, enter the gst-validate directory:
cd gst-validate
Build with:
meson build --prefix=<installation-prefix>
ninja -C build
sudo ninja -C build install (only if you want to install it)
Replace <installation-prefix> with your desired installation path, you can omit
the --prefix argument if you aren't going to install it or if you want the
default /usr/local. It is possible to use gst-validate CLI tools without
installation.
== INSTRUCTIONS
If you are looking for informations on how to use gst-validate -> docs/validate-usage.txt
If you are looking for informations on gst-validate design -> docs/validate-design.txt
@@ -0,0 +1,142 @@
# GStreamer
# Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
# Copyright (C) 2021 Stéphane Cerveau <scerveau@collabora.com>
#
# bash/zsh completion support for gst-validate
#
# 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.
_GST_HELPERDIR="${BASH_SOURCE[0]%/*}/../helpers"
if [[ ! -f $_GST_HELPERDIR/gst ]]; then
_GST_HELPERDIR="$(pkg-config --variable=bashhelpersdir gstreamer-1.0)"
else
_GST_HELPERDIR=`cd "$_GST_HELPERDIR"; pwd`
fi
# Common definitions
. "$_GST_HELPERDIR"/gst
_gst_validate_all_arguments ()
{
_gst_all_arguments gst-validate-1.0
}
_gst_complete_compatible_elements ()
{
COMPREPLY=( $(compgen -W "$($_GST_HELPER --compatible-with $previous_element)" -- $cur) )
}
_gst_complete_all_elements ()
{
COMPREPLY=( $(compgen -W "$($_GST_HELPER -l)" -- $cur) )
}
_gst_complete_element_properties ()
{
COMPREPLY=( $(compgen -W "$($_GST_HELPER --element-properties $previous_element)" -- $cur) )
}
_gstvalidate___exclude_ () { _gst_mandatory_argument gst-validate-1.0; }
_gst_validate_main ()
{
local i=1 command function_exists previous_element have_previous_element=0 completion_func
while [[ $i -ne $COMP_CWORD ]];
do
local var
var="${COMP_WORDS[i]}"
if [[ "$var" == "-"* ]]
then
command="$var"
fi
i=$(($i+1))
done
i=1
while [[ $i -ne $COMP_CWORD ]];
do
local var
var="${COMP_WORDS[i]}"
if [[ "$var" == "-"* ]]
then
i=$(($i+1))
continue
fi
$(gst-inspect-1.0 --exists $var)
if [ $? -eq 0 ]
then
previous_element="$var"
have_previous_element=1
fi
i=$(($i+1))
done
if [[ "$command" == "--gst"* ]]; then
completion_func="_${command//-/_}"
else
completion_func="_gstlaunch_${command//-/_}"
fi
# Seems like bash doesn't like "exclude" in function names
if [[ "$completion_func" == "_gstlaunch___exclude" ]]
then
completion_func="_gstvalidate___exclude_"
fi
declare -f $completion_func >/dev/null 2>&1
function_exists=$?
if [[ "$cur" == "-"* ]]; then
_gst_validate_all_arguments
elif [ $function_exists -eq 0 ]
then
$completion_func
elif [ $have_previous_element -ne 0 ] && [[ "$prev" == "!" ]]
then
_gst_complete_compatible_elements
elif [ $have_previous_element -ne 0 ]
then
_gst_complete_element_properties
else
_gst_complete_all_elements
fi
}
_gst_validate_func_wrap ()
{
local cur prev
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
$1
}
# Setup completion for certain functions defined above by setting common
# variables and workarounds.
# This is NOT a public function; use at your own risk.
_gst_validate_complete ()
{
local wrapper="__launch_wrap${2}"
eval "$wrapper () { _gst_validate_func_wrap $2 ; }"
complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \
|| complete -o default -o nospace -F $wrapper $1
}
_gst_validate_complete gst-validate-1.0 _gst_validate_main
@@ -0,0 +1,241 @@
### This file contains either validate specific suppressions or bugs that we
### can't easily address because they are lower in the stack.
### All the other suppressions should be added ton gstreamer/tests/check/gstreamer.supp
### Each set of suppression rules should be prefixed by either:
### - FIXED: if the bug/leak has been fixed upstream but we keep the rule
### because the fix may not be deployed yet (because it's lower in the
### stack and not in gst itself).
### - PENDING: if the bug/leak hasn't been fixed yet. In this case, please
### add an url to the bug report.
# PENDING: https://bugs.freedesktop.org/show_bug.cgi?id=90073
{
Leak in mesa fixed with http://lists.freedesktop.org/archives/mesa-dev/2015-April/082101.html
Memcheck:Leak
fun:malloc
fun:read_packet
fun:_xcb_in_read
fun:_xcb_conn_wait
fun:wait_for_reply
fun:xcb_wait_for_reply
fun:dri3_open
fun:dri3_create_screen
fun:AllocAndFetchScreenConfigs
fun:__glXInitialize
fun:glXQueryVersion
}
{
Leak in mesa fixed with http://lists.freedesktop.org/archives/mesa-dev/2015-April/082100.html
Memcheck:Leak
...
fun:get_render_node_from_id_path_tag
fun:loader_get_user_preferred_fd
fun:dri3_create_screen
fun:AllocAndFetchScreenConfigs
fun:__glXInitialize
fun:glXQueryVersion
}
# FIXED
{
Fixed in pixman master
Memcheck:Leak
fun:memalign
fun:allocate_and_init
fun:tls_get_addr_tail
}
# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=748417
{
Ignore all the invalid memory errors from libvpx
Memcheck:Cond
obj:/usr/lib64/libvpx.so*
}
{
Ignore all the invalid memory errors from libvpx
Memcheck:Value8
obj:/usr/lib64/libvpx.so*
}
# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=747110
{
https://bugzilla.gnome.org/show_bug.cgi?id=747110
Memcheck:Addr4
...
fun:aac_decode_frame_int
fun:aac_decode_frame
}
# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=752989
{
https://bugzilla.gnome.org/show_bug.cgi?id=752989
Memcheck:Value4
...
fun:aac_decode_frame_int
fun:aac_decode_frame
}
# PENDING: https://bugs.freedesktop.org/show_bug.cgi?id=90194
{
https://bugs.freedesktop.org/show_bug.cgi?id=90194
Memcheck:Param
ioctl(generic)
fun:ioctl
fun:drmIoctl
fun:drmPrimeHandleToFD
}
# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=749232
# x264 errors
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:x264_encoder_encode
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:x264_encoder_encode
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:x264_lookahead_thread
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:x264_lookahead_thread
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:x264_threadpool_thread
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
obj:/usr/lib64/libx264.so.*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
obj:/usr/lib64/libx264.so.*
}
# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=749428
# Theora encoder
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:theora_enc_handle_frame
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:theora_enc_handle_frame
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
fun:oc_enc_tokenize_ac
}
# FIXED
{
Fixed with mesa master
Memcheck:Cond
fun:lp_build_blend_factor_unswizzled
...
fun:gst_glimage_sink_on_draw
}
# FIXED
{
Fixed with mesa master
Memcheck:Leak
match-leak-kinds: definite
fun:calloc
...
fun:_do_convert_draw
fun:_do_convert_one_view
}
# FIXED
{
Fixed with mesa master
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
...
fun:gst_gl_shader_compile
}
# FIXED
{
Fixed with mesa master
Memcheck:Leak
match-leak-kinds: definite
fun:calloc
...
fun:_draw_checker_background
fun:_draw_background
fun:gst_gl_video_mixer_callback
}
{
#https://bugs.freedesktop.org/show_bug.cgi?id=8215
#https://bugs.freedesktop.org/show_bug.cgi?id=8428
#FcPattern uses 'intptr_t elts_offset' instead of 'FcPatternEltPtr elts',
#which confuses valgrind.
font_config_bug_2
Memcheck:Leak
fun:*alloc
...
fun:Fc*Add*
}
{
#Same root cause as font_config_bug_2.
#The 'leak' here is a copy of rule values, as opposed to new values.
font_config_bug_3
Memcheck:Leak
fun:*alloc
...
fun:FcConfigValues
}
{
#Same root cause as font_config_bug_2.
#The 'leak' is copies of font or pattern values into returned pattern values.
font_config_bug_4
Memcheck:Leak
fun:*alloc
...
fun:FcValue*
fun:FcFontRenderPrepare
}
{
font_config_bug_6
Memcheck:Leak
fun:*alloc
...
obj:*/libfontconfig.so.*
}
@@ -0,0 +1 @@
subdir('scenarios')
@@ -0,0 +1,5 @@
description, duration=15.0
set-restriction, playback-time=5.0, restriction-caps="video/x-raw,framerate=(fraction)5/1"
set-restriction, playback-time=10.0, restriction-caps="video/x-raw,framerate=(fraction)30/1"
eos, playback-time=15.0
stop, playback-time=15.0
@@ -0,0 +1,7 @@
description, duration=25.0
set-restriction, playback-time=5.0, restriction-caps="video/x-raw,framerate=(fraction)5/1"
set-restriction, playback-time=10.0, restriction-caps="video/x-raw,height=20,width=20,framerate=(fraction)5/1"
set-restriction, playback-time=15.0, restriction-caps="video/x-raw,height=20,width=20,framerate=(fraction)30/1"
set-restriction, playback-time=20.0, restriction-caps="video/x-raw,height=720,width=1280,framerate=(fraction)30/1"
eos, playback-time=25.0
stop, playback-time=25.0
@@ -0,0 +1,5 @@
description, duration=15.0
set-restriction, playback-time=5.0, restriction-caps="video/x-raw,height=480,width=854"
set-restriction, playback-time=10.0, restriction-caps="video/x-raw,height=720,width=1280"
eos, playback-time=15.0
stop, playback-time=15.0
@@ -0,0 +1,14 @@
description, duration=55.0, min-media-duration=470.0, seek=true, reverse-playback=true
include,location=includes/default-seek-flags.scenario
seek, name=backward-seek, playback-time=0.0, rate=-1.0, start=0.0, stop=310.0, flags="$(default_flags)"
seek, name=forward-seek, playback-time=305.0, rate=1.0, start=305.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time=310.0, rate=2.0, start=310.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=320.0, rate=-2.0, start=0.0, stop=320.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time=310.0, rate=4.0, start=310.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=330.0, rate=-4.0, start=0.0, stop=330.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time=310.0, rate=8.0, start=310.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=350.0, rate=-8.0, start=0.0, stop=350.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time=310.0, rate=16.0, start=310.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=390.0, rate=-16.0, start=0.0, stop=390.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time=310.0, rate=32.0, start=310.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=470.0, rate=-32.0, start=310.0, stop=470.0, flags="$(default_flags)"
@@ -0,0 +1,4 @@
description, duration=20.0
emit-signal, target-element-name=camerabin0, signal-name=start-capture, playback-time=10.0
eos, playback-time=20.0
@@ -0,0 +1,8 @@
description, duration=0, summary="Set state to NULL->PLAYING->NULL 20 times", need-clock-sync=true, min-media-duration=1.0, live_content_compatible=True, handles-states=true, ignore-eos=true
foreach, i=[0, 40],
actions = {
"set-state, state=playing",
"set-state, state=null",
}
stop;
@@ -0,0 +1,6 @@
description, summary="Disable subtitle track while pipeline is PAUSED", min-subtitle-track=2, duration=5.0, handles-states=true, needs_preroll=true
pause;
switch-track, name="Disable subtitle", type=text, disable=true
wait, duration=0.5
play;
stop, playback-time=2.0
@@ -0,0 +1,9 @@
description, duration=30.0, minfo-media-duration=310.0, seek=true, reverse-playback=true, need-clock-sync=true, min-media-duration=310.0, ignore-eos=true
include,location=includes/default-seek-flags.scenario
seek, name=Fast-backward-seek, playback-time=0.0, rate=-2.0, start=0.0, stop=310.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=300.0, rate=-4.0, start=0.0, stop=300.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=280.0, rate=-8.0, start=0.0, stop=280.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=240.0, rate=-16.0, start=0.0, stop=240.0, flags="$(default_flags)"
seek, name=Fast-backward-seek, playback-time=160.0, rate=-32.0, start=0.0, stop=160.0, flags="$(default_flags)"
wait, message-type=eos
stop
@@ -0,0 +1,8 @@
description, duration=25.0, seek=true, need-clock-sync=true, min-media-duration=5.0, ignore-eos=true
include,location=includes/default-seek-flags.scenario
seek, name=Fast-forward-seek, playback-time=0.0, rate=2.0, start=0.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time="min(10.0, $(duration) * 0.0625)", rate=4.0, start=0.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time="min(20.0, $(duration) * 0.125)", rate=8.0, start=0.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time="min(40.0, $(duration) * 0.25)", rate=16.0, start=0.0, flags="$(default_flags)"
seek, name=Fast-forward-seek, playback-time="min(80.0, $(duration) * 0.50)", rate=32.0, start=0.0, flags="$(default_flags)"
stop, playback-time="min($(duration) - 0.3, 160.0)", on-message="eos"
@@ -0,0 +1,4 @@
description, duration=2.0
video-request-key-unit, playback-time=1.0, direction=upstream, running_time=-1.0, all-header=true, count=1
eos, playback-time=2.0
stop, playback-time=2.0
@@ -0,0 +1 @@
set-property, target-element-factory-name="rtspsrc", property-name=default-rtsp-version, property-value=(string)"2-0"
@@ -0,0 +1,9 @@
# Rewinds a live source completely
# The goal is to check for proper EOS handling when going back to the
# beginning of the live cache
description, seek=true, live_content_required=true, duration=14.0
# Wait for 5s (can't use playback-time since we don't know the position(live))
wait, duration=5.0
# Seek back all the way as fast as possible (from the end, i.e. 'now')
seek, name=End-seek, rate=-16.0, stop=0.0, stop_type=end, start=0.0, start_type=set, flags=flush+trickmode-key-units+trickmode-no-audio
#eos
@@ -0,0 +1,2 @@
set-vars,\
default_flags=accurate+flush
@@ -0,0 +1,43 @@
_scenarios = ['simple_seeks.scenario',
'seek_forward.scenario',
'seek_backward.scenario',
'seek_forward_backward.scenario',
'reverse_playback.scenario',
'fast_forward.scenario',
'fast_backward.scenario',
'alternate_fast_backward_forward.scenario',
'pause_resume.scenario',
'scrub_forward_seeking.scenario',
'scrub_backward_seeking.scenario',
'scrub_forward_seeking_full.scenario',
'scrub_backward_seeking_full.scenario',
'adaptive_video_size.scenario',
'adaptive_video_framerate.scenario',
'adaptive_video_framerate_size.scenario',
'force_key_unit.scenario',
'seek_with_stop.scenario',
'switch_audio_track_while_paused.scenario',
'switch_subtitle_track.scenario',
'switch_subtitle_track_while_paused.scenario',
'disable_subtitle_track_while_paused.scenario',
'play_15s.scenario',
'play_5s.scenario',
'change_state_intensive.scenario',
'switch_audio_track.scenario',
'force_rtsp2.scenario',]
install_data(sources: _scenarios,
install_dir: get_option('datadir') + '/gstreamer-' +
apiversion + '/validate/scenarios')
install_subdir('includes',
install_dir: get_option('datadir') + '/gstreamer-' +
apiversion + '/validate/scenarios')
install_subdir('rtsp_overrides',
install_dir: get_option('datadir') + '/gstreamer-' +
apiversion + '/validate/scenarios')
env = environment()
env.prepend('GST_VALIDATE_SCENARIOS_PATH', meson.current_source_dir())
meson.add_devenv(env)
@@ -0,0 +1,6 @@
description, duration=14.0, min-media-duration=7.0
pause, name=First-pause, playback-time=1.0, duration=1.0
pause, name=Second-pause, playback-time=3.0, duration=5.0
pause, name=Third-pause, playback-time=5.0, duration=1.0
eos, name=Done-testing, playback-time=7.0
stop, playback-time=7.0
@@ -0,0 +1,3 @@
description, duration=15.0
eos, playback-time=15.0
stop, playback-time=15.0
@@ -0,0 +1,4 @@
description, duration=15.0, live_content_required=True
wait, duration=15.0
stop
@@ -0,0 +1,3 @@
description, duration=5.0
eos, playback-time=5.0
stop, playback-time=5.0
@@ -0,0 +1,3 @@
description, seek=true, reverse-playback=true
include,location=includes/default-seek-flags.scenario
seek, name=Reverse-seek, playback-time=0.0, rate=-1.0, start="max($(duration) - 15.0, 0.0)", stop="$(duration)", flags="$(default_flags)"
@@ -0,0 +1,2 @@
set-vars,\
default_flags=flush
@@ -0,0 +1,8 @@
description, seek=true, handles-states=true, needs_preroll=true
include,location=includes/default-seek-flags.scenario
pause, playback-time=0.0
seek, playback-time=0.0, start="$(duration) - 0.5", flags="$(default_flags)"
seek, playback-time=0.0, start=position-0.1, repeat="min(10, ($(duration) - 0.6))/0.1", flags="$(default_flags)"
play, playback-time=0.0
stop, playback-time=1.0
@@ -0,0 +1,8 @@
description, seek=true, handles-states=true, needs_preroll=true
include,location=includes/default-seek-flags.scenario
pause, playback-time=0.0
seek, playback-time=0.0, start="$(duration) - 0.5", flags="$(default_flags)"
seek, playback-time=0.0, start=position-0.1, repeat="($(duration) - 0.6)/0.1", flags="$(default_flags)"
play, playback-time=0.0
stop, playback-time=1.0
@@ -0,0 +1,6 @@
description, seek=true, handles-states=true, needs_preroll=true
include,location=includes/default-seek-flags.scenario
pause, playback-time=0.0
seek, playback-time=0.0, start=position+0.1, repeat="min(10, ($(duration) - 0.5) / 0.1)", flags="$(default_flags)"
play, playback-time=0.0
stop, playback-time=1.0
@@ -0,0 +1,6 @@
description, seek=true, handles-states=true, needs_preroll=true
include,location=includes/default-seek-flags.scenario
pause, playback-time=0.0
seek, playback-time=0.0, start=position+0.1, repeat="($(duration) - 0.5)/0.1", flags="$(default_flags)"
play, playback-time=0.0
stop, playback-time=1.0
@@ -0,0 +1,6 @@
description, seek=true, duration=30, need-clock-sync=true, ignore-eos=true
include,location=includes/default-seek-flags.scenario
seek, name=Backward-seek, playback-time="min(5.0, ($(duration) / 4))", rate=1.0, start=0.0, flags="$(default_flags)"
seek, name=Backward-seek, playback-time="min(10.0, 2*($(duration) / 4))", rate=1.0, start="min(5.0, $(duration) / 4)", flags="$(default_flags)"
seek, name=Backward-seek, playback-time="min(15.0, 3*($(duration) / 4))", rate=1.0, start="min(10.0, 2*($(duration) / 4))", flags="$(default_flags)"
stop, playback-time="min(15.0, 3*($(duration) / 4))"
@@ -0,0 +1,17 @@
#FIXME : Rename to seek_end_live
description, seek=true, live_content_required=true, duration=14.0
# Wait for 5s (can't use playback-time since we don't know the position(live))
wait, duration=5.0
# Seek back 1min (from the end, i.e. 'now')
seek, name=End-seek, rate=1.0, start=-60.0, start_type=end, flags=flush
wait, duration=5.0
# go back to live !
seek, name=End-seek, rate=1.0, start=0.0, start_type=end, flags=flush
wait, duration=5.0
# Now seek backwards from the end
seek, name=End-seek, rate=-1.0, start=0.0, start_type=none, stop=0.0, stop_type=end, flags=flush
wait, duration=5.0
# Try a simple seek (without setting the stop)
seek, name=End-seek, rate=1.0, start_type=end, start=0.0, flags=flush
wait, duration=5.0
stop
@@ -0,0 +1,6 @@
description, seek=true, duration=20, need-clock-sync=true, ignore-eos=true
include,location=includes/default-seek-flags.scenario
seek, name=First-forward-seek, playback-time="min(5.0, ($(duration)/8))", start="min(10, 2*($(duration)/8))", flags="$(default_flags)"
seek, name=Second-forward-seek, playback-time="min(15.0, 3*($(duration)/8))", start="min(20, 4*($(duration)/8))", flags="$(default_flags)"
seek, name=Third-forward-seek, playback-time="min(25, 5*($(duration)/8))", start="min(30.0, 6*($(duration)/8))", flags="$(default_flags)"
stop, playback-time="min($(duration) - 1, 35)", on-message=eos
@@ -0,0 +1,10 @@
description, seek=true, duration=40, min-media-duration=45.0
include,location=includes/default-seek-flags.scenario
seek, name=Forward-seek, playback-time=0.0, rate=1.0, start=5.0, flags="$(default_flags)"
seek, name=Backward-seek, playback-time=10.0, rate=1.0, start=0.0, flags="$(default_flags)"
seek, name=Backward-seek, playback-time=5.0, rate=1.0, start=25.0, stop=-1, flags="$(default_flags)"
seek, name=Backward-seek, playback-time=30.0, rate=1.0, start=0.0, flags="$(default_flags)"
seek, name=Forward-seek, playback-time=5.0, rate=1.0, start=15.0, flags="$(default_flags)"
seek, name=Forward-seek, playback-time=20.0, rate=1.0, start=35.0, flags="$(default_flags)"
seek, name=Backward-seek, playback-time=40.0, rate=1.0, start=25.0, flags="$(default_flags)"
seek, name=Last-backward-seek, playback-time=30.0, rate=1.0, start=5.0, stop=10.0, flags="$(default_flags)"
@@ -0,0 +1,3 @@
description, seek=true, duration=5.0, need_clock_sync=true, min-media-duration=2
include,location=includes/default-seek-flags.scenario
seek, playback-time=1.0, start=0.0, stop="min(5.0, duration-1.0)", flags="$(default_flags)"
@@ -0,0 +1,5 @@
description, seek=true, duration=5.0
include,location=includes/default-seek-flags.scenario
seek, playback-time=1.0, rate=1.0, start=2.0, flags="$(default_flags)"
seek, playback-time=3.0, rate=1.0, start=0.0, flags="$(default_flags)"
seek, playback-time=1.0, rate=1.0, start=2.0, stop=3.0, flags="$(default_flags)"
@@ -0,0 +1,3 @@
description, summary="Change audio track at 5 second to the second audio track", min-audio-track=2, duration=10.0, min-media-duration=5.1
switch-track, name=Next-audio-track, playback-time=5.0, type=audio, index=(string)+1
stop, playback-time=10.0
@@ -0,0 +1,11 @@
description, summary="Change audio track while pipeline is paused", min-audio-track=2, duration=6.0, need-clock-sync=true, needs_preroll=true
pause, playback-time=1.0;
# Wait so that humans can see the pipeline is paused
wait, duration=0.5
switch-track, name=Next-audio-track, type=audio, index=(string)+1
# Wait so that humans can see the pipeline is paused
wait, duration=0.5
play;
stop, playback-time=5.0
@@ -0,0 +1 @@
description, summary="Change subtitle track at 1 second while paused", duration=5.0, needs-ext-file="subtitles/%s.1.srt:subtitles/%s.1.srt"
@@ -0,0 +1,3 @@
description, summary="Change subtitle track at 1 second while playing back", min-subtitle-track=2, duration=5.0, need-clock-sync=true
switch-track, playback-time=1.0, type=text, index=(string)+1
stop, playback-time=5.0
@@ -0,0 +1,7 @@
description, summary="Change subtitle track while pipeline is PAUSED", min-subtitle-track=2, duration=5.0, handles-states=true, need-clock-sync=true, needs_preroll=true
pause;
wait, duration=0.5
switch-track, type=text, index=(string)+1
wait, duration=0.5
play;
stop, playback-time=5.0
@@ -0,0 +1,9 @@
description, duration=10.0, seek=true, need-clock-sync=true, min-media-duration=8.0, min-video-track=1
seek, name=Fast-forward-seek, playback-time="min(5.0, duration*0.0625)", rate=2.0, start=0.0, flags=flush+trickmode-key-units
seek, name=Fast-forward-seek, playback-time="min(10.0, duration*0.0625)", rate=4.0, start=0.0, flags=flush+trickmode-key-units
seek, name=Fast-forward-seek, playback-time="min(20.0, duration*0.125)", rate=8.0, start=0.0, flags=flush+trickmode-key-units
seek, name=Fast-forward-seek, playback-time="min(40.0, duration*0.25)", rate=16.0, start=0.0, flags=flush+trickmode-key-units
seek, name=Fast-forward-seek, playback-time="min(80.0, duration*0.50)", rate=32.0, start=0.0, flags=flush+trickmode-key-units
# and go back to regular playback
seek, name=regular-playback, playback-time="min(160.0, duration*0.75)", rate=1.0, start=0.0, flags=flush
stop, playback-time="min(10.0, duration*0.0625)"
@@ -0,0 +1,3 @@
description, summary="Use the set seek type to seek at 5 seconds after 2 seconds", seek=true
include,location=includes/default-seek-flags.scenario
seek, playback-time=2.0, rate=1.0, start_type=set, start=5.0, stop_type=none, stop=0.0, flags="$(default_flags)"
@@ -0,0 +1,4 @@
description, summary="Use the set seek type to seek at 0 secs stop 10secs after 5 secs", seek=true
description, duration=15.0, seek=true
include,location=includes/default-seek-flags.scenario
seek, playback-time=5.0, rate=1.0, start_type=none, start=0.0, stop_type=set, stop=10.0, flags="$(default_flags)"
@@ -0,0 +1,2 @@
core, action=set-property, target-element-klass=Filter, property-name=qos, property-value=false
core, action=set-property, target-element-klass=Sink, property-name=max-lateness, property-value=-1, optional=true
@@ -0,0 +1,2 @@
To be able to use the new-release script from www/bin/ you will need to link $GST/gst-devtools/validate to
$GST/gst-validate/ -- After doing that you can follow the standard GStreamer releasing procedure.
@@ -0,0 +1,59 @@
== Main components
Gst-validate is composed of 4 parts: the issues, the reports, the runner and
the reporters.
= Issue
Gst-Validate's main target is finding problems in GStreamer elements/pipelines.
To make it easier to track down exactly what happens, the tests run by
Gst-Validate use an extensible list of 'Issues'. Each Issue describes a
potential error situation and has an unique ID and a severity level.
The issues list can be extended by 3rd party libraries if specific needs
should be met.
= Reporters
A reporter is the object that implements the GstValidateReporter interface and
is responsible for performing tests on some target element/scenario. The
reporter is able to create 'Reports' whenever a test it executes fails.
= Reports
The GstValidateReports are created whenever a test fails, they are posted to the
stderr and also are posted to the GstValidateRunner for accumulation.
Each report contains information about the object that generated the issue,
the issue associated with the report and a specific debug message for the case,
this helps tracking down the problem and fixing it.
= Runner
The GstValidateRunner is the point of communication for the app to gst-validate
monitoring. It provides an API to gather reports and to make them accessible
to the application.
== Reporter types
= Monitors
The monitors are used to wrap around pipelines (and elements and pads) and
attach to their data flow handling functions to be able to intercept the data
and compare it with the expected behaviors. There are 3 types of monitors:
* GstValidateElementMonitor
* GstValidateBinMonitor
* GstValidatePadMonitor
All 3 inherit from the base GstValidateMonitor class. Their name suggest what
they monitor and they have a relationship to their children and parents. A bin
monitor has, possibly, child element monitors and element monitors have child
pad monitors. The monitors are responsible for listening to new children added
to their monitored object and creating monitors for them. For example, element
monitors listen to element's pad-added signal and create pad monitors whenever
a new pad is added.
Most (if not all) the checks are implemented at the GstValidatePadMonitor,
as it is where the data flow happens.
= FileChecker
The file checker is another reporter that is used to make sure a file has a
few expected properties. It inspects the file and compares the results with
expected values set by the user. Values such as file duration, file size, if
it can be played back and also if its encoding and container types.
@@ -0,0 +1,90 @@
=== The GstValidate CLI Tools
The commands here assume that you have installed gst-validate. If this
is not the case, go into the gst-validate directory and call the tools
directly with the path tools/<tool-executable>
1- gst-validate-1.0: It is the simplest tool and is used to run a gst
launch style pipeline. Monitors are added to it to identify issues in the
used elements. At the end a report will be printed, this report will
contain information about all issues that were encountered while running
gst-validate. To view issues as they are created, set the environment
variable GST_DEBUG=validate:2 and it will be printed as gstreamer
debugging. You can basically run any GstPipeline pipeline using it.
If you are not familiar with gst-launch syntax, please refer to
gst-launch's documentation.
Examples:
# Simple playback pipeline
gst-validate-1.0 playbin uri=file:///path/to/some/media/file
# Transcoding pipeline
gst-validate-1.0 filesrc location=/root/Videos/big_buck_bunny_1080p_h264.mov ! \
qtdemux name=d ! queue ! x264enc ! h264parse ! mpegtsmux name=m ! progressreport ! filesink location=/root/test.ts \
d. ! queue ! faac ! m.
You can also activate what we call "scenarios" which will execute
actions on the pipeline. Those actions can be for example, "set pipeline
to pause", "seek to N with rate=x" etc, using the following syntax:
gst-validate-1.0 playbin uri=file:///path/to/some/media/file --set-scenario=seek_forward
You can list all available scenarios using:
gst-validate-transcoding-1.0 --list-scenarios
Scenarios are simple text files describing a list of actions, you can find the
source scenario files in validate/data/
You can find more information about scenarios on the GstValidateScenario documentation: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-validate/html/GstValidateScenario.html
You can find more information about the various action types available to be executed with:
gst-validate-1.0 -t <optional-action-type>
or:
gst-validate-transcoding-1.0 -t <optional-action-type>
2- gst-validate-transcoding-1.0: Transcodes input-uri to output-uri,
using the given encoding profile. The pipeline will be monitored for
possible issues detection using the gst-validate lib, at the end of
execution, a report containing information about all found issues will
be printed.
Example:
# Will transcode file://path/to/some/media/file to H264/AAC into mp4
gst-validate-transcoding-1.0 -o 'video/quicktime,variant=iso:video/x-h264:audio/mpeg,mpegversion=4' \
file://path/to/some/media/file file:///path/to/destination_h264_aac.qt
The same scenarios can be activated on gst-validate-transcoding-1.0 as
with gst-validate-1.0
3- gst-validate-media-check-1.0: Analyzes a media file and writes the
results to stdout or a file. It can also compare the results found with
another results file for identifying regressions. The monitoring lib
from gst-validate will be enabled during the tests to identify issues
with the GStreamer elements involved with the media file's container and
codec types. It will actually do a series of checks over the media file.
Example:
# Will check various media properties from the file
gst-validate-media-check-1.0 file://path/to/some/media/file
=== LD_PRELOAD / Testing with exiting application
If you want to test an already existing application without modifying it. Just
use:
LD_PRELOAD=path/to/libgstvalidatepreload.so yourapp ...
gst-validate will try to replace GstPipeline creating functions and configure
monitors automatically for you, reports will be printed to stderr when
they are found. You can also use GST_DEBUG to view the issues that were found
NOTES: The exit code will be "18" in case a critical issue has
been seen while running any of those tools.
@@ -0,0 +1 @@
subdir('video')
@@ -0,0 +1,449 @@
/* GStreamer
*
* Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
* Copyright (C) 2015 Raspberry Pi Foundation
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <glib-object.h>
#include <glib.h>
#include <math.h>
#include <stdlib.h>
#include "gssim.h"
typedef gfloat (*SSimWeightFunc) (Gssim * self, gint y, gint x);
typedef struct _SSimWindowCache
{
gint x_window_start;
gint x_weight_start;
gint x_window_end;
gint y_window_start;
gint y_weight_start;
gint y_window_end;
gfloat element_summ;
} SSimWindowCache;
struct _GssimPrivate
{
gint width;
gint height;
gint windowsize;
gint windowtype;
SSimWindowCache *windows;
gfloat *weights;
gfloat const1;
gfloat const2;
gfloat sigma;
gfloat *orgmu;
GstVideoConverter *converter;
GstVideoInfo in_info, out_info;
};
/* *INDENT-OFF* */
G_DEFINE_TYPE_WITH_PRIVATE (Gssim, gssim, GST_TYPE_OBJECT)
/* *INDENT-ON* */
enum
{
PROP_FIRST_PROP = 1,
N_PROPS
};
static void
gssim_calculate_mu (Gssim * self, guint8 * buf)
{
gint oy, ox, iy, ix;
for (oy = 0; oy < self->priv->height; oy++) {
for (ox = 0; ox < self->priv->width; ox++) {
gfloat mu = 0;
gfloat elsumm;
gint weight_y_base, weight_x_base;
gint weight_offset;
gint pixel_offset;
gint winstart_y;
gint wghstart_y;
gint winend_y;
gint winstart_x;
gint wghstart_x;
gint winend_x;
gfloat weight;
gint source_offset;
source_offset = oy * self->priv->width + ox;
winstart_x = self->priv->windows[source_offset].x_window_start;
wghstart_x = self->priv->windows[source_offset].x_weight_start;
winend_x = self->priv->windows[source_offset].x_window_end;
winstart_y = self->priv->windows[source_offset].y_window_start;
wghstart_y = self->priv->windows[source_offset].y_weight_start;
winend_y = self->priv->windows[source_offset].y_window_end;
elsumm = self->priv->windows[source_offset].element_summ;
switch (self->priv->windowtype) {
case 0:
for (iy = winstart_y; iy <= winend_y; iy++) {
pixel_offset = iy * self->priv->width;
for (ix = winstart_x; ix <= winend_x; ix++)
mu += buf[pixel_offset + ix];
}
mu = mu / elsumm;
break;
case 1:
weight_y_base = wghstart_y - winstart_y;
weight_x_base = wghstart_x - winstart_x;
for (iy = winstart_y; iy <= winend_y; iy++) {
pixel_offset = iy * self->priv->width;
weight_offset = (weight_y_base + iy) * self->priv->windowsize +
weight_x_base;
for (ix = winstart_x; ix <= winend_x; ix++) {
weight = self->priv->weights[weight_offset + ix];
mu += weight * buf[pixel_offset + ix];
}
}
mu = mu / elsumm;
break;
}
self->priv->orgmu[oy * self->priv->width + ox] = mu;
}
}
}
static gfloat
ssim_weight_func_none (Gssim * self, gint y, gint x)
{
return 1;
}
static gfloat
ssim_weight_func_gauss (Gssim * self, gint y, gint x)
{
gfloat coord = sqrt (x * x + y * y);
return exp (-1 * (coord * coord) / (2 * self->priv->sigma *
self->priv->sigma)) / (self->priv->sigma * sqrt (2 * G_PI));
}
static gboolean
gssim_regenerate_windows (Gssim * self)
{
gint windowiseven;
gint y, x, y2, x2;
SSimWeightFunc func;
gfloat normal_summ = 0;
gint normal_count = 0;
g_free (self->priv->weights);
self->priv->weights =
g_new (gfloat, self->priv->windowsize * self->priv->windowsize);
windowiseven =
((gint) self->priv->windowsize / 2) * 2 == self->priv->windowsize ? 1 : 0;
g_free (self->priv->windows);
self->priv->windows =
g_new (SSimWindowCache, self->priv->height * self->priv->width);
switch (self->priv->windowtype) {
case 0:
func = ssim_weight_func_none;
break;
case 1:
func = ssim_weight_func_gauss;
break;
default:
self->priv->windowtype = 1;
func = ssim_weight_func_gauss;
}
for (y = 0; y < self->priv->windowsize; y++) {
gint yoffset = y * self->priv->windowsize;
for (x = 0; x < self->priv->windowsize; x++) {
self->priv->weights[yoffset + x] =
func (self, x - self->priv->windowsize / 2 + windowiseven,
y - self->priv->windowsize / 2 + windowiseven);
normal_summ += self->priv->weights[yoffset + x];
normal_count++;
}
}
for (y = 0; y < self->priv->height; y++) {
for (x = 0; x < self->priv->width; x++) {
SSimWindowCache win;
gint element_count = 0;
win.x_window_start = x - self->priv->windowsize / 2 + windowiseven;
win.x_weight_start = 0;
if (win.x_window_start < 0) {
win.x_weight_start = -win.x_window_start;
win.x_window_start = 0;
}
win.x_window_end = x + self->priv->windowsize / 2;
if (win.x_window_end >= self->priv->width)
win.x_window_end = self->priv->width - 1;
win.y_window_start = y - self->priv->windowsize / 2 + windowiseven;
win.y_weight_start = 0;
if (win.y_window_start < 0) {
win.y_weight_start = -win.y_window_start;
win.y_window_start = 0;
}
win.y_window_end = y + self->priv->windowsize / 2;
if (win.y_window_end >= self->priv->height)
win.y_window_end = self->priv->height - 1;
win.element_summ = 0;
element_count = (win.y_window_end - win.y_window_start + 1) *
(win.x_window_end - win.x_window_start + 1);
if (element_count == normal_count)
win.element_summ = normal_summ;
else {
for (y2 = win.y_weight_start; y2 < self->priv->windowsize; y2++) {
for (x2 = win.x_weight_start; x2 < self->priv->windowsize; x2++) {
win.element_summ +=
self->priv->weights[y2 * self->priv->windowsize + x2];
}
}
}
self->priv->windows[(y * self->priv->width + x)] = win;
}
}
/* FIXME: while 0.01 and 0.03 are pretty much static, the 255 implies that
* we're working with 8-bit-per-color-component format, which may not be true
*/
self->priv->const1 = 0.01 * 255 * 0.01 * 255;
self->priv->const2 = 0.03 * 255 * 0.03 * 255;
return TRUE;
}
void
gssim_compare (Gssim * self, guint8 * org, guint8 * mod,
guint8 * out, gfloat * mean, gfloat * lowest, gfloat * highest)
{
gint oy, ox, iy, ix;
gfloat cumulative_ssim = 0;
*lowest = G_MAXFLOAT;
*highest = -G_MAXFLOAT;
if (self->priv->windows == NULL)
gssim_regenerate_windows (self);
gssim_calculate_mu (self, org);
for (oy = 0; oy < self->priv->height; oy++) {
for (ox = 0; ox < self->priv->width; ox++) {
gfloat mu_o = 0, mu_m = 0;
gdouble sigma_o = 0, sigma_m = 0, sigma_om = 0;
gfloat tmp1, tmp2;
gfloat elsumm = 0;
gint weight_y_base, weight_x_base;
gint weight_offset;
gint pixel_offset;
gint winstart_y;
gint wghstart_y;
gint winend_y;
gint winstart_x;
gint wghstart_x;
gint winend_x;
gfloat weight;
gint source_offset;
source_offset = oy * self->priv->width + ox;
winstart_x = self->priv->windows[source_offset].x_window_start;
wghstart_x = self->priv->windows[source_offset].x_weight_start;
winend_x = self->priv->windows[source_offset].x_window_end;
winstart_y = self->priv->windows[source_offset].y_window_start;
wghstart_y = self->priv->windows[source_offset].y_weight_start;
winend_y = self->priv->windows[source_offset].y_window_end;
elsumm = self->priv->windows[source_offset].element_summ;
switch (self->priv->windowtype) {
case 0:
for (iy = winstart_y; iy <= winend_y; iy++) {
pixel_offset = iy * self->priv->width;
for (ix = winstart_x; ix <= winend_x; ix++) {
mu_m += mod[pixel_offset + ix];
}
}
mu_m = mu_m / elsumm;
mu_o = self->priv->orgmu[oy * self->priv->width + ox];
for (iy = winstart_y; iy <= winend_y; iy++) {
pixel_offset = iy * self->priv->width;
for (ix = winstart_x; ix <= winend_x; ix++) {
tmp1 = org[pixel_offset + ix] - mu_o;
tmp2 = mod[pixel_offset + ix] - mu_m;
sigma_o += tmp1 * tmp1;
sigma_m += tmp2 * tmp2;
sigma_om += tmp1 * tmp2;
}
}
break;
case 1:
weight_y_base = wghstart_y - winstart_y;
weight_x_base = wghstart_x - winstart_x;
for (iy = winstart_y; iy <= winend_y; iy++) {
pixel_offset = iy * self->priv->width;
weight_offset = (weight_y_base + iy) * self->priv->windowsize +
weight_x_base;
for (ix = winstart_x; ix <= winend_x; ix++) {
weight = self->priv->weights[weight_offset + ix];
mu_o += weight * org[pixel_offset + ix];
mu_m += weight * mod[pixel_offset + ix];
}
}
mu_m = mu_m / elsumm;
mu_o = self->priv->orgmu[oy * self->priv->width + ox];
for (iy = winstart_y; iy <= winend_y; iy++) {
gfloat *weights_with_offset;
guint8 *org_with_offset, *mod_with_offset;
gfloat wt1, wt2;
pixel_offset = iy * self->priv->width;
weight_offset = (weight_y_base + iy) * self->priv->windowsize +
weight_x_base;
weights_with_offset = &self->priv->weights[weight_offset];
org_with_offset = &org[pixel_offset];
mod_with_offset = &mod[pixel_offset];
for (ix = winstart_x; ix <= winend_x; ix++) {
weight = weights_with_offset[ix];
tmp1 = org_with_offset[ix] - mu_o;
tmp2 = mod_with_offset[ix] - mu_m;
wt1 = weight * tmp1;
wt2 = weight * tmp2;
sigma_o += wt1 * tmp1;
sigma_m += wt2 * tmp2;
sigma_om += wt1 * tmp2;
}
}
break;
}
sigma_o = sqrt (sigma_o / elsumm);
sigma_m = sqrt (sigma_m / elsumm);
sigma_om = sigma_om / elsumm;
tmp1 =
(2 * mu_o * mu_m + self->priv->const1) * (2 * sigma_om +
self->priv->const2) / ((mu_o * mu_o + mu_m * mu_m +
self->priv->const1) * (sigma_o * sigma_o + sigma_m * sigma_m +
self->priv->const2));
/* SSIM can go negative, that's why it is
127 + index * 128 instead of index * 255 */
if (out)
out[oy * self->priv->width + ox] = 127 + tmp1 * 128;
*lowest = MIN (*lowest, tmp1);
*highest = MAX (*highest, tmp1);
cumulative_ssim += tmp1;
}
}
*mean = cumulative_ssim / (self->priv->width * self->priv->height);
}
gboolean
gssim_configure (Gssim * self, gint width, gint height)
{
if (width == self->priv->width && height == self->priv->height)
return FALSE;
self->priv->width = width;
self->priv->height = height;
g_free (self->priv->windows);
self->priv->windows = NULL;
g_free (self->priv->orgmu);
self->priv->orgmu = g_new (gfloat, width * height);
return TRUE;
}
static void
gssim_get_property (GObject * object,
guint property_id, GValue * value, GParamSpec * pspec)
{
//Gssim *self = GSSIM (object);
switch (property_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gssim_set_property (GObject * object,
guint property_id, const GValue * value, GParamSpec * pspec)
{
//Gssim *self = GSSIM (object);
switch (property_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gssim_finalize (GObject * object)
{
Gssim *self = GSSIM (object);
void (*chain_up) (GObject *) =
((GObjectClass *) gssim_parent_class)->finalize;
g_free (self->priv->orgmu);
g_free (self->priv->windows);
chain_up (object);
}
static void
gssim_class_init (GssimClass * klass)
{
GObjectClass *oclass = G_OBJECT_CLASS (klass);
oclass->get_property = gssim_get_property;
oclass->set_property = gssim_set_property;
oclass->finalize = gssim_finalize;
}
static void
gssim_init (Gssim * self)
{
self->priv = gssim_get_instance_private (self);
self->priv->windowsize = 11;
self->priv->windowtype = 1;
self->priv->windows = NULL;
self->priv->sigma = 1.5;
}
Gssim *
gssim_new (void)
{
return g_object_new (GSSIM_TYPE, NULL);
}
@@ -0,0 +1,62 @@
/* GStreamer
*
* Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
* Copyright (C) 2015 Raspberry Pi Foundation
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _GSSIM_H
#define _GSSIM_H
#include <glib.h>
#include <gst/gst.h>
#include <glib-object.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
typedef struct _GssimPrivate GssimPrivate;
typedef struct {
GstObject parent;
GssimPrivate *priv;
} Gssim;
typedef struct {
GstObjectClass parent;
} GssimClass;
#define GSSIM_TYPE (gssim_get_type ())
#define GSSIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSSIM_TYPE, Gssim))
#define GSSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSSIM_TYPE, GssimClass))
#define IS_GSSIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSSIM_TYPE))
#define IS_GSSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSSIM_TYPE))
#define GSSIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSSIM_TYPE, GssimClass))
GType gssim_get_type (void);
Gssim * gssim_new (void);
void gssim_compare (Gssim * self, guint8 * org, guint8 * mod,
guint8 * out, gfloat * mean, gfloat * lowest,
gfloat * highest);
gboolean gssim_configure (Gssim * self, gint width, gint height);
G_END_DECLS
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,77 @@
/* GStreamer
*
* Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
* Copyright (C) 2015 Raspberry Pi Foundation
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _GST_VALIDATE_SSIM_H
#define _GST_VALIDATE_SSIM_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glib.h>
#include <gst/gst.h>
#include <glib-object.h>
#include <gst/video/video.h>
#include <gst/validate/validate.h>
G_BEGIN_DECLS
typedef struct _GstValidateSsimPrivate GstValidateSsimPrivate;
typedef struct {
GstObject parent;
GstValidateSsimPrivate *priv;
} GstValidateSsim;
typedef struct {
GstObjectClass parent;
} GstValidateSsimClass;
#define GST_VALIDATE_SSIM_TIME_FORMAT "u-%02u-%02u.%09u"
#define GST_VALIDATE_SSIM_TYPE (gst_validate_ssim_get_type ())
#define GST_VALIDATE_SSIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_VALIDATE_SSIM_TYPE, GstValidateSsim))
#define GST_VALIDATE_SSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_VALIDATE_SSIM_TYPE, GstValidateSsimClass))
#define IS_GST_VALIDATE_SSIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_VALIDATE_SSIM_TYPE))
#define IS_GST_VALIDATE_SSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_VALIDATE_SSIM_TYPE))
#define GST_VALIDATE_SSIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_VALIDATE_SSIM_TYPE, GstValidateSsimClass))
GType gst_validate_ssim_get_type (void);
GstValidateSsim * gst_validate_ssim_new (GstValidateRunner *runner,
gfloat min_avg_similarity,
gfloat min_lowest_similarity,
gint fps_n,
gint fps_d);
gboolean gst_validate_ssim_compare_image_files (GstValidateSsim *self, const gchar *ref_file,
const gchar * file, gfloat * mean, gfloat * lowest,
gfloat * highest, const gchar *outfolder);
void gst_validate_ssim_compare_frames (GstValidateSsim * self, GstVideoFrame *ref_frame,
GstVideoFrame *frame, GstBuffer **outbuf,
gfloat * mean, gfloat * lowest, gfloat * highest);
G_END_DECLS
#endif
@@ -0,0 +1,18 @@
validate_video_dep = dependency('', required: false)
cairo_dep = dependency('cairo-png', required: get_option('cairo'), fallback: 'cairo')
if cairo_dep.found()
video = static_library(
'gstvalidatevideo',
'gstvalidatessim.c', 'gssim.c',
include_directories : inc_dirs,
dependencies : [gst_dep, gst_video_dep, gst_pbutils_dep, glib_dep, cairo_dep, gio_dep,
mathlib],
)
validate_video_dep = declare_dependency(
link_with : video,
include_directories : inc_dirs,
dependencies : [gst_dep, gst_video_dep, gst_pbutils_dep, glib_dep, cairo_dep, gio_dep,
mathlib],
)
endif
@@ -0,0 +1,2 @@
subdir('gst')
@@ -0,0 +1,2 @@
subdir('validate')
subdir('overrides')
@@ -0,0 +1,48 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>
*
* gst-validate-default-overrides.c - Test overrides
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/validate/gst-validate-override.h>
#include <gst/validate/gst-validate-override-registry.h>
#include <gst/validate/gst-validate-report.h>
/* public symbol */
GST_PLUGIN_EXPORT int gst_validate_create_overrides (void);
int
gst_validate_create_overrides (void)
{
GstValidateOverride *o;
/* Some random test override. Will moan on:
gst-launch videotestsrc num-buffers=10 ! video/x-raw-yuv ! fakesink */
o = gst_validate_override_new ();
gst_validate_override_change_severity (o,
g_quark_from_string ("caps::is-missing-field"),
GST_VALIDATE_REPORT_LEVEL_CRITICAL);
gst_validate_override_register_by_name ("capsfilter0", o);
g_object_unref (o);
return 1;
}
@@ -0,0 +1,9 @@
library('gstvalidate-default-overrides-1.0',
sources: 'gst-validate-default-overrides.c',
version : libversion,
soversion : soversion,
darwin_versions : osxversion,
include_directories : [inc_dirs],
install: true,
c_args : [gst_c_args] + ['-D_GNU_SOURCE'],
dependencies : validate_dep)
@@ -0,0 +1,436 @@
/* GStreamer
*
* Copyright (C) 2018-2019 Igalia S.L.
* Copyright (C) 2018 Metrological Group B.V.
* Author: Alicia Boya García <aboya@igalia.com>
*
* formatting.c: Functions used by validateflow to get string
* representations of buffers.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "formatting.h"
#include <gst/gst.h>
#include <gst/video/video.h>
#include <string.h>
#include <stdio.h>
#include <glib/gprintf.h>
#include "../../gst/validate/gst-validate-utils.h"
typedef void (*Uint64Formatter) (gchar * dest, guint64 time);
G_LOCK_DEFINE (checksums_as_id_lock);
static GstStructure *checksums_as_id = NULL;
#define CONSTIFY(strv) ((const gchar * const *) strv)
static gboolean
use_field (const gchar * field, gchar ** logged, gchar ** ignored)
{
if (logged)
return g_strv_contains (CONSTIFY (logged), field);
if (ignored)
return !g_strv_contains (CONSTIFY (ignored), field);
return TRUE;
}
void
format_time (gchar * dest_str, guint64 time)
{
if (GST_CLOCK_TIME_IS_VALID (time)) {
g_sprintf (dest_str, "%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
} else {
strcpy (dest_str, "none");
}
}
static void
format_number (gchar * dest_str, guint64 number)
{
g_sprintf (dest_str, "%" G_GUINT64_FORMAT, number);
}
gchar *
validate_flow_format_segment (const GstSegment * segment,
gchar ** logged_fields, gchar ** ignored_fields)
{
Uint64Formatter uint64_format;
gchar *segment_str;
gchar *parts[12];
GString *format;
gchar start_str[32], offset_str[32], stop_str[32], time_str[32], base_str[32],
position_str[32], duration_str[32];
int parts_index = 0;
uint64_format =
segment->format == GST_FORMAT_TIME ? format_time : format_number;
uint64_format (start_str, segment->start);
uint64_format (offset_str, segment->offset);
uint64_format (stop_str, segment->stop);
uint64_format (time_str, segment->time);
uint64_format (base_str, segment->base);
uint64_format (position_str, segment->position);
uint64_format (duration_str, segment->duration);
format = g_string_new (gst_format_get_name (segment->format));
format = g_string_ascii_up (format);
if (use_field ("format", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("format=%s", format->str);
if (use_field ("start", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("start=%s", start_str);
if (use_field ("offset", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("offset=%s", offset_str);
if (use_field ("stop", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("stop=%s", stop_str);
if (segment->rate != 1.0)
parts[parts_index++] = g_strdup_printf ("rate=%f", segment->rate);
if (segment->applied_rate != 1.0)
parts[parts_index++] =
g_strdup_printf ("applied_rate=%f", segment->applied_rate);
if (segment->flags && use_field ("flags", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("flags=0x%02x", segment->flags);
if (use_field ("time", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("time=%s", time_str);
if (use_field ("base", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("base=%s", base_str);
if (use_field ("position", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("position=%s", position_str);
if (GST_CLOCK_TIME_IS_VALID (segment->duration)
&& use_field ("duration", logged_fields, ignored_fields))
parts[parts_index++] = g_strdup_printf ("duration=%s", duration_str);
parts[parts_index] = NULL;
segment_str = g_strjoinv (", ", parts);
while (parts_index > 0)
g_free (parts[--parts_index]);
g_string_free (format, TRUE);
return segment_str;
}
typedef struct
{
GList *fields;
gchar **wanted_fields;
gchar **ignored_fields;
} StructureValues;
static gboolean
structure_set_fields (GQuark field_id, GValue * value, StructureValues * data)
{
const gchar *field = g_quark_to_string (field_id);
if (data->ignored_fields
&& g_strv_contains ((const gchar **) data->ignored_fields, field))
return TRUE;
if (data->wanted_fields
&& !g_strv_contains ((const gchar **) data->wanted_fields, field))
return TRUE;
data->fields = g_list_prepend (data->fields, (gchar *) field);
return TRUE;
}
static GstStructure *
validate_flow_structure_cleanup (const GstStructure * structure,
gchar ** wanted_fields, gchar ** ignored_fields)
{
GstStructure *nstructure;
StructureValues d = {
.fields = NULL,
.wanted_fields = wanted_fields,
.ignored_fields = ignored_fields,
};
gst_structure_foreach (structure,
(GstStructureForeachFunc) structure_set_fields, &d);
d.fields = g_list_sort (d.fields, (GCompareFunc) g_ascii_strcasecmp);
nstructure = gst_structure_new_empty (gst_structure_get_name (structure));
for (GList * tmp = d.fields; tmp; tmp = tmp->next) {
gchar *field = tmp->data;
gst_structure_set_value (nstructure, field,
gst_structure_get_value (structure, field));
}
g_list_free (d.fields);
return nstructure;
}
gchar *
validate_flow_format_caps (const GstCaps * caps, gchar ** wanted_fields)
{
guint i;
GstCaps *new_caps = gst_caps_new_empty ();
gchar *caps_str;
/* A single GstCaps can contain several caps structures (although only one is
* used in most cases). We will print them separated with spaces. */
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstStructure *structure =
validate_flow_structure_cleanup (gst_caps_get_structure (caps, i),
wanted_fields, NULL);
gst_caps_append_structure_full (new_caps, structure,
gst_caps_features_copy (gst_caps_get_features (caps, i)));
}
caps_str = gst_caps_to_string (new_caps);
gst_caps_unref (new_caps);
return caps_str;
}
static gchar *
buffer_get_flags_string (GstBuffer * buffer)
{
GFlagsClass *flags_class =
G_FLAGS_CLASS (g_type_class_ref (gst_buffer_flags_get_type ()));
GstBufferFlags flags = GST_BUFFER_FLAGS (buffer);
GString *string = NULL;
while (1) {
GFlagsValue *value = g_flags_get_first_value (flags_class, flags);
if (!value)
break;
if (string == NULL)
string = g_string_new (NULL);
else
g_string_append (string, " ");
g_string_append (string, value->value_nick);
flags &= ~value->value;
}
return (string != NULL) ? g_string_free (string, FALSE) : NULL;
}
/* Returns a newly-allocated string describing the metas on this buffer, or NULL */
static gchar *
buffer_get_meta_string (GstBuffer * buffer)
{
gpointer state = NULL;
GstMeta *meta;
GString *s = NULL;
while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
const gchar *desc = g_type_name (meta->info->type);
if (s == NULL)
s = g_string_new (NULL);
else
g_string_append (s, ", ");
if (meta->info->api == GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE) {
GstVideoRegionOfInterestMeta *roi = (GstVideoRegionOfInterestMeta *) meta;
g_string_append_printf (s,
"GstVideoRegionOfInterestMeta[x=%" G_GUINT32_FORMAT ", y=%"
G_GUINT32_FORMAT ", width=%" G_GUINT32_FORMAT ", height=%"
G_GUINT32_FORMAT "]", roi->x, roi->y, roi->w, roi->h);
} else {
g_string_append (s, desc);
}
}
return (s != NULL) ? g_string_free (s, FALSE) : NULL;
}
gchar *
validate_flow_format_buffer (GstBuffer * buffer, gint checksum_type,
GstStructure * logged_fields_struct, GstStructure * ignored_fields_struct)
{
gchar *flags_str, *meta_str, *buffer_str;
gchar *buffer_parts[7];
int buffer_parts_index = 0;
GstMapInfo map;
gchar **logged_fields =
logged_fields_struct ? gst_validate_utils_get_strv (logged_fields_struct,
"buffer") : NULL;
gchar **ignored_fields =
ignored_fields_struct ?
gst_validate_utils_get_strv (ignored_fields_struct, "buffer") : NULL;
if (checksum_type != CHECKSUM_TYPE_NONE || (logged_fields
&& g_strv_contains (CONSTIFY (logged_fields), "checksum"))) {
if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
GST_ERROR ("Buffer could not be mapped.");
} else if (checksum_type == CHECKSUM_TYPE_CONTENT_HEX) {
gint i;
GString *content = g_string_new ("content=");
for (i = 0; i < map.size; i++) {
if (i)
g_string_append_c (content, ' ');
g_string_append_printf (content, "0x%02x", map.data[i]);
}
buffer_parts[buffer_parts_index++] = g_string_free (content, FALSE);
} else {
gchar *sum =
g_compute_checksum_for_data (checksum_type ==
CHECKSUM_TYPE_AS_ID ? G_CHECKSUM_SHA1 : checksum_type, map.data,
map.size);
gst_buffer_unmap (buffer, &map);
if (checksum_type == CHECKSUM_TYPE_AS_ID) {
gint id;
G_LOCK (checksums_as_id_lock);
if (!checksums_as_id)
checksums_as_id = gst_structure_new_empty ("checksums-id");
if (!gst_structure_get_int (checksums_as_id, sum, &id)) {
id = gst_structure_n_fields (checksums_as_id);
gst_structure_set (checksums_as_id, sum, G_TYPE_INT, id, NULL);
}
G_UNLOCK (checksums_as_id_lock);
buffer_parts[buffer_parts_index++] =
g_strdup_printf ("content-id=%d", id);
} else {
buffer_parts[buffer_parts_index++] =
g_strdup_printf ("checksum=%s", sum);
}
g_free (sum);
}
}
if (GST_CLOCK_TIME_IS_VALID (buffer->dts)
&& use_field ("dts", logged_fields, ignored_fields)) {
gchar time_str[32];
format_time (time_str, buffer->dts);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("dts=%s", time_str);
}
if (GST_CLOCK_TIME_IS_VALID (buffer->pts)
&& use_field ("pts", logged_fields, ignored_fields)) {
gchar time_str[32];
format_time (time_str, buffer->pts);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("pts=%s", time_str);
}
if (GST_CLOCK_TIME_IS_VALID (buffer->duration)
&& use_field ("dur", logged_fields, ignored_fields)) {
gchar time_str[32];
format_time (time_str, buffer->duration);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("dur=%s", time_str);
}
flags_str = buffer_get_flags_string (buffer);
if (flags_str && use_field ("flags", logged_fields, ignored_fields)) {
buffer_parts[buffer_parts_index++] =
g_strdup_printf ("flags=%s", flags_str);
}
meta_str = buffer_get_meta_string (buffer);
if (meta_str && use_field ("meta", logged_fields, ignored_fields))
buffer_parts[buffer_parts_index++] = g_strdup_printf ("meta=%s", meta_str);
buffer_parts[buffer_parts_index] = NULL;
buffer_str =
buffer_parts_index > 0 ? g_strjoinv (", ",
buffer_parts) : g_strdup ("(empty)");
g_free (meta_str);
g_free (flags_str);
while (buffer_parts_index > 0)
g_free (buffer_parts[--buffer_parts_index]);
return buffer_str;
}
gchar *
validate_flow_format_event (GstEvent * event,
const gchar * const *caps_properties,
GstStructure * logged_fields_struct,
GstStructure * ignored_fields_struct,
const gchar * const *ignored_event_types,
const gchar * const *logged_event_types)
{
const gchar *event_type;
gchar *structure_string;
gchar *event_string;
gchar **ignored_fields;
gchar **logged_fields;
event_type = gst_event_type_get_name (GST_EVENT_TYPE (event));
if (logged_event_types && !g_strv_contains (logged_event_types, event_type))
return NULL;
if (ignored_event_types && g_strv_contains (ignored_event_types, event_type))
return NULL;
logged_fields =
logged_fields_struct ? gst_validate_utils_get_strv (logged_fields_struct,
event_type) : NULL;
ignored_fields =
ignored_fields_struct ?
gst_validate_utils_get_strv (ignored_fields_struct, event_type) : NULL;
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
const GstSegment *segment;
gst_event_parse_segment (event, &segment);
structure_string =
validate_flow_format_segment (segment, logged_fields, ignored_fields);
} else if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
GstCaps *caps;
gst_event_parse_caps (event, &caps);
structure_string =
validate_flow_format_caps (caps,
logged_fields ? logged_fields : (gchar **) caps_properties);
/* FIXME: Remove spurious `;` and regenerate all the expectation files */
event_string = g_strdup_printf ("%s: %s;", event_type, structure_string);
goto done;
} else if (!gst_event_get_structure (event)) {
structure_string = g_strdup ("(no structure)");
} else {
GstStructure *structure =
validate_flow_structure_cleanup (gst_event_get_structure (event),
logged_fields, ignored_fields);
structure_string = gst_structure_to_string (structure);
gst_structure_free (structure);
}
event_string = g_strdup_printf ("%s: %s", event_type, structure_string);
done:
g_strfreev (logged_fields);
g_strfreev (ignored_fields);
g_free (structure_string);
return event_string;
}
@@ -0,0 +1,42 @@
/* GStreamer
*
* Copyright (C) 2018-2019 Igalia S.L.
* Copyright (C) 2018 Metrological Group B.V.
* Author: Alicia Boya García <aboya@igalia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_FLOW_FORMATTING_H__
#define __GST_VALIDATE_FLOW_FORMATTING_H__
#include <gst/gst.h>
#define CHECKSUM_TYPE_AS_ID -1
#define CHECKSUM_TYPE_NONE -2
#define CHECKSUM_TYPE_CONTENT_HEX -3
void format_time(gchar* dest_str, guint64 time);
gchar* validate_flow_format_segment(const GstSegment* segment, gchar** logged_fields, gchar** ignored_fields);
gchar* validate_flow_format_caps (const GstCaps* caps, gchar **wanted_fields);
gchar* validate_flow_format_buffer(GstBuffer* buffer, gboolean add_checksum, GstStructure* logged_fields_struct, GstStructure* ignored_fields_struct);
gchar* validate_flow_format_event(GstEvent* event, const gchar* const* caps_properties, GstStructure* logged_event_fields, GstStructure* ignored_event_fields, const gchar* const* ignored_event_types, const gchar* const* logged_event_types);
#endif // __GST_VALIDATE_FLOW_FORMATTING_H__
@@ -0,0 +1,700 @@
/* GStreamer
*
* Copyright (C) 2018-2019 Igalia S.L.
* Copyright (C) 2018 Metrological Group B.V.
* Author: Alicia Boya García <aboya@igalia.com>
*
* gstvalidateflow.c: A plugin to record streams and match them to
* expectation files.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include "../validate.h"
#include "../gst-validate-utils.h"
#include "../gst-validate-report.h"
#include "../gst-validate-internal.h"
#include "formatting.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include "gstvalidateflow.h"
#define VALIDATE_FLOW_MISMATCH g_quark_from_static_string ("validateflow::mismatch")
#define VALIDATE_FLOW_NOT_ATTACHED g_quark_from_static_string ("validateflow::not-attached")
typedef enum _ValidateFlowMode
{
VALIDATE_FLOW_MODE_WRITING_EXPECTATIONS,
VALIDATE_FLOW_MODE_WRITING_ACTUAL_RESULTS
} ValidateFlowMode;
#define GST_TYPE_VALIDATE_FLOW_CHECKSUM_TYPE (validate_flow_checksum_type_get_type ())
static GType
validate_flow_checksum_type_get_type (void)
{
static GType gtype = 0;
if (gtype == 0) {
static const GEnumValue values[] = {
{CHECKSUM_TYPE_NONE, "NONE", "none"},
{CHECKSUM_TYPE_AS_ID, "AS-ID", "as-id"},
{CHECKSUM_TYPE_CONTENT_HEX, "raw-hex", "raw-hex"},
{G_CHECKSUM_MD5, "MD5", "md5"},
{G_CHECKSUM_SHA1, "SHA-1", "sha1"},
{G_CHECKSUM_SHA256, "SHA-256", "sha256"},
{G_CHECKSUM_SHA512, "SHA-512", "sha512"},
{0, NULL, NULL},
};
gtype = g_enum_register_static ("ValidateFlowChecksumType", values);
}
return gtype;
}
struct _ValidateFlowOverride
{
GstValidateOverride parent;
const gchar *pad_name;
gboolean record_buffers;
gint checksum_type;
gchar *expectations_dir;
gchar *actual_results_dir;
gboolean error_writing_file;
gchar **caps_properties;
GstStructure *ignored_fields;
GstStructure *logged_fields;
gchar **logged_event_types;
gchar **ignored_event_types;
gchar *expectations_file_path;
gchar *actual_results_file_path;
ValidateFlowMode mode;
gboolean was_attached;
GstStructure *config;
/* output_file will refer to the expectations file if it did not exist,
* or to the actual results file otherwise. */
gchar *output_file_path;
FILE *output_file;
GMutex output_file_mutex;
};
GList *all_overrides = NULL;
static void validate_flow_override_finalize (GObject * object);
static void validate_flow_override_attached (GstValidateOverride * override);
static void _runner_set (GObject * object, GParamSpec * pspec,
gpointer user_data);
static void runner_stopping (GstValidateRunner * runner,
ValidateFlowOverride * flow);
#define VALIDATE_TYPE_FLOW_OVERRIDE validate_flow_override_get_type ()
G_DEFINE_TYPE (ValidateFlowOverride, validate_flow_override,
GST_TYPE_VALIDATE_OVERRIDE);
void
validate_flow_override_init (ValidateFlowOverride * self)
{
}
void
validate_flow_override_class_init (ValidateFlowOverrideClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstValidateOverrideClass *override_class =
GST_VALIDATE_OVERRIDE_CLASS (klass);
object_class->finalize = validate_flow_override_finalize;
override_class->attached = validate_flow_override_attached;
g_assert (gst_validate_is_initialized ());
gst_validate_issue_register (gst_validate_issue_new
(VALIDATE_FLOW_MISMATCH,
"The recorded log does not match the expectation file.",
"The recorded log does not match the expectation file.",
GST_VALIDATE_REPORT_LEVEL_CRITICAL));
gst_validate_issue_register (gst_validate_issue_new
(VALIDATE_FLOW_NOT_ATTACHED,
"The pad to monitor was never attached.",
"The pad to monitor was never attached.",
GST_VALIDATE_REPORT_LEVEL_CRITICAL));
}
/* *INDENT-OFF* */
G_GNUC_PRINTF (2, 0)
/* *INDENT-ON* */
static void
validate_flow_override_vprintf (ValidateFlowOverride * flow, const char *format,
va_list ap)
{
g_mutex_lock (&flow->output_file_mutex);
if (!flow->error_writing_file && vfprintf (flow->output_file, format, ap) < 0) {
GST_ERROR_OBJECT (flow, "Writing to file %s failed",
flow->output_file_path);
flow->error_writing_file = TRUE;
}
g_mutex_unlock (&flow->output_file_mutex);
}
/* *INDENT-OFF* */
G_GNUC_PRINTF (2, 3)
/* *INDENT-ON* */
static void
validate_flow_override_printf (ValidateFlowOverride * flow, const char *format,
...)
{
va_list ap;
va_start (ap, format);
validate_flow_override_vprintf (flow, format, ap);
va_end (ap);
}
static void
validate_flow_override_event_handler (GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstEvent * event)
{
ValidateFlowOverride *flow = VALIDATE_FLOW_OVERRIDE (override);
gchar *event_string;
if (flow->error_writing_file)
return;
event_string = validate_flow_format_event (event,
(const gchar * const *) flow->caps_properties,
flow->logged_fields,
flow->ignored_fields,
(const gchar * const *) flow->ignored_event_types,
(const gchar * const *) flow->logged_event_types);
if (event_string) {
validate_flow_override_printf (flow, "event %s\n", event_string);
g_free (event_string);
}
}
static void
validate_flow_override_buffer_handler (GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstBuffer * buffer)
{
ValidateFlowOverride *flow = VALIDATE_FLOW_OVERRIDE (override);
gchar *buffer_str;
if (flow->error_writing_file || !flow->record_buffers)
return;
buffer_str = validate_flow_format_buffer (buffer, flow->checksum_type,
flow->logged_fields, flow->ignored_fields);
validate_flow_override_printf (flow, "buffer: %s\n", buffer_str);
g_free (buffer_str);
}
static gchar *
make_safe_file_name (const gchar * name)
{
gchar *ret = g_strdup (name);
gchar *c;
for (c = ret; *c; c++) {
switch (*c) {
case '<':
case '>':
case ':':
case '"':
case '/':
case '\\':
case '|':
case '?':
case '*':
*c = '-';
break;
}
}
return ret;
}
static ValidateFlowOverride *
validate_flow_override_new (GstStructure * config)
{
ValidateFlowOverride *flow;
GstValidateOverride *override;
gboolean use_checksum = FALSE;
gchar *ignored_fields = NULL, *logged_fields;
const GValue *tmpval;
flow = g_object_new (VALIDATE_TYPE_FLOW_OVERRIDE, NULL);
flow->config = config;
GST_OBJECT_FLAG_SET (flow, GST_OBJECT_FLAG_MAY_BE_LEAKED);
override = GST_VALIDATE_OVERRIDE (flow);
/* pad: Name of the pad where flowing buffers and events will be monitorized. */
flow->pad_name = gst_structure_get_string (config, "pad");
if (!flow->pad_name) {
gst_validate_error_structure (config,
"pad property is mandatory, not found in %" GST_PTR_FORMAT, config);
}
/* record-buffers: Whether buffers will be written to the expectation log. */
flow->record_buffers = FALSE;
gst_structure_get_boolean (config, "record-buffers", &flow->record_buffers);
flow->checksum_type = CHECKSUM_TYPE_NONE;
gst_structure_get_boolean (config, "buffers-checksum", &use_checksum);
if (use_checksum) {
flow->checksum_type = G_CHECKSUM_SHA1;
} else {
const gchar *checksum_type =
gst_structure_get_string (config, "buffers-checksum");
if (checksum_type) {
if (!gst_validate_utils_enum_from_str
(GST_TYPE_VALIDATE_FLOW_CHECKSUM_TYPE, checksum_type,
(guint *) & flow->checksum_type))
gst_validate_error_structure (config,
"Invalid value for buffers-checksum: %s", checksum_type);
}
}
if (flow->checksum_type != CHECKSUM_TYPE_NONE)
flow->record_buffers = TRUE;
/* caps-properties: Caps events can include many dfferent properties, but
* many of these may be irrelevant for some tests. If this option is set,
* only the listed properties will be written to the expectation log. */
flow->caps_properties =
gst_validate_utils_get_strv (config, "caps-properties");
flow->logged_event_types =
gst_validate_utils_get_strv (config, "logged-event-types");
flow->ignored_event_types =
gst_validate_utils_get_strv (config, "ignored-event-types");
tmpval = gst_structure_get_value (config, "ignored-fields");
if (tmpval) {
if (!G_VALUE_HOLDS_STRING (tmpval)) {
gst_validate_error_structure (config,
"Invalid value type for `ignored-fields`: '%s' instead of 'string'",
G_VALUE_TYPE_NAME (tmpval));
}
ignored_fields = (gchar *) g_value_get_string (tmpval);
}
if (ignored_fields) {
ignored_fields = g_strdup_printf ("ignored,%s", ignored_fields);
flow->ignored_fields = gst_structure_new_from_string (ignored_fields);
if (!flow->ignored_fields)
gst_validate_error_structure (config,
"Could not parse 'ignored-event-fields' structure: `%s`",
ignored_fields);
g_free (ignored_fields);
} else {
flow->ignored_fields =
gst_structure_new_from_string ("ignored,stream-start={stream-id}");
}
if (!gst_structure_has_field (flow->ignored_fields, "stream-start"))
gst_structure_set (flow->ignored_fields, "stream-start",
G_TYPE_STRING, "stream-id", NULL);
logged_fields = (gchar *) gst_structure_get_string (config, "logged-fields");
if (logged_fields) {
logged_fields = g_strdup_printf ("logged,%s", logged_fields);
flow->logged_fields = gst_structure_new_from_string (logged_fields);
if (!flow->logged_fields)
gst_validate_error_structure (config,
"Could not parse 'logged-fields' %s", logged_fields);
g_free (logged_fields);
} else {
flow->logged_fields = NULL;
}
/* expectations-dir: Path to the directory where the expectations will be
* written if they don't exist, relative to the current working directory.
* By default the current working directory is used. */
flow->expectations_dir =
g_strdup (gst_structure_get_string (config, "expectations-dir"));
if (!flow->expectations_dir)
flow->expectations_dir = g_strdup (".");
/* actual-results-dir: Path to the directory where the events will be
* recorded. The expectation file will be compared to this. */
flow->actual_results_dir =
g_strdup (gst_structure_get_string (config, "actual-results-dir"));
if (!flow->actual_results_dir)
flow->actual_results_dir = g_strdup (".");
{
gchar *pad_name_safe = make_safe_file_name (flow->pad_name);
gchar *expectations_file_name =
g_strdup_printf ("log-%s-expected", pad_name_safe);
gchar *actual_results_file_name =
g_strdup_printf ("log-%s-actual", pad_name_safe);
flow->expectations_file_path =
g_build_path (G_DIR_SEPARATOR_S, flow->expectations_dir,
expectations_file_name, NULL);
flow->actual_results_file_path =
g_build_path (G_DIR_SEPARATOR_S, flow->actual_results_dir,
actual_results_file_name, NULL);
g_free (expectations_file_name);
g_free (actual_results_file_name);
g_free (pad_name_safe);
}
flow->was_attached = FALSE;
gst_validate_override_register_by_name (flow->pad_name, override);
override->buffer_handler = validate_flow_override_buffer_handler;
override->buffer_probe_handler = validate_flow_override_buffer_handler;
override->event_handler = validate_flow_override_event_handler;
g_signal_connect (flow, "notify::validate-runner",
G_CALLBACK (_runner_set), NULL);
return flow;
}
static void
validate_flow_setup_files (ValidateFlowOverride * flow, gint default_generate)
{
gint local_generate_expectations = -1;
gboolean generate_if_doesn_exit = default_generate == -1;
gboolean exists =
g_file_test (flow->expectations_file_path, G_FILE_TEST_EXISTS);
if (generate_if_doesn_exit) {
gst_structure_get_boolean (flow->config, "generate-expectations",
&local_generate_expectations);
generate_if_doesn_exit = local_generate_expectations == -1;
}
if ((!default_generate || !local_generate_expectations) && !exists) {
gst_validate_error_structure (flow->config, "Not writing expectations and"
" configured expectation file %s doesn't exist in config:\n > %"
GST_PTR_FORMAT, flow->expectations_file_path, flow->config);
}
if (exists && local_generate_expectations != 1 && default_generate != 1) {
flow->mode = VALIDATE_FLOW_MODE_WRITING_ACTUAL_RESULTS;
flow->output_file_path = g_strdup (flow->actual_results_file_path);
gst_validate_printf (NULL, "**-> Checking expectations file: '%s'**\n",
flow->expectations_file_path);
} else {
flow->mode = VALIDATE_FLOW_MODE_WRITING_EXPECTATIONS;
flow->output_file_path = g_strdup (flow->expectations_file_path);
gst_validate_printf (NULL, "**-> Writing expectations file: '%s'**\n",
flow->expectations_file_path);
}
{
gchar *directory_path = g_path_get_dirname (flow->output_file_path);
if (g_mkdir_with_parents (directory_path, 0755) < 0) {
gst_validate_abort ("Could not create directory tree: %s Reason: %s",
directory_path, g_strerror (errno));
}
g_free (directory_path);
}
flow->output_file = fopen (flow->output_file_path, "wb");
if (!flow->output_file)
gst_validate_abort ("Could not open for writing: %s",
flow->output_file_path);
}
static void
_runner_set (GObject * object, GParamSpec * pspec, gpointer user_data)
{
ValidateFlowOverride *flow = VALIDATE_FLOW_OVERRIDE (object);
GstValidateRunner *runner =
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (flow));
g_signal_connect (runner, "stopping", G_CALLBACK (runner_stopping), flow);
gst_object_unref (runner);
}
static void
validate_flow_override_attached (GstValidateOverride * override)
{
ValidateFlowOverride *flow = VALIDATE_FLOW_OVERRIDE (override);
flow->was_attached = TRUE;
}
static void
run_diff (const gchar * expected_file, const gchar * actual_file)
{
GError *error = NULL;
GSubprocess *process =
g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error, "diff", "-u",
"--", expected_file, actual_file, NULL);
gchar *stdout_text = NULL;
g_subprocess_communicate_utf8 (process, NULL, NULL, &stdout_text, NULL,
&error);
if (!error) {
gboolean colored = gst_validate_has_colored_output ();
GSubprocess *process2;
gchar *fname = NULL;
gint f = g_file_open_tmp ("XXXXXX.diff", &fname, NULL);
if (f > 0) {
gchar *tmpstdout;
g_file_set_contents (fname, stdout_text, -1, NULL);
close (f);
process2 =
g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error, "bat", "-l",
"diff", "--paging", "never", "--color", colored ? "always" : "never",
fname, NULL);
g_subprocess_communicate_utf8 (process2, NULL, NULL, &tmpstdout, NULL,
&error);
if (!error) {
g_free (stdout_text);
stdout_text = tmpstdout;
} else {
colored = FALSE;
GST_DEBUG ("Could not use bat: %s", error->message);
g_clear_error (&error);
}
g_clear_object (&process2);
g_free (fname);
}
fprintf (stderr, "%s%s%s\n",
!colored ? "``` diff\n" : "", stdout_text, !colored ? "\n```" : "");
} else {
fprintf (stderr, "Cannot show more details, failed to run diff: %s",
error->message);
g_error_free (error);
}
g_object_unref (process);
g_free (stdout_text);
}
static const gchar *
_line_to_show (gchar ** lines, gsize i)
{
if (lines[i] == NULL) {
return "<nothing>";
} else if (*lines[i] == '\0') {
if (lines[i + 1] != NULL)
/* skip blank lines for reporting purposes (e.g. before CHECKPOINT) */
return lines[i + 1];
else
/* last blank line in the file */
return "<nothing>";
} else {
return lines[i];
}
}
static void
show_mismatch_error (ValidateFlowOverride * flow, gchar ** lines_expected,
gchar ** lines_actual, gsize line_index)
{
const gchar *line_expected = _line_to_show (lines_expected, line_index);
const gchar *line_actual = _line_to_show (lines_actual, line_index);
GST_VALIDATE_REPORT (flow, VALIDATE_FLOW_MISMATCH,
"Mismatch error in pad %s, line %" G_GSIZE_FORMAT
". Expected:\n%s\nActual:\n%s\n", flow->pad_name, line_index + 1,
line_expected, line_actual);
run_diff (flow->expectations_file_path, flow->actual_results_file_path);
}
static void
runner_stopping (GstValidateRunner * runner, ValidateFlowOverride * flow)
{
gchar **lines_expected, **lines_actual;
gsize i = 0;
fclose (flow->output_file);
flow->output_file = NULL;
if (!flow->was_attached) {
GST_VALIDATE_REPORT (flow, VALIDATE_FLOW_NOT_ATTACHED,
"The test ended without the pad ever being attached: %s",
flow->pad_name);
return;
}
if (flow->mode == VALIDATE_FLOW_MODE_WRITING_EXPECTATIONS) {
gst_validate_skip_test ("wrote expectation files for %s.\n",
flow->pad_name);
return;
}
{
gchar *contents;
GError *error = NULL;
g_file_get_contents (flow->expectations_file_path, &contents, NULL, &error);
if (error) {
gst_validate_abort ("Failed to open expectations file: %s Reason: %s",
flow->expectations_file_path, error->message);
}
lines_expected = g_strsplit (contents, "\n", 0);
g_free (contents);
}
{
gchar *contents;
GError *error = NULL;
g_file_get_contents (flow->actual_results_file_path, &contents, NULL,
&error);
if (error) {
gst_validate_abort ("Failed to open actual results file: %s Reason: %s",
flow->actual_results_file_path, error->message);
}
lines_actual = g_strsplit (contents, "\n", 0);
g_free (contents);
}
gst_validate_printf (flow, "Checking that flow %s matches expected flow %s\n",
flow->expectations_file_path, flow->actual_results_file_path);
for (i = 0; lines_expected[i] && lines_actual[i]; i++) {
if (g_strcmp0 (lines_expected[i], lines_actual[i])) {
show_mismatch_error (flow, lines_expected, lines_actual, i);
goto stop;
}
}
gst_validate_printf (flow, "OK\n");
if (!lines_expected[i] && lines_actual[i]) {
show_mismatch_error (flow, lines_expected, lines_actual, i);
goto stop;
} else if (lines_expected[i] && !lines_actual[i]) {
show_mismatch_error (flow, lines_expected, lines_actual, i);
goto stop;
}
stop:
g_strfreev (lines_expected);
g_strfreev (lines_actual);
}
static void
validate_flow_override_finalize (GObject * object)
{
ValidateFlowOverride *flow = VALIDATE_FLOW_OVERRIDE (object);
all_overrides = g_list_remove (all_overrides, flow);
g_free (flow->actual_results_dir);
g_free (flow->actual_results_file_path);
g_free (flow->expectations_dir);
g_free (flow->expectations_file_path);
g_free (flow->output_file_path);
if (flow->output_file)
fclose (flow->output_file);
g_strfreev (flow->caps_properties);
g_strfreev (flow->logged_event_types);
g_strfreev (flow->ignored_event_types);
if (flow->ignored_fields)
gst_structure_free (flow->ignored_fields);
G_OBJECT_CLASS (validate_flow_override_parent_class)->finalize (object);
}
static gboolean
_execute_checkpoint (GstValidateScenario * scenario, GstValidateAction * action)
{
GList *i;
gchar *checkpoint_name =
g_strdup (gst_structure_get_string (action->structure, "text"));
for (i = all_overrides; i; i = i->next) {
ValidateFlowOverride *flow = (ValidateFlowOverride *) i->data;
if (checkpoint_name)
validate_flow_override_printf (flow, "\nCHECKPOINT: %s\n\n",
checkpoint_name);
else
validate_flow_override_printf (flow, "\nCHECKPOINT\n\n");
}
g_free (checkpoint_name);
return TRUE;
}
gboolean
gst_validate_flow_init ()
{
GList *tmp;
gint default_generate = -1;
GList *config_list = gst_validate_get_config ("validateflow");
if (!config_list)
return TRUE;
for (tmp = config_list; tmp; tmp = tmp->next) {
GstStructure *config = tmp->data;
ValidateFlowOverride *flow;
if (gst_structure_has_field (config, "generate-expectations") &&
!gst_structure_has_field (config, "pad")) {
if (!gst_structure_get_boolean (config, "generate-expectations",
&default_generate)) {
gst_validate_error_structure (config,
"Field 'generate-expectations' should be a boolean");
}
continue;
}
flow = validate_flow_override_new (config);
all_overrides = g_list_append (all_overrides, flow);
}
g_list_free (config_list);
for (tmp = all_overrides; tmp; tmp = tmp->next)
validate_flow_setup_files (tmp->data, default_generate);
/* *INDENT-OFF* */
gst_validate_register_action_type ("checkpoint", "validateflow",
_execute_checkpoint, ((GstValidateActionParameter [])
{
{
.name = "text",
.description = "Text that will be logged in validateflow",
.mandatory = FALSE,
.types = "string"
},
{NULL}
}),
"Prints a line of text in validateflow logs so that it's easy to distinguish buffers and events ocurring before or after a given action.",
GST_VALIDATE_ACTION_TYPE_NONE);
/* *INDENT-ON* */
return TRUE;
}
@@ -0,0 +1,34 @@
/* GStreamer
*
* Copyright (C) 2018-2019 Igalia S.L.
* Copyright (C) 2018 Metrological Group B.V.
* Author: Alicia Boya García <aboya@igalia.com>
*
* gstvalidateflow.c: A plugin to record streams and match them to
* expectation files.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/gst.h>
#include "../../gst/validate/validate.h"
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (ValidateFlowOverride, validate_flow_override,
VALIDATE, FLOW_OVERRIDE, GstValidateOverride);
G_END_DECLS
@@ -0,0 +1,9 @@
shared_library('gstvalidateflow',
'gstvalidateflow.c', 'formatting.c',
include_directories : inc_dirs,
c_args: ['-DHAVE_CONFIG_H'],
install: true,
install_dir: validate_plugins_install_dir,
dependencies : [gst_dep, gst_pbutils_dep, gio_dep],
link_with : [gstvalidate]
)
@@ -0,0 +1,69 @@
/* Convenience header for conditional use of GNU <libintl.h>.
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
#ifndef _LIBGETTEXT_H
#define _LIBGETTEXT_H 1
/* NLS can be disabled through the configure --disable-nls option. */
#ifdef ENABLE_NLS
/* Get declarations of GNU message catalog functions. */
# include <libintl.h>
#else
/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
chokes if dcgettext is defined as a macro. So include it now, to make
later inclusions of <locale.h> a NOP. We don't include <libintl.h>
as well because people using "gettext.h" will not include <libintl.h>,
and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
is OK. */
#if defined(__sun)
# include <locale.h>
#endif
/* Disabled NLS.
The casts to 'const char *' serve the purpose of producing warnings
for invalid uses of the value returned from these functions.
On pre-ANSI systems without 'const', the config.h file is supposed to
contain "#define const". */
# define gettext(Msgid) ((const char *) (Msgid))
# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
# define ngettext(Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dngettext(Domainname, Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define textdomain(Domainname) ((const char *) (Domainname))
# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
#endif
/* A pseudo function call that serves as a marker for the automated
extraction of messages, but does not call gettext(). The run-time
translation is done at a different place in the code.
The argument, String, should be a literal string. Concatenated strings
and other string expressions won't work.
The macro's expansion is not parenthesized, so that it is suitable as
initializer for static 'char[]' or 'const char[]' variables. */
#define gettext_noop(String) String
#endif /* _LIBGETTEXT_H */
@@ -0,0 +1,370 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-bin-monitor.c - Validate BinMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gst-validate-internal.h"
#include "gst-validate-bin-monitor.h"
#include "gst-validate-monitor-factory.h"
#define PRINT_POSITION_TIMEOUT 250
/**
* SECTION:gst-validate-bin-monitor
* @short_description: Class that wraps a #GstBin for Validate checks
*
* TODO
*/
enum
{
PROP_0,
PROP_HANDLES_STATE,
PROP_LAST
};
#define gst_validate_bin_monitor_parent_class parent_class
G_DEFINE_TYPE (GstValidateBinMonitor, gst_validate_bin_monitor,
GST_TYPE_VALIDATE_ELEMENT_MONITOR);
static void
gst_validate_bin_monitor_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
gst_validate_bin_monitor_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void
gst_validate_bin_monitor_wrap_element (GstValidateBinMonitor * monitor,
GstElement * element);
static gboolean gst_validate_bin_monitor_setup (GstValidateMonitor * monitor);
static void
_validate_bin_element_added (GstBin * bin, GstElement * pad,
GstValidateBinMonitor * monitor);
static void
_validate_bin_element_removed (GstBin * bin, GstElement * element,
GstValidateBinMonitor * monitor);
static void
gst_validate_bin_set_media_descriptor (GstValidateMonitor * monitor,
GstValidateMediaDescriptor * media_descriptor)
{
GList *tmp;
GST_VALIDATE_MONITOR_LOCK (monitor);
for (tmp = GST_VALIDATE_BIN_MONITOR_CAST (monitor)->element_monitors; tmp;
tmp = tmp->next) {
GstValidateMonitor *sub_monitor = (GstValidateMonitor *) tmp->data;
gst_validate_monitor_set_media_descriptor (sub_monitor, media_descriptor);
}
GST_VALIDATE_MONITOR_UNLOCK (monitor);
GST_VALIDATE_MONITOR_CLASS (parent_class)->set_media_descriptor (monitor,
media_descriptor);
}
static void
gst_validate_bin_monitor_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
switch (prop_id) {
case PROP_HANDLES_STATE:
g_assert_not_reached ();
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_bin_monitor_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstValidateBinMonitor *monitor;
monitor = GST_VALIDATE_BIN_MONITOR_CAST (object);
switch (prop_id) {
case PROP_HANDLES_STATE:
if (monitor->scenario == NULL)
g_value_set_boolean (value, FALSE);
else
g_object_get_property (G_OBJECT (monitor->scenario), "handles-states",
value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
purge_and_unref_reporter (gpointer data)
{
GstValidateReporter *reporter = data;
gst_validate_reporter_purge_reports (reporter);
g_object_unref (reporter);
}
static void
gst_validate_bin_monitor_dispose (GObject * object)
{
GstValidateBinMonitor *monitor = GST_VALIDATE_BIN_MONITOR_CAST (object);
GstElement *bin =
GST_ELEMENT (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR_CAST
(monitor)));
if (bin) {
if (monitor->element_added_id)
g_signal_handler_disconnect (bin, monitor->element_added_id);
if (monitor->element_removed_id)
g_signal_handler_disconnect (bin, monitor->element_removed_id);
gst_object_unref (bin);
}
if (monitor->scenario) {
gst_validate_reporter_purge_reports (GST_VALIDATE_REPORTER
(monitor->scenario));
gst_clear_object (&monitor->scenario);
}
g_list_free_full (monitor->element_monitors, purge_and_unref_reporter);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_validate_bin_monitor_class_init (GstValidateBinMonitorClass * klass)
{
GObjectClass *gobject_class;
GstValidateMonitorClass *validatemonitor_class;
gobject_class = G_OBJECT_CLASS (klass);
validatemonitor_class = GST_VALIDATE_MONITOR_CLASS_CAST (klass);
gobject_class->get_property = gst_validate_bin_monitor_get_property;
gobject_class->set_property = gst_validate_bin_monitor_set_property;
gobject_class->dispose = gst_validate_bin_monitor_dispose;
g_object_class_install_property (gobject_class, PROP_HANDLES_STATE,
g_param_spec_boolean ("handles-states", "Handles state",
"True if the application should not set handle the first state change "
" False if it is application responsibility",
FALSE, G_PARAM_READABLE));
validatemonitor_class->setup = gst_validate_bin_monitor_setup;
validatemonitor_class->set_media_descriptor =
gst_validate_bin_set_media_descriptor;
}
static void
gst_validate_bin_monitor_init (GstValidateBinMonitor * bin_monitor)
{
}
/**
* gst_validate_bin_monitor_new:
* @bin: (transfer none): a #GstBin to run Validate on
*/
GstValidateBinMonitor *
gst_validate_bin_monitor_new (GstBin * bin, GstValidateRunner * runner,
GstValidateMonitor * parent)
{
GstValidateBinMonitor *monitor =
g_object_new (GST_TYPE_VALIDATE_BIN_MONITOR, "object",
bin, "validate-runner", runner, "validate-parent", parent, NULL);
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
if (target == NULL) {
g_object_unref (monitor);
return NULL;
}
gst_object_unref (target);
return monitor;
}
static void
gst_validate_bin_child_added_overrides (GstValidateMonitor * monitor,
GstElement * element)
{
GList *iter;
GST_VALIDATE_MONITOR_OVERRIDES_LOCK (monitor);
for (iter = GST_VALIDATE_MONITOR_OVERRIDES (monitor).head; iter;
iter = g_list_next (iter)) {
GstValidateOverride *override = iter->data;
gst_validate_override_element_added_handler (override, monitor, element);
}
GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (monitor);
}
static gboolean
gst_validate_bin_monitor_setup (GstValidateMonitor * monitor)
{
GstIterator *iterator;
gboolean done;
GstElement *element;
GstValidateBinMonitor *bin_monitor = GST_VALIDATE_BIN_MONITOR_CAST (monitor);
GstBin *bin = GST_BIN_CAST (gst_validate_monitor_get_target (monitor));
if (!GST_IS_BIN (bin)) {
GST_WARNING_OBJECT (monitor, "Trying to create bin monitor with other "
"type of object");
goto fail;
}
GST_DEBUG_OBJECT (bin_monitor, "Setting up monitor for bin %" GST_PTR_FORMAT,
bin);
if (g_object_get_data ((GObject *) bin, "validate-monitor")) {
GST_DEBUG_OBJECT (bin_monitor,
"Bin already has a validate-monitor associated");
goto fail;
}
bin_monitor->element_added_id =
g_signal_connect (bin, "element-added",
G_CALLBACK (_validate_bin_element_added), monitor);
bin_monitor->element_removed_id =
g_signal_connect (bin, "element-removed",
G_CALLBACK (_validate_bin_element_removed), monitor);
iterator = gst_bin_iterate_elements (bin);
done = FALSE;
while (!done) {
GValue value = { 0, };
switch (gst_iterator_next (iterator, &value)) {
case GST_ITERATOR_OK:
element = g_value_get_object (&value);
gst_validate_bin_monitor_wrap_element (bin_monitor, element);
g_value_reset (&value);
break;
case GST_ITERATOR_RESYNC:
/* TODO how to handle this? */
gst_iterator_resync (iterator);
break;
case GST_ITERATOR_ERROR:
done = TRUE;
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
gst_iterator_free (iterator);
gst_object_unref (bin);
return GST_VALIDATE_MONITOR_CLASS (parent_class)->setup (monitor);
fail:
if (bin)
gst_object_unref (bin);
return FALSE;
}
static void
gst_validate_bin_monitor_wrap_element (GstValidateBinMonitor * monitor,
GstElement * element)
{
GstValidateElementMonitor *element_monitor;
GstValidateRunner *runner =
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
GST_DEBUG_OBJECT (monitor, "Wrapping element %s", GST_ELEMENT_NAME (element));
element_monitor =
GST_VALIDATE_ELEMENT_MONITOR_CAST (gst_validate_monitor_factory_create
(GST_OBJECT_CAST (element), runner, GST_VALIDATE_MONITOR_CAST (monitor)));
g_return_if_fail (element_monitor != NULL);
GST_VALIDATE_MONITOR_CAST (element_monitor)->verbosity =
GST_VALIDATE_MONITOR_CAST (monitor)->verbosity;
gst_validate_bin_child_added_overrides (GST_VALIDATE_MONITOR (monitor),
element);
if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
GST_VALIDATE_VERBOSITY_NEW_ELEMENTS)
gst_validate_printf (NULL, "(element-added) %s added to %s\n",
GST_ELEMENT_NAME (element),
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)));
GST_VALIDATE_MONITOR_LOCK (monitor);
monitor->element_monitors = g_list_prepend (monitor->element_monitors,
element_monitor);
GST_VALIDATE_MONITOR_UNLOCK (monitor);
gst_object_unref (runner);
}
static void
_validate_bin_element_added (GstBin * bin, GstElement * element,
GstValidateBinMonitor * monitor)
{
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
g_return_if_fail (GST_ELEMENT_CAST (target) == GST_ELEMENT_CAST (bin));
gst_object_unref (target);
gst_validate_bin_monitor_wrap_element (monitor, element);
}
static void
_validate_bin_element_removed (GstBin * bin, GstElement * element,
GstValidateBinMonitor * monitor)
{
if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
GST_VALIDATE_VERBOSITY_NEW_ELEMENTS)
gst_validate_printf (NULL, "(element-removed) %s removed from %s\n",
GST_ELEMENT_NAME (element),
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)));
}
/**
* gst_validate_bin_monitor_get_scenario:
* @monitor: A #GstValidateBinMonitor
*
* Returns: (transfer full) (nullable): The #GstValidateScenario being executed
* under @monitor watch
*
* Since: 1.20
*/
GstValidateScenario *
gst_validate_bin_monitor_get_scenario (GstValidateBinMonitor * monitor)
{
if (monitor->scenario)
return gst_object_ref (monitor->scenario);
return NULL;
}
@@ -0,0 +1,91 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-bin-monitor.h - Validate BinMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_BIN_MONITOR_H__
#define __GST_VALIDATE_BIN_MONITOR_H__
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/validate/gst-validate-element-monitor.h>
#include <gst/validate/gst-validate-runner.h>
#include <gst/validate/gst-validate-scenario.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_BIN_MONITOR (gst_validate_bin_monitor_get_type ())
#define GST_IS_VALIDATE_BIN_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_BIN_MONITOR))
#define GST_IS_VALIDATE_BIN_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_BIN_MONITOR))
#define GST_VALIDATE_BIN_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_BIN_MONITOR, GstValidateBinMonitorClass))
#define GST_VALIDATE_BIN_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_BIN_MONITOR, GstValidateBinMonitor))
#define GST_VALIDATE_BIN_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_BIN_MONITOR, GstValidateBinMonitorClass))
#define GST_VALIDATE_BIN_MONITOR_CAST(obj) ((GstValidateBinMonitor*)(obj))
#define GST_VALIDATE_BIN_MONITOR_CLASS_CAST(klass) ((GstValidateBinMonitorClass*)(klass))
#endif
typedef struct _GstValidateBinMonitor GstValidateBinMonitor;
typedef struct _GstValidateBinMonitorClass GstValidateBinMonitorClass;
/**
* GstValidateBinMonitor:
*
* GStreamer Validate BinMonitor class.
*
* Class that wraps a #GstBin for Validate checks
*/
struct _GstValidateBinMonitor {
GstValidateElementMonitor parent;
GList *element_monitors;
GstValidateScenario *scenario;
/*< private >*/
gulong element_added_id;
gulong element_removed_id;
};
/**
* GstValidateBinMonitorClass:
* @parent_class: parent
*
* GStreamer Validate BinMonitor object class.
*/
struct _GstValidateBinMonitorClass {
GstValidateElementMonitorClass parent_class;
};
/* normal GObject stuff */
GST_VALIDATE_API
GType gst_validate_bin_monitor_get_type (void);
GST_VALIDATE_API GstValidateBinMonitor *
gst_validate_bin_monitor_new (GstBin * bin,
GstValidateRunner * runner,
GstValidateMonitor * parent);
GST_VALIDATE_API GstValidateScenario *
gst_validate_bin_monitor_get_scenario (GstValidateBinMonitor * monitor);
G_END_DECLS
#endif /* __GST_VALIDATE_BIN_MONITOR_H__ */
@@ -0,0 +1,352 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-element-monitor.c - Validate ElementMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gst-validate-internal.h"
#include "gst-validate-element-monitor.h"
#include "gst-validate-pad-monitor.h"
#include "gst-validate-monitor-factory.h"
#include "gst-validate-utils.h"
#include "validate.h"
#include <string.h>
/**
* SECTION:gst-validate-element-monitor
* @short_description: Class that wraps a #GstElement for Validate checks
*
* TODO
*/
#define gst_validate_element_monitor_parent_class parent_class
G_DEFINE_TYPE (GstValidateElementMonitor, gst_validate_element_monitor,
GST_TYPE_VALIDATE_MONITOR);
static void
gst_validate_element_monitor_wrap_pad (GstValidateElementMonitor * monitor,
GstPad * pad);
static gboolean gst_validate_element_monitor_do_setup (GstValidateMonitor *
monitor);
static GstElement *gst_validate_element_monitor_get_element (GstValidateMonitor
* monitor);
static void
_validate_element_pad_added (GstElement * element, GstPad * pad,
GstValidateElementMonitor * monitor);
static void
gst_validate_element_set_media_descriptor (GstValidateMonitor * monitor,
GstValidateMediaDescriptor * media_descriptor)
{
gboolean done;
GstPad *pad;
GstValidateMonitor *pmonitor;
GstIterator *iterator;
GstElement *elem = GST_ELEMENT (gst_validate_monitor_get_target (monitor));
iterator = gst_element_iterate_pads (elem);
gst_object_unref (elem);
done = FALSE;
while (!done) {
GValue value = { 0, };
switch (gst_iterator_next (iterator, &value)) {
case GST_ITERATOR_OK:
pad = g_value_get_object (&value);
pmonitor = g_object_get_data ((GObject *) pad, "validate-monitor");
if (pmonitor)
gst_validate_monitor_set_media_descriptor (pmonitor,
media_descriptor);
g_value_reset (&value);
break;
case GST_ITERATOR_RESYNC:
/* TODO how to handle this? */
gst_iterator_resync (iterator);
break;
case GST_ITERATOR_ERROR:
done = TRUE;
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
gst_iterator_free (iterator);
}
static void
purge_and_unref_reporter (gpointer data)
{
GstValidateReporter *reporter = data;
gst_validate_reporter_purge_reports (reporter);
g_object_unref (reporter);
}
static void
gst_validate_element_monitor_dispose (GObject * object)
{
GstValidateElementMonitor *monitor =
GST_VALIDATE_ELEMENT_MONITOR_CAST (object);
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
if (target) {
if (monitor->pad_added_id)
g_signal_handler_disconnect (target, monitor->pad_added_id);
gst_object_unref (target);
}
g_list_free_full (monitor->pad_monitors, purge_and_unref_reporter);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_validate_element_monitor_class_init (GstValidateElementMonitorClass * klass)
{
GObjectClass *gobject_class;
GstValidateMonitorClass *monitor_klass;
gobject_class = G_OBJECT_CLASS (klass);
monitor_klass = GST_VALIDATE_MONITOR_CLASS (klass);
gobject_class->dispose = gst_validate_element_monitor_dispose;
monitor_klass->setup = gst_validate_element_monitor_do_setup;
monitor_klass->get_element = gst_validate_element_monitor_get_element;
monitor_klass->set_media_descriptor =
gst_validate_element_set_media_descriptor;
}
static void
gst_validate_element_monitor_init (GstValidateElementMonitor * element_monitor)
{
}
/**
* gst_validate_element_monitor_new:
* @element: (transfer none): a #GstElement to run Validate on
*/
GstValidateElementMonitor *
gst_validate_element_monitor_new (GstElement * element,
GstValidateRunner * runner, GstValidateMonitor * parent)
{
GstValidateElementMonitor *monitor;
GstElement *target;
g_return_val_if_fail (element != NULL, NULL);
monitor = g_object_new (GST_TYPE_VALIDATE_ELEMENT_MONITOR, "object", element,
"validate-runner", runner, "validate-parent", parent, NULL);
target =
GST_ELEMENT (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
(monitor)));
if (!target) {
g_object_unref (monitor);
return NULL;
}
gst_object_unref (target);
return monitor;
}
static GstElement *
gst_validate_element_monitor_get_element (GstValidateMonitor * monitor)
{
return GST_ELEMENT (gst_validate_monitor_get_target (monitor));
}
static void
gst_validate_element_monitor_inspect (GstValidateElementMonitor * monitor)
{
GstElement *element;
GstElementClass *klass;
const gchar *klassname;
element =
GST_ELEMENT_CAST (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
(monitor)));
klass = GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element));
klassname =
gst_element_class_get_metadata (klass, GST_ELEMENT_METADATA_KLASS);
if (klassname) {
monitor->is_decoder = strstr (klassname, "Decoder") != NULL;
monitor->is_encoder = strstr (klassname, "Encoder") != NULL;
monitor->is_demuxer = strstr (klassname, "Demuxer") != NULL;
monitor->is_converter = strstr (klassname, "Converter") != NULL;
} else
GST_ERROR_OBJECT (element, "no klassname");
monitor->is_sink = GST_IS_BASE_SINK (element);
gst_object_unref (element);
}
static void
set_config_properties (GstValidateMonitor * monitor, GstElement * element)
{
GList *config, *l;
config = gst_validate_plugin_get_config (NULL);
for (l = config; l != NULL; l = g_list_next (l)) {
GstStructure *s = l->data;
const gchar *klass;
gchar *tmp;
const gchar *prop_name;
const GValue *prop_value;
if (g_strcmp0 (gst_structure_get_string (s, "action"), "set-property") != 0)
continue;
klass = gst_structure_get_string (s, "target-element-klass");
if (klass && !gst_validate_element_has_klass (element, klass))
continue;
prop_name = gst_structure_get_string (s, "property-name");
if (!prop_name
|| !g_object_class_find_property (G_OBJECT_GET_CLASS (element),
prop_name))
continue;
prop_value = gst_structure_get_value (s, "property-value");
if (!prop_value)
continue;
tmp = gst_value_serialize (prop_value);
gst_validate_printf (monitor, "Setting %s to %s", prop_name, tmp);
g_free (tmp);
gst_validate_object_set_property (GST_VALIDATE_REPORTER (monitor),
G_OBJECT (element), prop_name, prop_value, FALSE);
}
}
static gboolean
gst_validate_element_monitor_do_setup (GstValidateMonitor * monitor)
{
GstIterator *iterator;
gboolean done;
GstPad *pad;
GstValidateElementMonitor *elem_monitor;
GstElement *element;
GstObject *target = gst_validate_monitor_get_target (monitor);
if (!GST_IS_ELEMENT (target)) {
gst_object_unref (target);
GST_WARNING_OBJECT (monitor, "Trying to create element monitor with other "
"type of object");
return FALSE;
}
elem_monitor = GST_VALIDATE_ELEMENT_MONITOR_CAST (monitor);
GST_DEBUG_OBJECT (monitor, "Setting up monitor for element %" GST_PTR_FORMAT,
target);
element = GST_ELEMENT_CAST (target);
if (g_object_get_data ((GObject *) element, "validate-monitor")) {
GST_DEBUG_OBJECT (elem_monitor,
"Pad already has a validate-monitor associated");
gst_object_unref (target);
return FALSE;
}
if (!GST_IS_BIN (element))
gst_validate_element_monitor_inspect (elem_monitor);
elem_monitor->pad_added_id = g_signal_connect (element, "pad-added",
G_CALLBACK (_validate_element_pad_added), monitor);
iterator = gst_element_iterate_pads (element);
done = FALSE;
while (!done) {
GValue value = { 0, };
switch (gst_iterator_next (iterator, &value)) {
case GST_ITERATOR_OK:
pad = g_value_get_object (&value);
gst_validate_element_monitor_wrap_pad (elem_monitor, pad);
g_value_reset (&value);
break;
case GST_ITERATOR_RESYNC:
/* TODO how to handle this? */
gst_iterator_resync (iterator);
break;
case GST_ITERATOR_ERROR:
done = TRUE;
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
gst_iterator_free (iterator);
gst_object_unref (target);
set_config_properties (monitor, element);
return TRUE;
}
static void
gst_validate_element_monitor_wrap_pad (GstValidateElementMonitor * monitor,
GstPad * pad)
{
GstValidatePadMonitor *pad_monitor;
GstValidateRunner *runner =
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
GST_DEBUG_OBJECT (monitor, "Wrapping pad %s:%s", GST_DEBUG_PAD_NAME (pad));
pad_monitor =
GST_VALIDATE_PAD_MONITOR (gst_validate_monitor_factory_create (GST_OBJECT
(pad), runner, GST_VALIDATE_MONITOR (monitor)));
g_return_if_fail (pad_monitor != NULL);
GST_VALIDATE_MONITOR_LOCK (monitor);
monitor->pad_monitors = g_list_prepend (monitor->pad_monitors, pad_monitor);
GST_VALIDATE_MONITOR_UNLOCK (monitor);
gst_object_unref (runner);
}
static void
_validate_element_pad_added (GstElement * element, GstPad * pad,
GstValidateElementMonitor * monitor)
{
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
g_return_if_fail (target == (GstObject *) element);
gst_object_unref (target);
gst_validate_element_monitor_wrap_pad (monitor, pad);
}
@@ -0,0 +1,93 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-element-monitor.h - Validate ElementMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_ELEMENT_MONITOR_H__
#define __GST_VALIDATE_ELEMENT_MONITOR_H__
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/validate/gst-validate-monitor.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_ELEMENT_MONITOR (gst_validate_element_monitor_get_type ())
#define GST_IS_VALIDATE_ELEMENT_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_ELEMENT_MONITOR))
#define GST_IS_VALIDATE_ELEMENT_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_ELEMENT_MONITOR))
#define GST_VALIDATE_ELEMENT_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_ELEMENT_MONITOR, GstValidateElementMonitorClass))
#define GST_VALIDATE_ELEMENT_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_ELEMENT_MONITOR, GstValidateElementMonitor))
#define GST_VALIDATE_ELEMENT_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_ELEMENT_MONITOR, GstValidateElementMonitorClass))
#define GST_VALIDATE_ELEMENT_MONITOR_CAST(obj) ((GstValidateElementMonitor*)(obj))
#define GST_VALIDATE_ELEMENT_MONITOR_CLASS_CAST(klass) ((GstValidateElementMonitorClass*)(klass))
#endif
#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DECODER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_decoder)
#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_ENCODER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_encoder)
#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DEMUXER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_demuxer)
#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_CONVERTER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_converter)
#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_SINK(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_sink)
typedef struct _GstValidateElementMonitor GstValidateElementMonitor;
typedef struct _GstValidateElementMonitorClass GstValidateElementMonitorClass;
/**
* GstValidateElementMonitor:
*
* GStreamer Validate ElementMonitor class.
*
* Class that wraps a #GstElement for Validate checks
*/
struct _GstValidateElementMonitor {
GstValidateMonitor parent;
/*< private >*/
gulong pad_added_id;
GList *pad_monitors;
gboolean is_decoder;
gboolean is_encoder;
gboolean is_demuxer;
gboolean is_converter;
gboolean is_sink;
};
/**
* GstValidateElementMonitorClass:
* @parent_class: parent
*
* GStreamer Validate ElementMonitor object class.
*/
struct _GstValidateElementMonitorClass {
GstValidateMonitorClass parent_class;
};
/* normal GObject stuff */
GST_VALIDATE_API
GType gst_validate_element_monitor_get_type (void);
GST_VALIDATE_API
GstValidateElementMonitor * gst_validate_element_monitor_new (GstElement * element, GstValidateRunner * runner, GstValidateMonitor * parent);
G_END_DECLS
#endif /* __GST_VALIDATE_ELEMENT_MONITOR_H__ */
@@ -0,0 +1,41 @@
/*** BEGIN file-header ***/
#include "gst-validate-enum-types.h"
#include <gst/validate/validate.h>
#define C_ENUM(v) ((gint) v)
#define C_FLAGS(v) ((guint) v)
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
static gsize id = 0;
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
if (g_once_init_enter (&id)) {
GType tmp = g_@type@_register_static ("@EnumName@", values);
g_once_init_leave (&id, tmp);
}
return (GType) id;
}
/*** END value-tail ***/
/*** BEGIN file-tail ***/
/*** END file-tail ***/
@@ -0,0 +1,26 @@
/*** BEGIN file-header ***/
#ifndef __GST_ENUM_TYPES_H__
#define __GST_ENUM_TYPES_H__
#include <glib-object.h>
#include <gst/validate/validate.h>
#include <gst/validate/media-descriptor-writer.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
GST_VALIDATE_API GType @enum_name@_get_type (void);
#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
/*** END value-header ***/
/*** BEGIN file-tail ***/
G_END_DECLS
#endif /* __GST_ENUM_TYPES_H__ */
/*** END file-tail ***/
@@ -0,0 +1,116 @@
/* GStreamer
* Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@collabora.com>
*
* gst-validate-enums.h - Validate constants.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_ENUMS_H__
#define __GST_VALIDATE_ENUMS_H__
/**
* SECTION: gst-validate-enums.h
* @title: GstValidate enums
*/
/**
* GstValidateReportingDetails:
* @GST_VALIDATE_SHOW_NONE: No debugging level specified or desired. Used to deactivate
* debugging output.
* @GST_VALIDATE_SHOW_SMART: Sythetic for not fatal issues and detailed for
* others
* @GST_VALIDATE_SHOW_SYNTHETIC: Summary of the issues found, with no
* details.
* @GST_VALIDATE_SHOW_SUBCHAIN: If set as the default level, similar
* issues can be reported multiple times for different subchains.
* If set as the level for a particular object (my_object:subchain), validate
* will report the issues where the object is the first to report an issue for
* a subchain.
* @GST_VALIDATE_SHOW_MONITOR: If set as the default level, all the
* distinct issues for all the monitors will be reported.
* If set as the level for a particular object, all the distinct issues for this object
* will be reported.
* Note that if the same issue happens twice on the same object, up until this
* level that issue is only reported once.
* @GST_VALIDATE_SHOW_ALL: All the issues will be reported, even those
* that repeat themselves inside the same object. This can be *very* verbose if
* set globally.
* @GST_VALIDATE_SHOW_UNKNOWN: No reporting level known,
* reporting will default to the global reporting level.
*
* Setting the reporting level allows to control the way issues are reported
* when calling #gst_validate_runner_printf.
*
* The reporting level can be set through the "GST_VALIDATE_REPORTING_DETAILS"
* environment variable, as a comma-separated list of (optional) object categories / names
* and levels. No object category / name sets the global level.
*
* Examples: GST_VALIDATE_REPORTING_DETAILS=synthetic,h264parse:all
* GST_VALIDATE_REPORTING_DETAILS=none,h264parse::sink_0:synthetic
*/
typedef enum {
GST_VALIDATE_SHOW_UNKNOWN = 0,
GST_VALIDATE_SHOW_NONE = 1,
GST_VALIDATE_SHOW_SYNTHETIC = 2,
GST_VALIDATE_SHOW_SUBCHAIN = 3,
GST_VALIDATE_SHOW_MONITOR = 4,
GST_VALIDATE_SHOW_ALL = 5,
GST_VALIDATE_SHOW_SMART = 6,
GST_VALIDATE_SHOW_COUNT
} GstValidateReportingDetails;
/**
* GST_VALIDATE_SHOW_DEFAULT:
*
* Defines the default reporting level to be used with gst-validate. It is normally
* set to #GST_VALIDATE_SHOW_SYNTHETIC so only a synthetic report
* gets printed.
* As it can be configured at compile time, developer builds may chose to
* override that though.
*/
#ifndef GST_VALIDATE_SHOW_DEFAULT
#define GST_VALIDATE_SHOW_DEFAULT GST_VALIDATE_SHOW_SMART
#endif
/**
* GstValidateVerbosityFlags:
*
* Defines the level of verbosity of -validate (ie, printing on stdout).
*/
typedef enum
{
GST_VALIDATE_VERBOSITY_NONE = 0,
GST_VALIDATE_VERBOSITY_POSITION = 1 << 1,
GST_VALIDATE_VERBOSITY_MESSAGES = 1 << 2,
GST_VALIDATE_VERBOSITY_PROPS_CHANGES = 1 << 3,
GST_VALIDATE_VERBOSITY_NEW_ELEMENTS = 1 << 4,
GST_VALIDATE_VERBOSITY_ALL = GST_VALIDATE_VERBOSITY_POSITION | GST_VALIDATE_VERBOSITY_MESSAGES | GST_VALIDATE_VERBOSITY_PROPS_CHANGES | GST_VALIDATE_VERBOSITY_NEW_ELEMENTS
} GstValidateVerbosityFlags;
/**
* GstValidateStructureResolveVariablesFlags:
*
* Since: 1.20
*/
typedef enum {
GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL = 0,
GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_LOCAL_ONLY = 1 << 0,
GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_FAILURE = 1 << 1,
GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_EXPRESSION = 1 << 1,
} GstValidateStructureResolveVariablesFlags;
#endif /* __GST_VALIDATE_ENUMS_H__ */
@@ -0,0 +1,166 @@
#include <gst/gst.h>
#include "validate.h"
#include "gst-validate-utils.h"
#include "gst-validate-internal.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define EXTRA_CHECKS_WRONG_NUMBER_OF_INSTANCES g_quark_from_static_string ("extrachecks::wrong-number-of-instances")
typedef struct
{
gchar *pname;
gchar *klass;
gint expected_n_instances;
gint n_instances;
} CheckNumInstanceData;
static CheckNumInstanceData *
gst_validate_check_num_instances_data_new (GstStructure * check)
{
CheckNumInstanceData *data = g_new0 (CheckNumInstanceData, 1);
if (!gst_structure_get_int (check, "num-instances",
&data->expected_n_instances)) {
gst_validate_abort
("[CONFIG ERROR] Mandatory field `num-instances` not found in "
"extra-check `num-instances`");
goto failed;
}
data->pname = g_strdup (gst_structure_get_string (check, "pipeline-name"));
if (!data->pname) {
gst_validate_abort
("[CONFIG ERROR] Mandatory field `pipeline` not found in "
"extra-check `num-instances`");
goto failed;
}
data->klass = g_strdup (gst_structure_get_string (check, "element-klass"));
if (!data->klass) {
gst_validate_abort
("[CONFIG ERROR] Mandatory field `element-klass` not found in "
"extra-check `num-instances`");
goto failed;
}
return data;
failed:
g_free (data);
g_free (data->klass);
return NULL;
}
static void
gst_validate_check_num_instances_data_free (CheckNumInstanceData * data)
{
g_free (data->pname);
g_free (data);
}
static void
gst_validate_check_num_instances (GstValidateOverride * o,
GstValidateMonitor * monitor, GstElement * nelem)
{
gchar *pname;
CheckNumInstanceData *data = g_object_get_data (G_OBJECT (o), "check-data");
GstPipeline *pipe = gst_validate_monitor_get_pipeline (monitor);
if (!pipe)
return;
pname = gst_object_get_name (GST_OBJECT (pipe));
if (g_strcmp0 (data->pname, pname))
goto done;
if (!gst_validate_element_has_klass (nelem, data->klass))
return;
data->n_instances++;
if (data->n_instances > data->expected_n_instances) {
GST_VALIDATE_REPORT (o, EXTRA_CHECKS_WRONG_NUMBER_OF_INSTANCES,
"%d instances allows in pipeline %s but already %d where added.",
data->expected_n_instances, pname, data->n_instances);
}
GST_ERROR_OBJECT (nelem, "HERE I AM %d", data->n_instances);
done:
g_free (pname);
gst_object_unref (pipe);
}
static void
runner_stopping (GstValidateRunner * runner, GstValidateOverride * o)
{
CheckNumInstanceData *data = g_object_get_data (G_OBJECT (o), "check-data");
if (data->expected_n_instances != data->n_instances) {
GST_VALIDATE_REPORT (o, EXTRA_CHECKS_WRONG_NUMBER_OF_INSTANCES,
"%d instances expected in pipeline %s but %d where added.",
data->expected_n_instances, data->pname, data->n_instances);
}
}
static void
_runner_set (GObject * object, GParamSpec * pspec, gpointer user_data)
{
GstValidateRunner *runner =
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object));
g_signal_connect (runner, "stopping", G_CALLBACK (runner_stopping), object);
gst_object_unref (runner);
}
static void
gst_validate_add_num_instances_check (GstStructure * structure)
{
CheckNumInstanceData *data =
gst_validate_check_num_instances_data_new (structure);
GstValidateOverride *o = gst_validate_override_new ();
g_object_set_data_full (G_OBJECT (o), "check-data", data,
(GDestroyNotify) gst_validate_check_num_instances_data_free);
gst_validate_override_set_element_added_handler (o,
gst_validate_check_num_instances);
g_signal_connect (o, "notify::validate-runner", G_CALLBACK (_runner_set),
NULL);
gst_validate_override_register_by_type (GST_TYPE_BIN, o);
gst_object_unref (o);
}
gboolean
gst_validate_extra_checks_init ()
{
GList *config, *tmp;
config = gst_validate_get_config ("extrachecks");
if (!config)
return TRUE;
for (tmp = config; tmp; tmp = tmp->next) {
GstStructure *check = tmp->data;
if (gst_structure_has_field (check, "num-instances"))
gst_validate_add_num_instances_check (check);
}
g_list_free (config);
gst_validate_issue_register (gst_validate_issue_new
(EXTRA_CHECKS_WRONG_NUMBER_OF_INSTANCES,
"The configured number of possible instances of an element type"
" in a pipeline is not respected.",
"The `num-instances` extra checks allow user to make sure that"
" a previously defined number of instances of an element is added"
" in a given pipeline, that test failed.",
GST_VALIDATE_REPORT_LEVEL_CRITICAL));
return TRUE;
}
@@ -0,0 +1,47 @@
/* GStreamer
* Copyright (C) 2004 Thomas Vander Stichele <thomas@apestaart.org>
*
* gst-validate-i18n-lib.h: internationalization macros for the GStreamer libraries
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_I18N_LIB_H__
#define __GST_VALIDATE_I18N_LIB_H__
#ifndef PACKAGE_NAME
#error You must include config.h before including this header.
#endif
#ifdef ENABLE_NLS
#include <locale.h> /* some people need it and some people don't */
#include "gettext.h" /* included with gettext distribution and copied */
/* we want to use shorthand _() for translating and N_() for marking */
#define _(String) dgettext (GETTEXT_PACKAGE, String)
#define N_(String) gettext_noop (String)
/* FIXME: if we need it, we can add Q_ as well, like in glib */
#else
#define _(String) String
#define N_(String) String
#define ngettext(Singular,Plural,Count) ((Count>1)?Plural:Singular)
#endif
#endif /* __GST_VALIDATE_I18N_LIB_H__ */
@@ -0,0 +1,74 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* validate.c - Validate generic functions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_INTERNAL_H__
#define __GST_VALIDATE_INTERNAL_H__
#include <gst/gst.h>
#include "gst-validate-scenario.h"
#include "gst-validate-monitor.h"
#include <json-glib/json-glib.h>
extern G_GNUC_INTERNAL GstDebugCategory *gstvalidate_debug;
#define GST_CAT_DEFAULT gstvalidate_debug
extern G_GNUC_INTERNAL GRegex *newline_regex;
extern G_GNUC_INTERNAL GstClockTime _priv_start_time;
extern G_GNUC_INTERNAL GQuark _Q_VALIDATE_MONITOR;
/* If an action type is 1 (TRUE) we also consider it is a config to keep backward compatibility */
#define IS_CONFIG_ACTION_TYPE(type) (((type) & GST_VALIDATE_ACTION_TYPE_CONFIG) || ((type) == TRUE))
extern G_GNUC_INTERNAL GType _gst_validate_action_type_type;
void init_scenarios (void);
void register_action_types (void);
/* FIXME 2.0 Remove that as this is only for backward compatibility
* as we used to have to print actions in the action execution function
* and this is done by the scenario itself now */
G_GNUC_INTERNAL gboolean _action_check_and_set_printed (GstValidateAction *action);
G_GNUC_INTERNAL gboolean gst_validate_action_get_level (GstValidateAction *action);
G_GNUC_INTERNAL gboolean gst_validate_scenario_check_and_set_needs_clock_sync (GList *structures, GstStructure **meta);
#define GST_VALIDATE_SCENARIO_SUFFIX ".scenario"
G_GNUC_INTERNAL gchar** gst_validate_scenario_get_include_paths(const gchar* relative_scenario);
G_GNUC_INTERNAL void _priv_validate_override_registry_deinit(void);
G_GNUC_INTERNAL GstValidateReportingDetails gst_validate_runner_get_default_reporting_details (GstValidateRunner *runner);
G_GNUC_INTERNAL GstValidateMonitor * gst_validate_get_monitor (GObject *object);
G_GNUC_INTERNAL void gst_validate_init_runner (void);
G_GNUC_INTERNAL void gst_validate_deinit_runner (void);
G_GNUC_INTERNAL void gst_validate_report_deinit (void);
G_GNUC_INTERNAL gboolean gst_validate_send (JsonNode * root);
G_GNUC_INTERNAL void gst_validate_set_test_file_globals (GstStructure* meta, const gchar* testfile, gboolean use_fakesinks);
G_GNUC_INTERNAL gboolean gst_validate_get_test_file_scenario (GList** structs, const gchar** scenario_name, gchar** original_name);
G_GNUC_INTERNAL GstValidateScenario* gst_validate_scenario_from_structs (GstValidateRunner* runner, GstElement* pipeline, GList* structures,
gchar* origin_file);
G_GNUC_INTERNAL GList* gst_validate_get_config (const gchar *structname);
G_GNUC_INTERNAL GList * gst_validate_get_test_file_expected_issues (void);
G_GNUC_INTERNAL gboolean gst_validate_extra_checks_init (void);
G_GNUC_INTERNAL gboolean gst_validate_flow_init (void);
G_GNUC_INTERNAL gboolean is_tty (void);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-media-info.h - Media information structure
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_MEDIA_INFO_H__
#define __GST_VALIDATE_MEDIA_INFO_H__
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include <gst/validate/validate-prelude.h>
G_BEGIN_DECLS
typedef struct _GstValidateMediaInfo GstValidateMediaInfo;
typedef struct _GstValidateStreamInfo GstValidateStreamInfo;
/**
* GstValidateMediaInfo:
*
* GStreamer Validate MediaInfo struct.
*
* Stores extracted information about a media
*/
struct _GstValidateMediaInfo {
/* <File checking data> */
/* Value for the expected total duration of the file in nanosecs
* Set to GST_CLOCK_TIME_NONE if it shouldn't be tested */
GstClockTime duration;
gboolean is_image;
/* Expected file_size, set to 0 to skip test */
guint64 file_size;
gboolean seekable;
gchar *playback_error;
gchar *reverse_playback_error;
gchar *track_switch_error;
gchar *uri;
gboolean discover_only;
GstValidateStreamInfo *stream_info;
};
GST_VALIDATE_API
void gst_validate_media_info_init (GstValidateMediaInfo * mi);
GST_VALIDATE_API
void gst_validate_media_info_clear (GstValidateMediaInfo * mi);
GST_VALIDATE_API
void gst_validate_media_info_free (GstValidateMediaInfo * mi);
GST_VALIDATE_API
gchar * gst_validate_media_info_to_string (GstValidateMediaInfo * mi, gsize * length);
GST_VALIDATE_API
gboolean gst_validate_media_info_save (GstValidateMediaInfo * mi, const gchar * path, GError ** err);
GST_VALIDATE_API
GstValidateMediaInfo * gst_validate_media_info_load (const gchar * path, GError ** err);
GST_VALIDATE_API
gboolean gst_validate_media_info_inspect_uri (GstValidateMediaInfo * mi, const gchar * uri,
gboolean discover_only, GError ** err);
GST_VALIDATE_API
gboolean gst_validate_media_info_compare (GstValidateMediaInfo * expected, GstValidateMediaInfo * extracted);
G_END_DECLS
#endif /* __GST_VALIDATE_MEDIA_INFO_H__ */
@@ -0,0 +1,183 @@
/* GStreamer
* Copyright (C) 2019 Igalia S.L
* Copyright (C) 2019 Metrological
* Author: Charlie Turner <cturner@igalia.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-validate-mockdecryptor.h"
#define CLEARKEY_SYSTEM_ID "78f32170-d883-11e0-9572-0800200c9a66"
#define WIDEVINE_SYSTEM_ID "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
GST_DEBUG_CATEGORY_STATIC (gst_mockdecryptor_debug);
#define GST_CAT_DEFAULT gst_mockdecryptor_debug
static GstStaticPadTemplate gst_mockdecryptor_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS
("application/x-cenc, original-media-type=(string)video/x-h264, "
GST_PROTECTION_SYSTEM_ID_CAPS_FIELD "=(string)" WIDEVINE_SYSTEM_ID "; "
"application/x-cenc, original-media-type=(string)audio/mpeg, "
GST_PROTECTION_SYSTEM_ID_CAPS_FIELD "=(string)" WIDEVINE_SYSTEM_ID)
);
static GstStaticPadTemplate gst_mockdecryptor_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/webm; "
"audio/webm; "
"video/mp4; " "audio/mp4; " "audio/mpeg; " "video/x-h264"));
#define _mockdecryptor_do_init \
GST_DEBUG_CATEGORY_INIT (gst_mockdecryptor_debug, GST_MOCKDECRYPTOR_NAME, 0, "mock decryptor element");
#define gst_mockdecryptor_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstMockDecryptor, gst_mockdecryptor,
GST_TYPE_BASE_TRANSFORM, _mockdecryptor_do_init);
static GstCaps *gst_mockdecryptor_transform_caps (GstBaseTransform *,
GstPadDirection, GstCaps *, GstCaps *);
static GstFlowReturn gst_mockdecryptor_transform_in_place (GstBaseTransform *,
GstBuffer *);
static void
gst_mockdecryptor_class_init (GstMockDecryptorClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseTransformClass *base_transform_class =
GST_BASE_TRANSFORM_CLASS (klass);
base_transform_class->transform_ip =
GST_DEBUG_FUNCPTR (gst_mockdecryptor_transform_in_place);
base_transform_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_mockdecryptor_transform_caps);
base_transform_class->transform_ip_on_passthrough = FALSE;
gst_element_class_add_static_pad_template (element_class,
&gst_mockdecryptor_sink_template);
gst_element_class_add_static_pad_template (element_class,
&gst_mockdecryptor_src_template);
gst_element_class_set_metadata (element_class,
"Mock decryptor element for unit tests",
GST_ELEMENT_FACTORY_KLASS_DECRYPTOR,
"Use in unit tests", "Charlie Turner <cturner@igalia.com>");
}
static void
gst_mockdecryptor_init (GstMockDecryptor * klass)
{
}
static GstCaps *
gst_mockdecryptor_transform_caps (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, GstCaps * filter)
{
GstCaps *transformed_caps = NULL;
guint incoming_caps_size, size;
gint duplicate;
guint i;
if (direction == GST_PAD_UNKNOWN)
return NULL;
GST_DEBUG_OBJECT (base,
"direction: %s, caps: %" GST_PTR_FORMAT " filter: %" GST_PTR_FORMAT,
(direction == GST_PAD_SRC) ? "src" : "sink", caps, filter);
transformed_caps = gst_caps_new_empty ();
incoming_caps_size = gst_caps_get_size (caps);
for (i = 0; i < incoming_caps_size; ++i) {
GstStructure *incoming_structure = gst_caps_get_structure (caps, i);
GstStructure *outgoing_structure = NULL;
guint index;
if (direction == GST_PAD_SINK) {
if (!gst_structure_has_field (incoming_structure, "original-media-type"))
continue;
outgoing_structure = gst_structure_copy (incoming_structure);
gst_structure_set_name (outgoing_structure,
gst_structure_get_string (outgoing_structure, "original-media-type"));
gst_structure_remove_fields (outgoing_structure, "protection-system",
"original-media-type", "encryption-algorithm", "encoding-scope",
"cipher-mode", NULL);
} else {
outgoing_structure = gst_structure_copy (incoming_structure);
/* Filter out the video related fields from the up-stream caps,
* because they are not relevant to the input caps of this element and
* can cause caps negotiation failures with adaptive bitrate streams.
*/
gst_structure_remove_fields (outgoing_structure, "base-profile",
"codec_data", "height", "framerate", "level", "pixel-aspect-ratio",
"profile", "rate", "width", NULL);
gst_structure_set (outgoing_structure,
"protection-system", G_TYPE_STRING, WIDEVINE_SYSTEM_ID,
"original-media-type", G_TYPE_STRING,
gst_structure_get_name (incoming_structure), NULL);
gst_structure_set_name (outgoing_structure, "application/x-cenc");
}
duplicate = FALSE;
size = gst_caps_get_size (transformed_caps);
for (index = 0; !duplicate && index < size; ++index) {
GstStructure *structure =
gst_caps_get_structure (transformed_caps, index);
if (gst_structure_is_equal (structure, outgoing_structure))
duplicate = TRUE;
}
if (!duplicate)
gst_caps_append_structure (transformed_caps, outgoing_structure);
else
gst_structure_free (outgoing_structure);
}
if (filter) {
GstCaps *intersection;
GST_DEBUG_OBJECT (base, "Using filter caps %" GST_PTR_FORMAT, filter);
intersection =
gst_caps_intersect_full (transformed_caps, filter,
GST_CAPS_INTERSECT_FIRST);
gst_caps_replace (&transformed_caps, intersection);
}
GST_DEBUG_OBJECT (base, "returning %" GST_PTR_FORMAT, transformed_caps);
return transformed_caps;
}
static GstFlowReturn
gst_mockdecryptor_transform_in_place (GstBaseTransform * base,
GstBuffer * buffer)
{
/* We are a mock decryptor, just pass the encrypted buffers through... */
return GST_FLOW_OK;
}
@@ -0,0 +1,61 @@
/* GStreamer
* Copyright (C) 2019 Igalia S.L
* Copyright (C) 2019 Metrological
* Author: Charlie Turner <cturner@igalia.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_MOCKDECRYPTOR_H__
#define __GST_MOCKDECRYPTOR_H__
typedef struct _GstMockDecryptor GstMockDecryptor;
typedef struct _GstMockDecryptorClass GstMockDecryptorClass;
#include <gst/gst.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
#define GST_TYPE_MOCKDECRYPTOR \
(gst_mockdecryptor_get_type ())
#define GST_MOCKDECRYPTOR(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MOCKDECRYPTOR,GstMockDecryptor))
#define GST_MOCKDECRYPTOR_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MOCKDECRYPTOR,GstMockDecryptorClass))
#define GST_IS_MOCKDECRYPTOR(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MOCKDECRYPTOR))
#define GST_IS_MOCKDECRYPTOR_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MOCKDECRYPTOR))
#define GST_MOCKDECRYPTOR_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_MOCKDECRYPTOR,GstMockDecryptorClass))
#define GST_MOCKDECRYPTOR_NAME "mockdecryptor"
struct _GstMockDecryptor
{
GstBaseTransform element;
};
struct _GstMockDecryptorClass
{
GstBaseTransformClass parent_class;
};
G_GNUC_INTERNAL GType gst_mockdecryptor_get_type (void);
G_END_DECLS
#endif /* __GST_MOCKDECRYPTOR_H__ */
@@ -0,0 +1,90 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-monitor-factory.c - Validate Element monitors factory utility functions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:gst-validate-monitor-factory
* @title: GstValidateMonitorFactory
* @short_description: Lets you start monitoring a #GstObject with GstValidate
*
* To start monitoring and thus run GstValidate tests on a #GstPipeline, the only thing to
* do is to instanciate a #GstValidateRunner and then attach a #GstValidateMonitor
* to it with #gst_validate_monitor_factory_create
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gst-validate-monitor-factory.h"
#include "gst-validate-bin-monitor.h"
#include "gst-validate-pipeline-monitor.h"
#include "gst-validate-pad-monitor.h"
#include "gst-validate-override-registry.h"
/**
* gst_validate_monitor_factory_create:
* @target: The #GstObject to create a #GstValidateMonitor for
* @runner: The #GstValidateRunner to use for the new monitor
* @parent: (nullable): The parent of the new monitor
*
* Create a new monitor for @target and starts monitoring it.
*
* Returns: (transfer full): The newly created #GstValidateMonitor
*/
GstValidateMonitor *
gst_validate_monitor_factory_create (GstObject * target,
GstValidateRunner * runner, GstValidateMonitor * parent)
{
GstValidateMonitor *monitor = NULL;
g_return_val_if_fail (target != NULL, NULL);
monitor = g_object_get_data ((GObject *) target, "validate-monitor");
if (monitor) {
GST_INFO_OBJECT (target, "Is already monitored by %" GST_PTR_FORMAT,
monitor);
return g_object_ref (monitor);
}
if (GST_IS_PAD (target)) {
monitor =
GST_VALIDATE_MONITOR_CAST (gst_validate_pad_monitor_new (GST_PAD_CAST
(target), runner, GST_VALIDATE_ELEMENT_MONITOR_CAST (parent)));
} else if (GST_IS_PIPELINE (target)) {
monitor =
GST_VALIDATE_MONITOR_CAST (gst_validate_pipeline_monitor_new
(GST_PIPELINE_CAST (target), runner, parent));
} else if (GST_IS_BIN (target)) {
monitor =
GST_VALIDATE_MONITOR_CAST (gst_validate_bin_monitor_new (GST_BIN_CAST
(target), runner, parent));
} else if (GST_IS_ELEMENT (target)) {
monitor =
GST_VALIDATE_MONITOR_CAST (gst_validate_element_monitor_new
(GST_ELEMENT_CAST (target), runner, parent));
} else {
g_assert_not_reached ();
}
return monitor;
}
@@ -0,0 +1,39 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-monitor-factory.h - Validate Element monitors factory utility functions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_MONITOR_FACTORY_H__
#define __GST_VALIDATE_MONITOR_FACTORY_H__
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/validate/gst-validate-monitor.h>
#include <gst/validate/gst-validate-runner.h>
G_BEGIN_DECLS
GST_VALIDATE_API
GstValidateMonitor * gst_validate_monitor_factory_create (GstObject * target, GstValidateRunner * runner, GstValidateMonitor * parent);
G_END_DECLS
#endif /* __GST_VALIDATE_MONITOR_FACTORY_H__ */
@@ -0,0 +1,497 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-monitor.c - Validate Monitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gst-validate-enum-types.h"
#include "gst-validate-internal.h"
#include "gst-validate-monitor.h"
#include "gst-validate-override-registry.h"
/**
* SECTION:gst-validate-monitor
* @short_description: Base class that wraps a #GObject for Validate checks
*
* TODO
*/
enum
{
PROP_0,
PROP_OBJECT,
PROP_PIPELINE,
PROP_RUNNER,
PROP_VALIDATE_PARENT,
PROP_VERBOSITY,
PROP_LAST
};
static gboolean gst_validate_monitor_do_setup (GstValidateMonitor * monitor);
static void
gst_validate_monitor_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
gst_validate_monitor_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static GObject *gst_validate_monitor_constructor (GType type,
guint n_construct_params, GObjectConstructParam * construct_params);
static gboolean gst_validate_monitor_setup (GstValidateMonitor * monitor);
static GstValidateInterceptionReturn
gst_validate_monitor_intercept_report (GstValidateReporter * reporter,
GstValidateReport * report);
#define _do_init \
G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init)
static GstValidateReportingDetails
_get_reporting_level (GstValidateReporter * monitor)
{
return GST_VALIDATE_MONITOR (monitor)->level;
}
/**
* gst_validate_monitor_get_pipeline:
* @monitor: The monitor to get the pipeline from
*
* Returns: (transfer full): The pipeline in which @monitor
* target is in.
*/
GstPipeline *
gst_validate_monitor_get_pipeline (GstValidateMonitor * monitor)
{
return g_weak_ref_get (&monitor->pipeline);
}
/**
* gst_validate_monitor_get_target:
* @monitor: The monitor to get the target from
*
* Returns: (transfer full): The target object
*/
GstObject *
gst_validate_monitor_get_target (GstValidateMonitor * monitor)
{
return g_weak_ref_get (&monitor->target);
}
static GstPipeline *
_get_pipeline (GstValidateReporter * monitor)
{
return g_weak_ref_get (&(GST_VALIDATE_MONITOR (monitor)->pipeline));
}
static void
_reporter_iface_init (GstValidateReporterInterface * iface)
{
iface->intercept_report = gst_validate_monitor_intercept_report;
iface->get_reporting_level = _get_reporting_level;
iface->get_pipeline = _get_pipeline;
}
#define gst_validate_monitor_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstValidateMonitor, gst_validate_monitor,
GST_TYPE_OBJECT, _do_init);
static void
gst_validate_monitor_dispose (GObject * object)
{
GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (object);
g_mutex_clear (&monitor->mutex);
g_mutex_clear (&monitor->overrides_mutex);
g_queue_clear (&monitor->overrides);
g_weak_ref_clear (&monitor->pipeline);
g_weak_ref_clear (&monitor->target);
if (monitor->media_descriptor)
gst_object_unref (monitor->media_descriptor);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_validate_monitor_finalize (GObject * object)
{
gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (object), NULL);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_validate_monitor_class_init (GstValidateMonitorClass * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gst_validate_monitor_get_property;
gobject_class->set_property = gst_validate_monitor_set_property;
gobject_class->dispose = gst_validate_monitor_dispose;
gobject_class->finalize = gst_validate_monitor_finalize;
gobject_class->constructor = gst_validate_monitor_constructor;
klass->setup = gst_validate_monitor_do_setup;
g_object_class_install_property (gobject_class, PROP_OBJECT,
g_param_spec_object ("object", "Object", "The object to be monitored",
G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_PIPELINE,
g_param_spec_object ("pipeline", "Pipeline", "The pipeline in which the"
"monitored object is", GST_TYPE_PIPELINE,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_RUNNER,
g_param_spec_object ("validate-runner", "VALIDATE Runner",
"The Validate runner to report errors to",
GST_TYPE_VALIDATE_RUNNER,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_VALIDATE_PARENT,
g_param_spec_object ("validate-parent", "VALIDATE parent monitor",
"The Validate monitor that is the parent of this one",
GST_TYPE_VALIDATE_MONITOR,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_VERBOSITY,
g_param_spec_flags ("verbosity", "Verbosity",
"The verbosity of GstValidate on the monitor",
GST_TYPE_VALIDATE_VERBOSITY_FLAGS,
GST_VALIDATE_VERBOSITY_POSITION, G_PARAM_READWRITE));
}
static GObject *
gst_validate_monitor_constructor (GType type, guint n_construct_params,
GObjectConstructParam * construct_params)
{
GstObject *target;
GstValidateMonitor *monitor =
GST_VALIDATE_MONITOR_CAST (G_OBJECT_CLASS (parent_class)->constructor
(type,
n_construct_params,
construct_params));
if (monitor->parent) {
GstPipeline *parent_pipeline =
gst_validate_monitor_get_pipeline (monitor->parent);
gst_validate_monitor_set_media_descriptor (monitor,
monitor->parent->media_descriptor);
if (parent_pipeline) {
g_weak_ref_init (&monitor->pipeline, parent_pipeline);
gst_object_unref (parent_pipeline);
}
}
gst_validate_monitor_setup (monitor);
gst_validate_override_registry_attach_overrides (monitor);
target = gst_validate_monitor_get_target (monitor);
g_object_set_data ((GObject *) target, "validate-monitor", monitor);
gst_object_unref (target);
return (GObject *) monitor;
}
static void
gst_validate_monitor_init (GstValidateMonitor * monitor)
{
g_mutex_init (&monitor->mutex);
g_mutex_init (&monitor->overrides_mutex);
g_queue_init (&monitor->overrides);
monitor->verbosity = GST_VALIDATE_VERBOSITY_POSITION;
}
static gboolean
gst_validate_monitor_do_setup (GstValidateMonitor * monitor)
{
/* NOP */
return TRUE;
}
static GstValidateReportingDetails
_get_report_level_for_pad (GstValidateRunner * runner, GstObject * pad)
{
gchar *name;
GstValidateReportingDetails level = GST_VALIDATE_SHOW_UNKNOWN;
name = g_strdup_printf ("%s__%s", GST_DEBUG_PAD_NAME (pad));
level = gst_validate_runner_get_reporting_level_for_name (runner, name);
g_free (name);
return level;
}
static void
_determine_reporting_level (GstValidateMonitor * monitor)
{
GstValidateRunner *runner;
GstObject *object, *parent;
gchar *object_name;
GstValidateReportingDetails level = GST_VALIDATE_SHOW_UNKNOWN;
object = gst_validate_monitor_get_target (monitor);
runner = gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
do {
if (!GST_IS_OBJECT (object))
break;
/* Let's allow for singling out pads */
if (GST_IS_PAD (object)) {
level = _get_report_level_for_pad (runner, object);
if (level != GST_VALIDATE_SHOW_UNKNOWN)
break;
}
object_name = gst_object_get_name (object);
level = gst_validate_runner_get_reporting_level_for_name (runner,
object_name);
parent = gst_object_get_parent (object);
gst_object_unref (object);
object = parent;
g_free (object_name);
} while (object && level == GST_VALIDATE_SHOW_UNKNOWN);
if (object)
gst_object_unref (object);
if (runner)
gst_object_unref (runner);
monitor->level = level;
}
gboolean
gst_validate_monitor_setup (GstValidateMonitor * monitor)
{
GList *config;
GST_DEBUG_OBJECT (monitor, "Starting monitor setup");
for (config = gst_validate_plugin_get_config (NULL); config;
config = config->next) {
const gchar *verbosity =
gst_structure_get_string (GST_STRUCTURE (config->data),
"verbosity");
if (verbosity)
gst_util_set_object_arg (G_OBJECT (monitor), "verbosity", verbosity);
}
/* For now we just need to do this at setup time */
_determine_reporting_level (monitor);
return GST_VALIDATE_MONITOR_GET_CLASS (monitor)->setup (monitor);
}
/**
* gst_validate_monitor_get_element
* @monitor: The monitor
*
* Returns: (transfer none): The GstElement associated with @monitor
*/
GstElement *
gst_validate_monitor_get_element (GstValidateMonitor * monitor)
{
GstValidateMonitorClass *klass = GST_VALIDATE_MONITOR_GET_CLASS (monitor);
GstElement *element = NULL;
if (klass->get_element)
element = klass->get_element (monitor);
return element;
}
gchar *
gst_validate_monitor_get_element_name (GstValidateMonitor * monitor)
{
gchar *res = NULL;
GstElement *element;
element = gst_validate_monitor_get_element (monitor);
if (element) {
res = g_strdup (GST_ELEMENT_NAME (element));
gst_object_unref (element);
}
return res;
}
/* Check if any of our overrides wants to change the report severity */
static GstValidateInterceptionReturn
gst_validate_monitor_intercept_report (GstValidateReporter * reporter,
GstValidateReport * report)
{
GList *iter;
GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (reporter);
GST_VALIDATE_MONITOR_OVERRIDES_LOCK (monitor);
for (iter = monitor->overrides.head; iter; iter = g_list_next (iter)) {
report->level =
gst_validate_override_get_severity (iter->data,
gst_validate_issue_get_id (report->issue), report->level);
}
GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (monitor);
return GST_VALIDATE_REPORTER_REPORT;
}
void
gst_validate_monitor_attach_override (GstValidateMonitor * monitor,
GstValidateOverride * override)
{
GstValidateRunner *runner;
GstValidateRunner *mrunner;
if (!gst_validate_override_can_attach (override, monitor)) {
GST_INFO_OBJECT (monitor, "Can not attach override %s",
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (override)));
return;
}
runner = gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (override));
mrunner = gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
GST_VALIDATE_MONITOR_OVERRIDES_LOCK (monitor);
if (runner) {
g_assert (runner == mrunner);
} else
gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (override),
mrunner);
g_queue_push_tail (&monitor->overrides, override);
GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (monitor);
if (runner)
gst_object_unref (runner);
if (mrunner)
gst_object_unref (mrunner);
gst_validate_override_attached (override);
}
static void
gst_validate_monitor_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstValidateMonitor *monitor;
monitor = GST_VALIDATE_MONITOR_CAST (object);
switch (prop_id) {
case PROP_OBJECT:
{
GstObject *target;
target = g_value_get_object (value);
g_assert (gst_validate_monitor_get_target (monitor) == NULL);
g_weak_ref_init (&monitor->target, target);
if (GST_IS_OBJECT (target))
gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (monitor),
gst_object_get_name (target));
break;
}
case PROP_PIPELINE:
g_weak_ref_init (&monitor->pipeline, g_value_get_object (value));
break;
case PROP_RUNNER:
gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (monitor),
g_value_get_object (value));
break;
case PROP_VALIDATE_PARENT:
monitor->parent = g_value_get_object (value);
break;
case PROP_VERBOSITY:
monitor->verbosity = g_value_get_flags (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_monitor_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstValidateMonitor *monitor;
monitor = GST_VALIDATE_MONITOR_CAST (object);
switch (prop_id) {
case PROP_OBJECT:
g_value_take_object (value, gst_validate_monitor_get_target (monitor));
break;
case PROP_PIPELINE:
g_value_take_object (value, gst_validate_monitor_get_pipeline (monitor));
break;
case PROP_RUNNER:
g_value_take_object (value,
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
break;
case PROP_VALIDATE_PARENT:
g_value_set_object (value, GST_VALIDATE_MONITOR_GET_PARENT (monitor));
break;
case PROP_VERBOSITY:
g_value_set_flags (value, monitor->verbosity);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
void
gst_validate_monitor_set_media_descriptor (GstValidateMonitor * monitor,
GstValidateMediaDescriptor * media_descriptor)
{
GstValidateMonitorClass *klass = GST_VALIDATE_MONITOR_GET_CLASS (monitor);
GST_DEBUG_OBJECT (monitor, "Set media desc: %" GST_PTR_FORMAT,
media_descriptor);
if (monitor->media_descriptor)
gst_object_unref (monitor->media_descriptor);
if (media_descriptor)
gst_object_ref (media_descriptor);
monitor->media_descriptor = media_descriptor;
if (klass->set_media_descriptor)
klass->set_media_descriptor (monitor, media_descriptor);
}
GstValidateMonitor *
gst_validate_get_monitor (GObject * object)
{
return GST_VALIDATE_MONITOR (g_object_get_data (object, "validate-monitor"));
}
@@ -0,0 +1,142 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-monitor.h - Validate Monitor abstract base class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_MONITOR_H__
#define __GST_VALIDATE_MONITOR_H__
#include <glib-object.h>
#include <gst/gst.h>
typedef struct _GstValidateMonitor GstValidateMonitor;
typedef struct _GstValidateMonitorClass GstValidateMonitorClass;
#include <gst/validate/gst-validate-report.h>
#include <gst/validate/gst-validate-reporter.h>
#include <gst/validate/gst-validate-runner.h>
#include <gst/validate/gst-validate-override.h>
#include <gst/validate/media-descriptor-parser.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_MONITOR (gst_validate_monitor_get_type ())
#define GST_IS_VALIDATE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_MONITOR))
#define GST_IS_VALIDATE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_MONITOR))
#define GST_VALIDATE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_MONITOR, GstValidateMonitorClass))
#define GST_VALIDATE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_MONITOR, GstValidateMonitor))
#define GST_VALIDATE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_MONITOR, GstValidateMonitorClass))
#define GST_VALIDATE_MONITOR_CAST(obj) ((GstValidateMonitor*)(obj))
#define GST_VALIDATE_MONITOR_CLASS_CAST(klass) ((GstValidateMonitorClass*)(klass))
#endif
#define GST_VALIDATE_MONITOR_GET_PARENT(m) (GST_VALIDATE_MONITOR_CAST (m)->parent)
#define GST_VALIDATE_MONITOR_LOCK(m) \
G_STMT_START { \
GST_LOG_OBJECT (m, "About to lock %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \
(g_mutex_lock (&GST_VALIDATE_MONITOR_CAST(m)->mutex)); \
GST_LOG_OBJECT (m, "Acquired lock %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \
} G_STMT_END
#define GST_VALIDATE_MONITOR_UNLOCK(m) \
G_STMT_START { \
GST_LOG_OBJECT (m, "About to unlock %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \
(g_mutex_unlock (&GST_VALIDATE_MONITOR_CAST(m)->mutex)); \
GST_LOG_OBJECT (m, "unlocked %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \
} G_STMT_END
#define GST_VALIDATE_MONITOR_OVERRIDES_LOCK(m) g_mutex_lock (&GST_VALIDATE_MONITOR_CAST (m)->overrides_mutex)
#define GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK(m) g_mutex_unlock (&GST_VALIDATE_MONITOR_CAST (m)->overrides_mutex)
#define GST_VALIDATE_MONITOR_OVERRIDES(m) (GST_VALIDATE_MONITOR_CAST (m)->overrides)
/* #else TODO Implemen no variadic macros, use inline,
* Problem being:
* GST_VALIDATE_REPORT_LEVEL_ ## status
* GST_VALIDATE_AREA_ ## area ## _ ## subarea
*/
/**
* GstValidateMonitor:
*
* GStreamer Validate Monitor class.
*
* Class that wraps a #GObject for Validate checks
*/
struct _GstValidateMonitor {
GstObject object;
GWeakRef target;
GWeakRef pipeline;
GMutex mutex;
gchar *target_name;
GstValidateMonitor *parent;
GMutex overrides_mutex;
GQueue overrides;
GstValidateMediaDescriptor *media_descriptor;
GstValidateReportingDetails level;
/*< private >*/
GHashTable *reports;
GstValidateVerbosityFlags verbosity;
};
/**
* GstValidateMonitorClass:
* @parent_class: parent
*
* GStreamer Validate Monitor object class.
*/
struct _GstValidateMonitorClass {
GstObjectClass parent_class;
gboolean (* setup) (GstValidateMonitor * monitor);
GstElement *(* get_element) (GstValidateMonitor * monitor);
void (*set_media_descriptor) (GstValidateMonitor * monitor,
GstValidateMediaDescriptor * media_descriptor);
};
/* normal GObject stuff */
GST_VALIDATE_API
GType gst_validate_monitor_get_type (void);
GST_VALIDATE_API
void gst_validate_monitor_attach_override (GstValidateMonitor * monitor,
GstValidateOverride * override);
GST_VALIDATE_API
GstElement * gst_validate_monitor_get_element (GstValidateMonitor * monitor);
GST_VALIDATE_API
gchar * gst_validate_monitor_get_element_name (GstValidateMonitor * monitor);
GST_VALIDATE_API
void gst_validate_monitor_set_media_descriptor (GstValidateMonitor * monitor,
GstValidateMediaDescriptor *media_descriptor);
GST_VALIDATE_API
GstPipeline * gst_validate_monitor_get_pipeline (GstValidateMonitor * monitor);
GST_VALIDATE_API
GstObject * gst_validate_monitor_get_target (GstValidateMonitor * monitor);
G_END_DECLS
#endif /* __GST_VALIDATE_MONITOR_H__ */
@@ -0,0 +1,526 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-override-registry.c - Validate Override Registry
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <gmodule.h>
#include "gst-validate-report.h"
#include "gst-validate-reporter.h"
#include "gst-validate-utils.h"
#include "gst-validate-internal.h"
#include "gst-validate-monitor.h"
#include "gst-validate-override.h"
#include "gst-validate-override-registry.h"
typedef struct
{
gchar *name;
GstValidateOverride *override;
} GstValidateOverrideRegistryNameEntry;
typedef struct
{
GType gtype;
GstValidateOverride *override;
} GstValidateOverrideRegistryGTypeEntry;
static GMutex _gst_validate_override_registry_mutex;
static GstValidateOverrideRegistry *_registry_default = NULL;
#define GST_VALIDATE_OVERRIDE_REGISTRY_LOCK(r) g_mutex_lock (&r->mutex)
#define GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK(r) g_mutex_unlock (&r->mutex)
#define GST_VALIDATE_OVERRIDE_INIT_SYMBOL "gst_validate_create_overrides"
typedef int (*GstValidateCreateOverride) (void);
static void
gst_validate_override_registry_name_entry_free
(GstValidateOverrideRegistryNameEntry * entry)
{
g_free (entry->name);
g_object_unref (entry->override);
g_slice_free (GstValidateOverrideRegistryNameEntry, entry);
}
static void
gst_validate_override_registry_type_entry_free
(GstValidateOverrideRegistryGTypeEntry * entry)
{
g_object_unref (entry->override);
g_slice_free (GstValidateOverrideRegistryGTypeEntry, entry);
}
static GstValidateOverrideRegistry *
gst_validate_override_registry_new (void)
{
GstValidateOverrideRegistry *reg = g_slice_new0 (GstValidateOverrideRegistry);
g_mutex_init (&reg->mutex);
g_queue_init (&reg->name_overrides);
g_queue_init (&reg->gtype_overrides);
g_queue_init (&reg->klass_overrides);
return reg;
}
static void
gst_validate_overide_registery_free (GstValidateOverrideRegistry * reg)
{
g_queue_foreach (&reg->klass_overrides,
(GFunc) gst_validate_override_registry_name_entry_free, NULL);
g_queue_foreach (&reg->name_overrides,
(GFunc) gst_validate_override_registry_name_entry_free, NULL);
g_queue_foreach (&reg->gtype_overrides,
(GFunc) gst_validate_override_registry_type_entry_free, NULL);
g_queue_clear (&reg->name_overrides);
g_queue_clear (&reg->gtype_overrides);
g_queue_clear (&reg->klass_overrides);
g_mutex_clear (&reg->mutex);
g_slice_free (GstValidateOverrideRegistry, reg);
}
/**
* gst_validate_override_registry_get: (skip):
*/
GstValidateOverrideRegistry *
gst_validate_override_registry_get (void)
{
g_mutex_lock (&_gst_validate_override_registry_mutex);
if (G_UNLIKELY (!_registry_default)) {
_registry_default = gst_validate_override_registry_new ();
}
g_mutex_unlock (&_gst_validate_override_registry_mutex);
return _registry_default;
}
void
gst_validate_override_register_by_name (const gchar * name,
GstValidateOverride * override)
{
GstValidateOverrideRegistry *registry = gst_validate_override_registry_get ();
GstValidateOverrideRegistryNameEntry *entry =
g_slice_new (GstValidateOverrideRegistryNameEntry);
GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry);
entry->name = g_strdup (name);
entry->override = g_object_ref (override);
g_queue_push_tail (&registry->name_overrides, entry);
GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry);
}
void
gst_validate_override_register_by_type (GType gtype,
GstValidateOverride * override)
{
GstValidateOverrideRegistry *registry = gst_validate_override_registry_get ();
GstValidateOverrideRegistryGTypeEntry *entry =
g_slice_new (GstValidateOverrideRegistryGTypeEntry);
GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry);
entry->gtype = gtype;
entry->override = g_object_ref (override);
g_queue_push_tail (&registry->gtype_overrides, entry);
GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry);
}
void
gst_validate_override_register_by_klass (const gchar * klass,
GstValidateOverride * override)
{
GstValidateOverrideRegistry *registry = gst_validate_override_registry_get ();
GstValidateOverrideRegistryNameEntry *entry =
g_slice_new (GstValidateOverrideRegistryNameEntry);
GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry);
entry->name = g_strdup (klass);
entry->override = g_object_ref (override);
g_queue_push_tail (&registry->klass_overrides, entry);
GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry);
}
static void
gst_validate_override_registry_attach_name_overrides_unlocked
(GstValidateOverrideRegistry * registry, GstValidateMonitor * monitor)
{
GstValidateOverrideRegistryNameEntry *entry;
GList *iter;
const gchar *name;
name = gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor));
for (iter = registry->name_overrides.head; iter; iter = g_list_next (iter)) {
entry = iter->data;
if (g_regex_match_simple (entry->name, name, 0, 0)) {
GST_INFO ("%p Adding override %s to %s", registry, entry->name, name);
gst_validate_monitor_attach_override (monitor, entry->override);
}
}
}
static void
gst_validate_override_registry_attach_gtype_overrides_unlocked
(GstValidateOverrideRegistry * registry, GstValidateMonitor * monitor)
{
GstValidateOverrideRegistryGTypeEntry *entry;
GstElement *element;
GList *iter;
element = gst_validate_monitor_get_element (monitor);
if (!element)
return;
for (iter = registry->gtype_overrides.head; iter; iter = g_list_next (iter)) {
entry = iter->data;
if (G_TYPE_CHECK_INSTANCE_TYPE (element, entry->gtype)) {
gst_validate_monitor_attach_override (monitor, entry->override);
}
}
gst_object_unref (element);
}
static void
gst_validate_override_registry_attach_klass_overrides_unlocked
(GstValidateOverrideRegistry * registry, GstValidateMonitor * monitor)
{
GstValidateOverrideRegistryNameEntry *entry;
GList *iter;
GstElement *element;
element = gst_validate_monitor_get_element (monitor);
if (!element)
return;
for (iter = registry->klass_overrides.head; iter; iter = g_list_next (iter)) {
entry = iter->data;
if (gst_validate_element_has_klass (element, entry->name)) {
gst_validate_monitor_attach_override (monitor, entry->override);
}
}
gst_object_unref (element);
}
void
gst_validate_override_registry_attach_overrides (GstValidateMonitor * monitor)
{
GstValidateOverrideRegistry *reg = gst_validate_override_registry_get ();
GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (reg);
gst_validate_override_registry_attach_name_overrides_unlocked (reg, monitor);
gst_validate_override_registry_attach_gtype_overrides_unlocked (reg, monitor);
gst_validate_override_registry_attach_klass_overrides_unlocked (reg, monitor);
GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (reg);
}
enum
{
WRONG_FILE,
WRONG_OVERRIDES,
OK
};
static gboolean
_add_override_from_struct (GstStructure * soverride)
{
GQuark issue_id;
GstValidateReportLevel level;
GstValidateOverride *override;
const gchar *str_issue_id, *str_new_severity,
*factory_name = NULL, *name = NULL, *klass = NULL;
gboolean registered = FALSE;
if (!gst_structure_has_name (soverride, "change-severity")
&& !gst_structure_has_name (soverride, "change-issue-severity")) {
gst_validate_abort
("Currently only 'change-severity' overrides are supported");
return FALSE;
}
str_issue_id = gst_structure_get_string (soverride, "issue-id");
if (!str_issue_id) {
gst_validate_abort ("No issue id provided in override: %" GST_PTR_FORMAT,
soverride);
return FALSE;
}
issue_id = g_quark_from_string (str_issue_id);
if (gst_validate_issue_from_id (issue_id) == NULL) {
gst_validate_abort ("No GstValidateIssue registered for %s", str_issue_id);
return FALSE;
}
str_new_severity = gst_structure_get_string (soverride, "new-severity");
if (str_new_severity == NULL) {
gst_validate_abort ("No 'new-severity' field found in %" GST_PTR_FORMAT,
soverride);
return FALSE;
}
level = gst_validate_report_level_from_name (str_new_severity);
if (level == GST_VALIDATE_REPORT_LEVEL_UNKNOWN) {
gst_validate_abort ("Unknown level name %s", str_new_severity);
return FALSE;
}
gst_validate_printf (NULL, "**-> Changing issue '%s' severity to: '%s'\n",
str_issue_id, str_new_severity);
override = gst_validate_override_new ();
gst_validate_override_change_severity (override, issue_id, level);
name = gst_structure_get_string (soverride, "element-name");
klass = gst_structure_get_string (soverride, "element-classification");
factory_name = gst_structure_get_string (soverride, "element-factory-name");
if (factory_name) {
GType type;
GstElement *element = gst_element_factory_make (factory_name, NULL);
if (element == NULL) {
GST_ERROR ("Unknown element factory name: %s (gst is %sinitialized)",
factory_name, gst_is_initialized ()? "" : "NOT ");
if (!name && !klass)
return FALSE;
} else {
type = G_OBJECT_TYPE (element);
gst_validate_override_register_by_type (type, override);
gst_object_unref (element);
}
registered = TRUE;
}
if (name) {
gst_validate_override_register_by_name (name, override);
registered = TRUE;
}
if (klass) {
gst_validate_override_register_by_klass (klass, override);
registered = TRUE;
}
if (!registered) {
GstValidateIssue *issue = gst_validate_issue_from_id (issue_id);
if (!issue) {
g_object_unref (override);
return FALSE;
}
gst_validate_issue_set_default_level (issue, level);
}
g_object_unref (override);
return TRUE;
}
static gboolean
_load_text_override_file (const gchar * filename)
{
gint ret = OK;
GList *structs =
gst_validate_utils_structs_parse_from_filename (filename, NULL, NULL);
if (structs) {
GList *tmp;
for (tmp = structs; tmp; tmp = tmp->next) {
GstStructure *_struct = (GstStructure *) tmp->data;
if (!_add_override_from_struct (_struct)) {
GST_ERROR ("Wrong overrides %" GST_PTR_FORMAT, _struct);
ret = WRONG_OVERRIDES;
}
}
goto done;
}
ret = WRONG_FILE;
done:
g_list_free_full (structs, (GDestroyNotify) gst_structure_free);
return ret;
}
int
gst_validate_override_registry_preload (void)
{
gchar **modlist, *const *modname;
const char *sos, *loaderr;
GModule *module;
int ret, nloaded = 0;
gpointer ext_create_overrides;
GList *tmp, *overrides = gst_validate_get_config ("change-issue-severity");
for (tmp = overrides; tmp; tmp = tmp->next)
_add_override_from_struct (tmp->data);
g_list_free (overrides);
sos = g_getenv ("GST_VALIDATE_OVERRIDE");
if (!sos) {
GST_INFO ("No GST_VALIDATE_OVERRIDE found, no overrides to load");
return 0;
}
modlist = g_strsplit (sos, G_SEARCHPATH_SEPARATOR_S, 0);
for (modname = modlist; *modname; ++modname) {
GST_INFO ("Loading overrides from %s", *modname);
module = g_module_open (*modname, G_MODULE_BIND_LAZY);
if (module == NULL) {
if (_load_text_override_file (*modname) == WRONG_FILE) {
loaderr = g_module_error ();
GST_ERROR ("Failed to load %s %s", *modname,
loaderr ? loaderr : "no idea why");
}
continue;
}
if (g_module_symbol (module, GST_VALIDATE_OVERRIDE_INIT_SYMBOL,
&ext_create_overrides)) {
ret = ((GstValidateCreateOverride) ext_create_overrides) ();
if (ret > 0) {
GST_INFO ("Loaded %d overrides from %s", ret, *modname);
nloaded += ret;
} else if (ret < 0) {
GST_WARNING ("Error loading overrides from %s", *modname);
} else {
GST_INFO ("Loaded no overrides from %s", *modname);
}
} else {
GST_WARNING (GST_VALIDATE_OVERRIDE_INIT_SYMBOL " not found in %s",
*modname);
}
g_module_close (module);
}
g_strfreev (modlist);
GST_INFO ("%d overrides loaded", nloaded);
return nloaded;
}
/**
* gst_validate_override_registry_get_override_for_names: (skip):
*/
GList *gst_validate_override_registry_get_override_for_names
(GstValidateOverrideRegistry * reg, const gchar * name, ...)
{
GList *iter;
GList *ret = NULL;
if (name) {
va_list varargs;
GstValidateOverrideRegistryNameEntry *entry;
va_start (varargs, name);
GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (reg);
while (name) {
for (iter = reg->name_overrides.head; iter; iter = g_list_next (iter)) {
entry = iter->data;
if ((g_strcmp0 (name, entry->name)) == 0) {
ret = g_list_prepend (ret, entry->override);
}
}
name = va_arg (varargs, gchar *);
}
GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (reg);
va_end (varargs);
}
return ret;
}
/**
* gst_validate_override_registry_get_override_list:
* @registry: the override registry
*
* Returns: (transfer full) (element-type GstValidateOverride): a list of #GstValidateOverride
*/
GList *
gst_validate_override_registry_get_override_list (GstValidateOverrideRegistry *
registry)
{
GList *all_overrides = NULL;
GList *i;
GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry);
for (i = registry->name_overrides.head; i; i = i->next) {
GstValidateOverrideRegistryNameEntry *entry =
(GstValidateOverrideRegistryNameEntry *) i->data;
if (!g_list_find (all_overrides, entry->override))
all_overrides = g_list_append (all_overrides, entry->override);
}
for (i = registry->klass_overrides.head; i; i = i->next) {
GstValidateOverrideRegistryNameEntry *entry =
(GstValidateOverrideRegistryNameEntry *) i->data;
if (!g_list_find (all_overrides, entry->override))
all_overrides = g_list_append (all_overrides, entry->override);
}
for (i = registry->name_overrides.head; i; i = i->next) {
GstValidateOverrideRegistryGTypeEntry *entry =
(GstValidateOverrideRegistryGTypeEntry *) i->data;
if (!g_list_find (all_overrides, entry->override))
all_overrides = g_list_append (all_overrides, entry->override);
}
GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry);
return all_overrides;
}
void
_priv_validate_override_registry_deinit (void)
{
GstValidateOverrideRegistry *reg = NULL;
g_mutex_lock (&_gst_validate_override_registry_mutex);
if (G_UNLIKELY (_registry_default)) {
reg = _registry_default;
_registry_default = NULL;
}
g_mutex_unlock (&_gst_validate_override_registry_mutex);
if (reg)
gst_validate_overide_registery_free (reg);
}
@@ -0,0 +1,66 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-override-registry.h - Validate Override registry
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_OVERRIDE_REGISTRY_H__
#define __GST_VALIDATE_OVERRIDE_REGISTRY_H__
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/validate/gst-validate-report.h>
#include <gst/validate/gst-validate-monitor.h>
#include <gst/validate/gst-validate-override.h>
G_BEGIN_DECLS
typedef struct {
GMutex mutex;
GQueue name_overrides;
GQueue gtype_overrides;
GQueue klass_overrides;
} GstValidateOverrideRegistry;
GST_VALIDATE_API
GstValidateOverrideRegistry * gst_validate_override_registry_get (void);
GST_VALIDATE_API GList *
gst_validate_override_registry_get_override_for_names (GstValidateOverrideRegistry *reg,
const gchar *name, ...);
GST_VALIDATE_API GList *
gst_validate_override_registry_get_override_list (GstValidateOverrideRegistry *registry);
GST_VALIDATE_API
void gst_validate_override_register_by_name (const gchar * name, GstValidateOverride * override);
GST_VALIDATE_API
void gst_validate_override_register_by_type (GType gtype, GstValidateOverride * override);
GST_VALIDATE_API
void gst_validate_override_register_by_klass (const gchar * klass, GstValidateOverride * override);
GST_VALIDATE_API
void gst_validate_override_registry_attach_overrides (GstValidateMonitor * monitor);
GST_VALIDATE_API
int gst_validate_override_registry_preload (void);
G_END_DECLS
#endif /* __GST_VALIDATE_OVERRIDE_REGISTRY_H__ */
@@ -0,0 +1,332 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
* Copyright (C) 2015 Raspberry Pi Foundation
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* gst-validate-override.c - Validate Override that allows customizing Validate behavior
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION: gst-validate-override
* @title: GstValidateOverride
* @short_description: TODO
*
* TODO
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "gst-validate-internal.h"
#include "gst-validate-override.h"
/* *INDENT-OFF* */
struct _GstValidateOverridePrivate
{
GHashTable *level_override;
};
enum
{
PROP_FIRST_PROP = 1,
PROP_RUNNER,
PROP_LAST
};
G_DEFINE_TYPE_WITH_CODE (GstValidateOverride, gst_validate_override,
GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateOverride)
G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL))
/* *INDENT-ON* */
static void
_get_property (GObject * object,
guint property_id, GValue * value, GParamSpec * pspec)
{
switch (property_id) {
case PROP_RUNNER:
g_value_take_object (value,
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
break;
default:
break;
}
}
static void
_set_property (GObject * object,
guint property_id, const GValue * value, GParamSpec * pspec)
{
switch (property_id) {
case PROP_RUNNER:
/* we assume the runner is valid as long as this scenario is,
* no ref taken */
gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
g_value_get_object (value));
break;
default:
break;
}
}
static void
gst_validate_override_finalize (GObject * object)
{
GstValidateOverride *self = GST_VALIDATE_OVERRIDE (object);
void (*chain_up) (GObject *) =
((GObjectClass *) gst_validate_override_parent_class)->finalize;
g_hash_table_unref (self->priv->level_override);
chain_up (object);
}
static void
gst_validate_override_class_init (GstValidateOverrideClass * klass)
{
GObjectClass *oclass = G_OBJECT_CLASS (klass);
oclass->finalize = gst_validate_override_finalize;
oclass->get_property = _get_property;
oclass->set_property = _set_property;
g_object_class_install_property (oclass, PROP_RUNNER,
g_param_spec_object ("validate-runner", "VALIDATE Runner",
"The Validate runner to report errors to",
GST_TYPE_VALIDATE_RUNNER,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
}
static void
gst_validate_override_init (GstValidateOverride * self)
{
self->priv = gst_validate_override_get_instance_private (self);
self->priv->level_override = g_hash_table_new (g_direct_hash, g_direct_equal);
}
GstValidateOverride *
gst_validate_override_new (void)
{
return g_object_new (GST_TYPE_VALIDATE_OVERRIDE, NULL);
}
void
gst_validate_override_change_severity (GstValidateOverride * override,
GstValidateIssueId issue_id, GstValidateReportLevel new_level)
{
g_hash_table_insert (override->priv->level_override, (gpointer) issue_id,
(gpointer) new_level);
}
/*
* Also receives @default_level to preserve a custom level that might have
* been set by a previous GstValidateOverride and should not go back to the
* GstValidateIssue default
*/
GstValidateReportLevel
gst_validate_override_get_severity (GstValidateOverride * override,
GstValidateIssueId issue_id, GstValidateReportLevel default_level)
{
GstValidateReportLevel *level = NULL;
if (g_hash_table_lookup_extended (override->priv->level_override,
(gpointer) issue_id, NULL, (gpointer) & level)) {
return GPOINTER_TO_INT (level);
}
return default_level;
}
/**
* gst_validate_override_set_event_handler: (skip):
*/
void
gst_validate_override_set_event_handler (GstValidateOverride * override,
GstValidateOverrideEventHandler handler)
{
override->event_handler = handler;
}
/**
* gst_validate_override_set_buffer_handler : (skip):
*/
void
gst_validate_override_set_buffer_handler (GstValidateOverride * override,
GstValidateOverrideBufferHandler handler)
{
override->buffer_handler = handler;
}
/**
* gst_validate_override_set_query_handler: (skip):
*/
void
gst_validate_override_set_query_handler (GstValidateOverride * override,
GstValidateOverrideQueryHandler handler)
{
override->query_handler = handler;
}
/**
* gst_validate_override_set_buffer_probe_handler: (skip):
*/
void
gst_validate_override_set_buffer_probe_handler (GstValidateOverride * override,
GstValidateOverrideBufferHandler handler)
{
override->buffer_probe_handler = handler;
}
/**
* gst_validate_override_set_getcaps_handler: (skip):
*/
void
gst_validate_override_set_getcaps_handler (GstValidateOverride * override,
GstValidateOverrideGetCapsHandler handler)
{
override->getcaps_handler = handler;
}
/**
* gst_validate_override_set_setcaps_handler: (skip):
*/
void
gst_validate_override_set_setcaps_handler (GstValidateOverride * override,
GstValidateOverrideSetCapsHandler handler)
{
override->setcaps_handler = handler;
}
/**
* gst_validate_override_event_handler: (skip):
*/
void
gst_validate_override_event_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstEvent * event)
{
if (override->event_handler)
override->event_handler (override, monitor, event);
}
/**
* gst_validate_override_buffer_handler: (skip):
*/
void
gst_validate_override_buffer_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstBuffer * buffer)
{
if (override->buffer_handler)
override->buffer_handler (override, monitor, buffer);
}
/**
* gst_validate_override_query_handler: (skip):
*/
void
gst_validate_override_query_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstQuery * query)
{
if (override->query_handler)
override->query_handler (override, monitor, query);
}
/**
* gst_validate_override_buffer_probe_handler: (skip):
*/
void
gst_validate_override_buffer_probe_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstBuffer * buffer)
{
if (override->buffer_probe_handler)
override->buffer_probe_handler (override, monitor, buffer);
}
/**
* gst_validate_override_getcaps_handler: (skip):
*/
void
gst_validate_override_getcaps_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstCaps * caps)
{
if (override->getcaps_handler)
override->getcaps_handler (override, monitor, caps);
}
/**
* gst_validate_override_setcaps_handler: (skip):
*/
void
gst_validate_override_setcaps_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstCaps * caps)
{
if (override->setcaps_handler)
override->setcaps_handler (override, monitor, caps);
}
/**
* gst_validate_override_element_added_handler: (skip):
*/
void
gst_validate_override_element_added_handler (GstValidateOverride * override,
GstValidateMonitor * monitor, GstElement * child)
{
if (override->element_added_handler)
override->element_added_handler (override, monitor, child);
}
/**
* gst_validate_override_set_element_added_handler: (skip):
*/
void
gst_validate_override_set_element_added_handler (GstValidateOverride * override,
GstValidateOverrideElementAddedHandler func)
{
override->element_added_handler = func;
}
/**
* gst_validate_override_can_attach: (skip):
*/
gboolean
gst_validate_override_can_attach (GstValidateOverride * override,
GstValidateMonitor * monitor)
{
GstValidateOverrideClass *klass = GST_VALIDATE_OVERRIDE_GET_CLASS (override);
if (klass->can_attach)
return klass->can_attach (override, monitor);
return TRUE;
}
void
gst_validate_override_attached (GstValidateOverride * override)
{
GstValidateOverrideClass *klass = GST_VALIDATE_OVERRIDE_GET_CLASS (override);
if (klass->attached)
klass->attached (override);
}
@@ -0,0 +1,141 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-override.h - Validate Override that allows customizing Validate behavior
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_OVERRIDE_H__
#define __GST_VALIDATE_OVERRIDE_H__
#include <glib-object.h>
#include <gst/gst.h>
typedef struct _GstValidateOverride GstValidateOverride;
typedef struct _GstValidateOverrideClass GstValidateOverrideClass;
typedef struct _GstValidateOverridePrivate GstValidateOverridePrivate;
#include <gst/validate/gst-validate-report.h>
#include <gst/validate/gst-validate-monitor.h>
G_BEGIN_DECLS
typedef void (*GstValidateOverrideBufferHandler)(GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstBuffer * buffer);
typedef void (*GstValidateOverrideEventHandler)(GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstEvent * event);
typedef void (*GstValidateOverrideQueryHandler)(GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstQuery * query);
typedef void (*GstValidateOverrideGetCapsHandler)(GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstCaps * caps);
typedef void (*GstValidateOverrideSetCapsHandler)(GstValidateOverride * override,
GstValidateMonitor * pad_monitor, GstCaps * caps);
typedef void (*GstValidateOverrideElementAddedHandler)(GstValidateOverride * override,
GstValidateMonitor * bin_monitor, GstElement * new_child);
struct _GstValidateOverrideClass
{
/*<private>*/
GstObjectClass parent_class;
gboolean (*can_attach)(GstValidateOverride * override,
GstValidateMonitor * monitor);
void (*attached)(GstValidateOverride * override);
};
struct _GstValidateOverride
{
GstObject parent;
GstValidateOverrideBufferHandler buffer_handler;
GstValidateOverrideEventHandler event_handler;
GstValidateOverrideQueryHandler query_handler;
GstValidateOverrideBufferHandler buffer_probe_handler;
GstValidateOverrideGetCapsHandler getcaps_handler;
GstValidateOverrideSetCapsHandler setcaps_handler;
GstValidateOverrideElementAddedHandler element_added_handler;
/*<private>*/
GstValidateOverridePrivate *priv;
};
GST_VALIDATE_API
GType gst_validate_override_get_type (void) G_GNUC_CONST;
/* TYPE MACROS */
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_OVERRIDE (gst_validate_override_get_type ())
#define GST_VALIDATE_OVERRIDE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverride))
#define GST_VALIDATE_OVERRIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverrideClass))
#define GST_IS_VALIDATE_OVERRIDE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VALIDATE_OVERRIDE))
#define GST_IS_VALIDATE_OVERRIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VALIDATE_OVERRIDE))
#define GST_VALIDATE_OVERRIDE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverrideClass))
#endif
GST_VALIDATE_API
GstValidateOverride * gst_validate_override_new (void);
void gst_validate_override_free (GstValidateOverride * override);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstValidateOverride, gst_validate_override_free)
GST_VALIDATE_API
void gst_validate_override_change_severity (GstValidateOverride * override, GstValidateIssueId issue_id, GstValidateReportLevel new_level);
GST_VALIDATE_API
GstValidateReportLevel gst_validate_override_get_severity (GstValidateOverride * override, GstValidateIssueId issue_id, GstValidateReportLevel default_level);
GST_VALIDATE_API
void gst_validate_override_event_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstEvent * event);
GST_VALIDATE_API
void gst_validate_override_buffer_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstBuffer * buffer);
GST_VALIDATE_API
void gst_validate_override_query_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstQuery * query);
GST_VALIDATE_API
void gst_validate_override_buffer_probe_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstBuffer * buffer);
GST_VALIDATE_API
void gst_validate_override_getcaps_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstCaps * caps);
GST_VALIDATE_API
void gst_validate_override_setcaps_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstCaps * caps);
GST_VALIDATE_API
void gst_validate_override_set_event_handler (GstValidateOverride * override, GstValidateOverrideEventHandler handler);
GST_VALIDATE_API
void gst_validate_override_set_buffer_handler (GstValidateOverride * override, GstValidateOverrideBufferHandler handler);
GST_VALIDATE_API
void gst_validate_override_set_query_handler (GstValidateOverride * override, GstValidateOverrideQueryHandler handler);
GST_VALIDATE_API
void gst_validate_override_set_buffer_probe_handler (GstValidateOverride * override, GstValidateOverrideBufferHandler handler);
GST_VALIDATE_API
void gst_validate_override_set_getcaps_handler (GstValidateOverride * override, GstValidateOverrideGetCapsHandler handler);
GST_VALIDATE_API
void gst_validate_override_set_setcaps_handler (GstValidateOverride * override, GstValidateOverrideSetCapsHandler handler);
GST_VALIDATE_API
void gst_validate_override_element_added_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstElement * child);
GST_VALIDATE_API
void gst_validate_override_set_element_added_handler (GstValidateOverride * override, GstValidateOverrideElementAddedHandler func);
GST_VALIDATE_API
gboolean gst_validate_override_can_attach (GstValidateOverride * override, GstValidateMonitor *monitor);
GST_VALIDATE_API
void gst_validate_override_attached (GstValidateOverride * override);
G_END_DECLS
#endif /* #ifndef __GST_VALIDATE_OVERRIDE_H__*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,156 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-pad-monitor.h - Validate PadMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_PAD_MONITOR_H__
#define __GST_VALIDATE_PAD_MONITOR_H__
#include <glib-object.h>
#include <gst/gst.h>
typedef struct _GstValidatePadMonitor GstValidatePadMonitor;
typedef struct _GstValidatePadMonitorClass GstValidatePadMonitorClass;
typedef struct _GstValidatePadSeekData GstValidatePadSeekData;
#include <gst/validate/gst-validate-monitor.h>
#include <gst/validate/media-descriptor-parser.h>
#include <gst/validate/gst-validate-element-monitor.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_PAD_MONITOR (gst_validate_pad_monitor_get_type ())
#define GST_IS_VALIDATE_PAD_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_PAD_MONITOR))
#define GST_IS_VALIDATE_PAD_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_PAD_MONITOR))
#define GST_VALIDATE_PAD_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_PAD_MONITOR, GstValidatePadMonitorClass))
#define GST_VALIDATE_PAD_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_PAD_MONITOR, GstValidatePadMonitor))
#define GST_VALIDATE_PAD_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_PAD_MONITOR, GstValidatePadMonitorClass))
#define GST_VALIDATE_PAD_MONITOR_CAST(obj) ((GstValidatePadMonitor*)(obj))
#define GST_VALIDATE_PAD_MONITOR_CLASS_CAST(klass) ((GstValidatePadMonitorClass*)(klass))
#endif
/**
* GstValidatePadMonitor:
*
* GStreamer Validate PadMonitor class.
*
* Class that wraps a #GstPad for Validate checks
*/
struct _GstValidatePadMonitor {
GstValidateMonitor parent;
gboolean setup;
GstPadChainFunction chain_func;
GstPadEventFunction event_func;
GstPadEventFullFunction event_full_func;
GstPadQueryFunction query_func;
GstPadActivateModeFunction activatemode_func;
GstPadGetRangeFunction get_range_func;
gulong pad_probe_id;
/*< private >*/
/* Last caps pushed/received */
GstCaps *last_caps;
gboolean caps_is_audio;
gboolean caps_is_video;
gboolean caps_is_raw;
/* FIXME : Let's migrate all those booleans into a 32 (or 64) bit flag */
gboolean first_buffer;
gboolean has_segment;
gboolean is_eos;
gboolean pending_flush_stop;
guint32 pending_newsegment_seqnum;
guint32 pending_eos_seqnum;
/* List of GstValidatePadSeekData containing pending/current seeks */
GList *seeks;
GstValidatePadSeekData *current_seek;
/* Whether the next buffer should have a DISCONT flag on it, because
* it's the first one, or follows a SEGMENT and/or a FLUSH */
gboolean pending_buffer_discont;
GstEvent *expected_segment;
GPtrArray *serialized_events;
GList *expired_events;
GstStructure *pending_setcaps_fields;
GstCaps * last_refused_caps;
GstCaps * last_query_filter;
GstCaps * last_query_res;
/* tracked data */
GstSegment segment;
GstClockTime current_timestamp;
GstClockTime current_duration;
/* Stores the timestamp range of data that has flown through
* this pad by using TIMESTAMP and TIMESTAMP+DURATION from
* incomming buffers. Every time a buffer is pushed, this range
* is extended.
*
* When a buffer is pushed, the timestamp range is checked against
* the outgoing timestamp to check it is in the received boundaries.
*/
GstClockTime timestamp_range_start;
GstClockTime timestamp_range_end;
/* GstValidateMediaCheck related fields */
GList *all_bufs;
/* The GstBuffer that should arrive next in a GList */
GList *current_buf;
gboolean check_buffers;
/* 'min-buffer-frequency' config check */
gdouble min_buf_freq;
gint buffers_pushed;
gint last_buffers_pushed;
GstClockTime min_buf_freq_interval_ts;
GstClockTime min_buf_freq_first_buffer_ts;
GstClockTime min_buf_freq_start;
};
/**
* GstValidatePadMonitorClass:
* @parent_class: parent
*
* GStreamer Validate PadMonitor object class.
*/
struct _GstValidatePadMonitorClass {
GstValidateMonitorClass parent_class;
};
/* normal GObject stuff */
GST_VALIDATE_API
GType gst_validate_pad_monitor_get_type (void);
GST_VALIDATE_API
GstValidatePadMonitor * gst_validate_pad_monitor_new (GstPad * pad, GstValidateRunner * runner, GstValidateElementMonitor *element_monitor);
G_END_DECLS
#endif /* __GST_VALIDATE_PAD_MONITOR_H__ */
@@ -0,0 +1,869 @@
/* GStreamer
*
* Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
*
* gst-validate-pipeline-monitor.c - Validate PipelineMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gst-validate-internal.h"
#include "gst-validate-pipeline-monitor.h"
#include "gst-validate-pad-monitor.h"
#include "gst-validate-monitor-factory.h"
#include "gst-validate-report.h"
#include "gst-validate-utils.h"
#include "validate.h"
#define PRINT_POSITION_TIMEOUT 250
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
enum
{
PROP_0,
PROP_VERBOSITY,
};
/**
* SECTION:gst-validate-pipeline-monitor
* @title: GstValidatePipelineMonitor
* @short_description: Class that wraps a #GstPipeline for Validate checks
*
* TODO
*/
typedef struct
{
gint caps_struct_num;
gint filter_caps_struct_num;
GString *str;
GstStructure *filter;
gboolean found;
} StructureIncompatibleFieldsInfo;
enum
{
PROP_LAST
};
#define gst_validate_pipeline_monitor_parent_class parent_class
G_DEFINE_TYPE (GstValidatePipelineMonitor, gst_validate_pipeline_monitor,
GST_TYPE_VALIDATE_BIN_MONITOR);
static void
gst_validate_pipeline_monitor_dispose (GObject * object)
{
GstValidatePipelineMonitor *self = (GstValidatePipelineMonitor *) object;
g_clear_object (&self->stream_collection);
if (self->streams_selected) {
g_list_free_full (self->streams_selected, gst_object_unref);
self->streams_selected = NULL;
}
G_OBJECT_CLASS (gst_validate_pipeline_monitor_parent_class)->dispose (object);
}
static void
gst_validate_pipeline_monitor_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstElement *pipeline = NULL;
GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (object);
GstValidatePipelineMonitor *self = GST_VALIDATE_PIPELINE_MONITOR (object);
switch (prop_id) {
case PROP_VERBOSITY:
pipeline = GST_ELEMENT (gst_validate_monitor_get_pipeline (monitor));
monitor->verbosity = g_value_get_flags (value);
if (monitor->verbosity & GST_VALIDATE_VERBOSITY_PROPS_CHANGES) {
if (pipeline && !self->deep_notify_id) {
self->deep_notify_id =
gst_element_add_property_deep_notify_watch (pipeline, NULL, TRUE);
}
} else if (pipeline && self->deep_notify_id) {
gst_element_remove_property_notify_watch (pipeline,
self->deep_notify_id);
self->deep_notify_id = 0;
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
gst_clear_object (&pipeline);
}
static void
gst_validate_pipeline_monitor_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (object);
switch (prop_id) {
case PROP_VERBOSITY:
g_value_set_flags (value, monitor->verbosity);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_pipeline_monitor_class_init (GstValidatePipelineMonitorClass *
klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gst_validate_pipeline_monitor_dispose;
object_class->set_property = gst_validate_pipeline_monitor_set_property;
object_class->get_property = gst_validate_pipeline_monitor_get_property;
g_object_class_override_property (object_class, PROP_VERBOSITY, "verbosity");
}
static void
gst_validate_pipeline_monitor_init (GstValidatePipelineMonitor *
pipeline_monitor)
{
}
static gboolean
print_position (GstValidateMonitor * monitor)
{
GstQuery *query;
gint64 position, duration;
GstElement *pipeline =
GST_ELEMENT (gst_validate_monitor_get_pipeline (monitor));
gdouble rate = 1.0;
GstFormat format = GST_FORMAT_TIME;
if (!(GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
GST_VALIDATE_VERBOSITY_POSITION))
goto done;
if (!gst_element_query_position (pipeline, format, &position)) {
GST_DEBUG_OBJECT (monitor, "Could not query position");
goto done;
}
format = GST_FORMAT_TIME;
if (!gst_element_query_duration (pipeline, format, &duration)) {
GST_DEBUG_OBJECT (monitor, "Could not query duration");
goto done;
}
if (GST_CLOCK_TIME_IS_VALID (duration) && GST_CLOCK_TIME_IS_VALID (position)
&& position > duration) {
GST_VALIDATE_REPORT (monitor, QUERY_POSITION_SUPERIOR_DURATION,
"Reported position %" GST_TIME_FORMAT " > reported duration %"
GST_TIME_FORMAT, GST_TIME_ARGS (position), GST_TIME_ARGS (duration));
}
query = gst_query_new_segment (GST_FORMAT_DEFAULT);
if (gst_element_query (pipeline, query))
gst_query_parse_segment (query, &rate, NULL, NULL, NULL);
gst_query_unref (query);
gst_validate_print_position (position, duration, rate, NULL);
done:
gst_object_unref (pipeline);
return TRUE;
}
static void
_check_pad_query_failures (GstPad * pad, GString * str,
GstValidatePadMonitor ** last_query_caps_fail_monitor,
GstValidatePadMonitor ** last_refused_caps_monitor)
{
GstValidatePadMonitor *monitor;
monitor = g_object_get_data (G_OBJECT (pad), "validate-monitor");
if (!monitor) {
GST_DEBUG_OBJECT (pad, "Has no monitor");
return;
}
if (monitor->last_query_res && gst_caps_is_empty (monitor->last_query_res)) {
gst_object_replace ((GstObject **) last_query_caps_fail_monitor,
(GstObject *) monitor);
}
if (monitor->last_refused_caps)
gst_object_replace ((GstObject **) last_refused_caps_monitor,
(GstObject *) monitor);
}
static GstPad *
_get_peer_pad (GstPad * pad)
{
GstPad *peer = gst_pad_get_peer (pad);
if (!peer)
return NULL;
while (GST_IS_PROXY_PAD (peer)) {
GstPad *next_pad;
if (GST_PAD_IS_SINK (peer)) {
if (GST_IS_GHOST_PAD (peer))
next_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer));
else
next_pad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (peer)));
} else {
next_pad = gst_pad_get_peer (peer);
}
gst_object_unref (peer);
if (!next_pad)
return NULL;
peer = next_pad;
}
return peer;
}
static void
_gather_pad_negotiation_details (GstPad * pad, GString * str,
GstValidatePadMonitor ** last_query_caps_fail_monitor,
GstValidatePadMonitor ** last_refused_caps_monitor)
{
GList *tmp;
GstElement *next;
GstPad *peer = _get_peer_pad (pad);
_check_pad_query_failures (pad, str, last_query_caps_fail_monitor,
last_refused_caps_monitor);
if (!peer)
return;
_check_pad_query_failures (peer, str, last_query_caps_fail_monitor,
last_refused_caps_monitor);
next = GST_ELEMENT (gst_pad_get_parent (peer));
GST_OBJECT_LOCK (next);
for (tmp = next->srcpads; tmp; tmp = tmp->next) {
GstPad *to_check = (GstPad *) tmp->data;
_gather_pad_negotiation_details (to_check, str,
last_query_caps_fail_monitor, last_refused_caps_monitor);
}
GST_OBJECT_UNLOCK (next);
gst_object_unref (peer);
gst_object_unref (next);
}
static void
_incompatible_fields_info_set_found (StructureIncompatibleFieldsInfo * info)
{
if (info->found == FALSE) {
g_string_append_printf (info->str, " for the following possible reasons:");
info->found = TRUE;
}
}
static gboolean
_find_structure_incompatible_fields (GQuark field_id, const GValue * value,
StructureIncompatibleFieldsInfo * info)
{
gchar *value_str, *filter_str;
GValue intersect = { 0, };
const GValue *filter_value = gst_structure_id_get_value (info->filter,
field_id);
if (!filter_value)
return TRUE;
value_str = gst_value_serialize (value);
filter_str = gst_value_serialize (filter_value);
if (!gst_value_can_intersect (value, filter_value)) {
_incompatible_fields_info_set_found (info);
g_string_append_printf (info->str,
"\n -> Field '%s' downstream value from structure %d '(%s)%s' can't intersect with"
" filter value from structure number %d '(%s)%s' because of their types.",
g_quark_to_string (field_id), info->caps_struct_num,
G_VALUE_TYPE_NAME (value), value_str, info->filter_caps_struct_num,
G_VALUE_TYPE_NAME (filter_value), filter_str);
return TRUE;
}
if (gst_value_intersect (&intersect, value, filter_value)) {
g_value_reset (&intersect);
g_free (value_str);
g_free (filter_str);
return TRUE;
}
_incompatible_fields_info_set_found (info);
g_string_append_printf (info->str,
"\n -> Field '%s' downstream value from structure %d '(%s)%s' can't intersect with"
" filter value from structure number %d '(%s)%s'",
g_quark_to_string (field_id), info->caps_struct_num,
G_VALUE_TYPE_NAME (value), value_str, info->filter_caps_struct_num,
G_VALUE_TYPE_NAME (filter_value), filter_str);
g_free (value_str);
g_free (filter_str);
return TRUE;
}
static void
_append_query_caps_failure_details (GstValidatePadMonitor * monitor,
GString * str)
{
gint i, j;
gboolean found = FALSE, empty_filter;
GstCaps *filter = gst_caps_copy (monitor->last_query_filter);
const gchar *filter_name, *possible_name;
GstStructure *filter_struct, *possible_struct;
GstPad *pad =
GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
(monitor)));
GstCaps *possible_caps = gst_pad_query_caps (pad, NULL);
g_string_append_printf (str,
"\n Caps negotiation failed starting from pad '%s'"
" as the QUERY_CAPS returned EMPTY caps",
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)));
empty_filter = gst_caps_is_empty (filter);
if (empty_filter) {
GstPad *peer = _get_peer_pad (pad);
gchar *prev_path = NULL;
if (peer) {
GstObject *prev = gst_pad_get_parent (peer);
if (prev) {
prev_path = gst_object_get_path_string (prev);
gst_object_unref (prev);
}
}
g_string_append_printf (str,
"\n - The QUERY filter caps is EMPTY, this is invalid and is a bug in "
"a previous element (probably in: '%s')\n",
prev_path ? prev_path : "no suspect");
g_free (prev_path);
}
for (i = 0; i < gst_caps_get_size (possible_caps); i++) {
possible_struct = gst_caps_get_structure (possible_caps, i);
possible_name = gst_structure_get_name (possible_struct);
for (j = 0; j < gst_caps_get_size (filter); j++) {
StructureIncompatibleFieldsInfo info = {
.caps_struct_num = i,
.filter_caps_struct_num = j,
.str = str,
.found = found
};
info.filter = filter_struct = gst_caps_get_structure (filter, j);
filter_name = gst_structure_get_name (filter_struct);
if (g_strcmp0 (possible_name, filter_name)) {
_incompatible_fields_info_set_found (&info);
g_string_append_printf (str,
"\n -> Downstream caps struct %d name '%s' differs from "
"filter caps struct %d name '%s'",
i, possible_name, j, filter_name);
continue;
}
gst_structure_foreach (possible_struct,
(GstStructureForeachFunc) _find_structure_incompatible_fields, &info);
if (info.found)
found = TRUE;
}
}
if (!found && !empty_filter) {
gchar *filter_caps_str = gst_caps_to_string (filter);
gchar *possible_caps_str = gst_caps_to_string (possible_caps);
g_string_append_printf (str,
". The exact reason could not be determined but"
" here is the gathered information:\n"
" - %s last query caps filter: %s\n"
" - %s possible caps (as returned by a query on it without filter): %s\n",
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
filter_caps_str,
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
possible_caps_str);
}
gst_caps_unref (possible_caps);
gst_caps_unref (filter);
gst_object_unref (pad);
}
static gboolean
_append_accept_caps_failure_details (GstValidatePadMonitor * monitor,
GString * str)
{
gint i, j;
GstCaps *refused_caps = gst_caps_copy (monitor->last_refused_caps);
GstPad *pad =
GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
(monitor)));
GstCaps *possible_caps = gst_pad_query_caps (pad, NULL);
gchar *caps_str = gst_caps_to_string (monitor->last_refused_caps);
StructureIncompatibleFieldsInfo info = {
.str = str,
.found = FALSE
};
g_string_append_printf (str,
"\n Caps negotiation failed at pad '%s' as it refused caps: %s",
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
caps_str);
g_free (caps_str);
for (i = 0; i < gst_caps_get_size (refused_caps); i++) {
GstStructure *refused_struct = gst_caps_get_structure (refused_caps, i);
const gchar *filter_name;
const gchar *refused_name = gst_structure_get_name (refused_struct);
for (j = 0; j < gst_caps_get_size (possible_caps); j++) {
info.caps_struct_num = i,
info.filter_caps_struct_num = j,
info.filter = gst_caps_get_structure (possible_caps, j);
filter_name = gst_structure_get_name (info.filter);
if (g_strcmp0 (refused_name, filter_name)) {
g_string_append_printf (str,
"\n -> Downstream caps struct %d name '%s' differs from "
"filter caps struct %d name '%s'", i, refused_name, j, filter_name);
continue;
}
gst_structure_foreach (refused_struct,
(GstStructureForeachFunc) _find_structure_incompatible_fields, &info);
}
}
gst_caps_unref (possible_caps);
gst_object_unref (pad);
return TRUE;
}
static gchar *
_generate_not_negotiated_error_report (GstMessage * msg)
{
GString *str;
GList *tmp;
GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (msg));
GstValidatePadMonitor *last_query_caps_fail_monitor = NULL,
*last_refused_caps_monitor = NULL;
str = g_string_new (NULL);
g_string_append_printf (str, "Error message posted by: %s",
GST_OBJECT_NAME (element));
GST_OBJECT_LOCK (element);
for (tmp = element->srcpads; tmp; tmp = tmp->next) {
GstPad *to_check = (GstPad *) tmp->data;
_gather_pad_negotiation_details (to_check, str,
&last_query_caps_fail_monitor, &last_refused_caps_monitor);
}
GST_OBJECT_UNLOCK (element);
if (last_query_caps_fail_monitor)
_append_query_caps_failure_details (last_query_caps_fail_monitor, str);
else if (last_refused_caps_monitor)
_append_accept_caps_failure_details (last_refused_caps_monitor, str);
else {
GST_ERROR ("We should always be able to generate a detailed report"
" about why negotiation failed. Please report a bug against"
" gst-devtools:validate with this message and a way to reproduce.");
}
gst_object_replace ((GstObject **) & last_query_caps_fail_monitor, NULL);
gst_object_replace ((GstObject **) & last_refused_caps_monitor, NULL);
return g_string_free (str, FALSE);
}
static void
_bus_handler (GstBus * bus, GstMessage * message,
GstValidatePipelineMonitor * monitor)
{
GError *err = NULL;
gchar *debug = NULL;
const GstStructure *details = NULL;
gint error_flow = GST_FLOW_OK;
if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
GST_VALIDATE_VERBOSITY_MESSAGES
&& GST_MESSAGE_TYPE (message) != GST_MESSAGE_PROPERTY_NOTIFY) {
GstObject *src_obj;
const GstStructure *s;
guint32 seqnum;
GString *str = g_string_new (NULL);
seqnum = gst_message_get_seqnum (message);
s = gst_message_get_structure (message);
src_obj = GST_MESSAGE_SRC (message);
if (GST_IS_ELEMENT (src_obj)) {
g_string_append_printf (str, "Got message #%u from element \"%s\" (%s): ",
(guint) seqnum, GST_ELEMENT_NAME (src_obj),
GST_MESSAGE_TYPE_NAME (message));
} else if (GST_IS_PAD (src_obj)) {
g_string_append_printf (str, "Got message #%u from pad \"%s:%s\" (%s): ",
(guint) seqnum, GST_DEBUG_PAD_NAME (src_obj),
GST_MESSAGE_TYPE_NAME (message));
} else if (GST_IS_OBJECT (src_obj)) {
g_string_append_printf (str, "Got message #%u from object \"%s\" (%s): ",
(guint) seqnum, GST_OBJECT_NAME (src_obj),
GST_MESSAGE_TYPE_NAME (message));
} else {
g_string_append_printf (str, "Got message #%u (%s): ", (guint) seqnum,
GST_MESSAGE_TYPE_NAME (message));
}
if (s) {
gchar *sstr;
sstr = gst_structure_to_string (s);
g_string_append_printf (str, "%s\n", sstr);
g_free (sstr);
} else {
g_string_append (str, "no message details\n");
}
gst_validate_printf (NULL, "%s", str->str);
g_string_free (str, TRUE);
}
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
print_position (GST_VALIDATE_MONITOR (monitor));
break;
case GST_MESSAGE_ERROR:
gst_message_parse_error (message, &err, &debug);
gst_message_parse_error_details (message, &details);
if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN)) {
if (!gst_validate_fail_on_missing_plugin ()) {
gst_validate_skip_test ("missing plugin: %s -- Debug message: %s\n",
err->message, debug);
} else {
GST_VALIDATE_REPORT (monitor, MISSING_PLUGIN,
"Error: %s -- Debug message: %s", err->message, debug);
}
} else if ((g_error_matches (err, GST_STREAM_ERROR,
GST_STREAM_ERROR_FAILED) && details
&& gst_structure_get_int (details, "flow-return", &error_flow)
&& error_flow == GST_FLOW_NOT_NEGOTIATED)
|| g_error_matches (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FORMAT)) {
gchar *report = _generate_not_negotiated_error_report (message);
GST_VALIDATE_REPORT (monitor, NOT_NEGOTIATED, "%s", report);
g_free (report);
} else {
GST_VALIDATE_REPORT (monitor, ERROR_ON_BUS,
"Got error: %s -- Debug message: %s (%" GST_PTR_FORMAT ")",
err->message, debug, details);
}
GST_VALIDATE_MONITOR_LOCK (monitor);
monitor->got_error = TRUE;
GST_VALIDATE_MONITOR_UNLOCK (monitor);
g_error_free (err);
g_free (debug);
break;
case GST_MESSAGE_WARNING:
gst_message_parse_warning (message, &err, &debug);
GST_VALIDATE_REPORT (monitor, WARNING_ON_BUS,
"Got warning: %s -- Debug message: %s", err->message, debug);
g_error_free (err);
g_free (debug);
break;
case GST_MESSAGE_STATE_CHANGED:
{
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
if (GST_MESSAGE_SRC (message) == target) {
GstState oldstate, newstate, pending;
gst_message_parse_state_changed (message, &oldstate, &newstate,
&pending);
if (oldstate == GST_STATE_READY && newstate == GST_STATE_PAUSED) {
monitor->print_pos_srcid =
g_timeout_add (PRINT_POSITION_TIMEOUT,
(GSourceFunc) print_position, monitor);
} else if (oldstate >= GST_STATE_PAUSED && newstate <= GST_STATE_READY) {
if (monitor->print_pos_srcid
&& g_source_remove (monitor->print_pos_srcid))
monitor->print_pos_srcid = 0;
monitor->got_error = FALSE;
}
}
if (target)
gst_object_unref (target);
break;
}
case GST_MESSAGE_BUFFERING:
{
JsonBuilder *jbuilder = json_builder_new ();
GstBufferingMode mode;
gint percent;
gst_message_parse_buffering (message, &percent);
gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL);
json_builder_begin_object (jbuilder);
json_builder_set_member_name (jbuilder, "type");
json_builder_add_string_value (jbuilder, "buffering");
json_builder_set_member_name (jbuilder, "state");
if (percent == 100) {
/* a 100% message means buffering is done */
gst_validate_printf (NULL, "\nDone buffering\n");
json_builder_add_string_value (jbuilder, "done");
if (monitor->buffering) {
monitor->print_pos_srcid =
g_timeout_add (PRINT_POSITION_TIMEOUT,
(GSourceFunc) print_position, monitor);
monitor->buffering = FALSE;
}
} else {
/* buffering... */
if (!monitor->buffering) {
monitor->buffering = TRUE;
gst_validate_printf (NULL, "\nStart buffering\n");
json_builder_add_string_value (jbuilder, "started");
if (monitor->print_pos_srcid
&& g_source_remove (monitor->print_pos_srcid)) {
monitor->print_pos_srcid = 0;
}
} else {
json_builder_add_string_value (jbuilder, "progress");
}
if (is_tty ())
gst_validate_printf (NULL, "%s %d%% \r", "Buffering...", percent);
}
json_builder_set_member_name (jbuilder, "position");
json_builder_add_int_value (jbuilder, percent);
json_builder_end_object (jbuilder);
gst_validate_send (json_builder_get_root (jbuilder));
g_object_unref (jbuilder);
break;
}
case GST_MESSAGE_STREAM_COLLECTION:
{
GstStreamCollection *collection = NULL;
gst_message_parse_stream_collection (message, &collection);
gst_object_replace ((GstObject **) & monitor->stream_collection,
(GstObject *) collection);
gst_object_unref (collection);
break;
}
case GST_MESSAGE_STREAMS_SELECTED:
{
guint i;
if (monitor->streams_selected) {
g_list_free_full (monitor->streams_selected, gst_object_unref);
monitor->streams_selected = NULL;
}
for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
GstStream *stream =
gst_message_streams_selected_get_stream (message, i);
monitor->streams_selected =
g_list_append (monitor->streams_selected, stream);
}
break;
}
case GST_MESSAGE_PROPERTY_NOTIFY:
{
const GValue *val;
const gchar *name;
GstObject *obj;
gchar *val_str = NULL;
gchar *obj_name;
if (!(GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
GST_VALIDATE_VERBOSITY_PROPS_CHANGES))
return;
gst_message_parse_property_notify (message, &obj, &name, &val);
obj_name = gst_object_get_path_string (GST_OBJECT (obj));
if (val != NULL) {
if (G_VALUE_HOLDS_STRING (val))
val_str = g_value_dup_string (val);
else if (G_VALUE_TYPE (val) == GST_TYPE_CAPS)
val_str = gst_caps_to_string (g_value_get_boxed (val));
else if (G_VALUE_TYPE (val) == GST_TYPE_TAG_LIST)
val_str = gst_tag_list_to_string (g_value_get_boxed (val));
else if (G_VALUE_TYPE (val) == GST_TYPE_STRUCTURE)
val_str = gst_structure_to_string (g_value_get_boxed (val));
else
val_str = gst_value_serialize (val);
} else {
val_str = g_strdup ("(no value)");
}
gst_validate_printf (NULL, "%s: %s = %s\n", obj_name, name, val_str);
g_free (obj_name);
g_free (val_str);
break;
break;
}
default:
break;
}
}
static void
gst_validate_pipeline_monitor_create_scenarios (GstValidateBinMonitor * monitor)
{
/* scenarios currently only make sense for pipelines */
const gchar *scenarios_names, *scenario_name = NULL;
gchar **scenarios = NULL, *testfile = NULL;
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
GstValidateRunner *runner =
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
GList *scenario_structs = NULL;
if (gst_validate_get_test_file_scenario (&scenario_structs, &scenario_name,
&testfile)) {
if (scenario_name) {
monitor->scenario =
gst_validate_scenario_factory_create (runner,
GST_ELEMENT_CAST (target), scenario_name);
goto done;
}
monitor->scenario =
gst_validate_scenario_from_structs (runner,
GST_ELEMENT_CAST (target), scenario_structs, testfile);
goto done;
}
if ((scenarios_names = g_getenv ("GST_VALIDATE_SCENARIO"))) {
gint i;
scenarios = g_strsplit (scenarios_names, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; scenarios[i]; i++) {
gchar **scenario_v = g_strsplit (scenarios[i], "->", 2);
if (scenario_v[1] && target) {
if (!g_pattern_match_simple (scenario_v[1], GST_OBJECT_NAME (target))) {
GST_INFO_OBJECT (monitor, "Not attaching to pipeline %" GST_PTR_FORMAT
" as not matching pattern %s", target, scenario_v[1]);
g_strfreev (scenario_v);
goto done;
}
}
if (target)
monitor->scenario =
gst_validate_scenario_factory_create (runner,
GST_ELEMENT_CAST (target), scenario_v[0]);
else
GST_INFO_OBJECT (monitor, "Not creating scenario as monitor"
" already does not have a target.");
g_strfreev (scenario_v);
}
}
done:
g_strfreev (scenarios);
if (target)
gst_object_unref (target);
if (runner)
gst_object_unref (runner);
}
/**
* gst_validate_pipeline_monitor_new:
* @pipeline: (transfer none): a #GstPipeline to run Validate on
*/
GstValidatePipelineMonitor *
gst_validate_pipeline_monitor_new (GstPipeline * pipeline,
GstValidateRunner * runner, GstValidateMonitor * parent)
{
GstBus *bus;
GstValidatePipelineMonitor *monitor =
g_object_new (GST_TYPE_VALIDATE_PIPELINE_MONITOR, "object",
pipeline, "validate-runner", runner, "validate-parent", parent,
"pipeline", pipeline, NULL);
GstObject *target =
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
if (target == NULL) {
g_object_unref (monitor);
return NULL;
}
gst_validate_pipeline_monitor_create_scenarios (GST_VALIDATE_BIN_MONITOR
(monitor));
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
gst_bus_enable_sync_message_emission (bus);
g_signal_connect (bus, "sync-message", (GCallback) _bus_handler, monitor);
if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
GST_VALIDATE_VERBOSITY_PROPS_CHANGES) {
monitor->deep_notify_id =
gst_element_add_property_deep_notify_watch ((GstElement *) pipeline,
NULL, TRUE);
}
gst_object_unref (bus);
if (g_strcmp0 (G_OBJECT_TYPE_NAME (pipeline), "GstPlayBin") == 0)
monitor->is_playbin = TRUE;
else if (g_strcmp0 (G_OBJECT_TYPE_NAME (pipeline), "GstPlayBin3") == 0)
monitor->is_playbin3 = TRUE;
gst_object_unref (target);
return monitor;
}
@@ -0,0 +1,98 @@
/* GStreamer
* Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
*
* gst-validate-pipeline-monitor.h - Validate PipelineMonitor class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_PIPELINE_MONITOR_H__
#define __GST_VALIDATE_PIPELINE_MONITOR_H__
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/validate/gst-validate-bin-monitor.h>
#include <gst/validate/gst-validate-runner.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_PIPELINE_MONITOR (gst_validate_pipeline_monitor_get_type ())
#define GST_IS_VALIDATE_PIPELINE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_PIPELINE_MONITOR))
#define GST_IS_VALIDATE_PIPELINE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_PIPELINE_MONITOR))
#define GST_VALIDATE_PIPELINE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_PIPELINE_MONITOR, GstValidatePipelineMonitorClass))
#define GST_VALIDATE_PIPELINE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_PIPELINE_MONITOR, GstValidatePipelineMonitor))
#define GST_VALIDATE_PIPELINE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_PIPELINE_MONITOR, GstValidatePipelineMonitorClass))
#define GST_VALIDATE_PIPELINE_MONITOR_CAST(obj) ((GstValidatePipelineMonitor*)(obj))
#define GST_VALIDATE_PIPELINE_MONITOR_CLASS_CAST(klass) ((GstValidatePipelineMonitorClass*)(klass))
#endif
#define GST_VALIDATE_PIPELINE_MONITOR_GET_PIPELINE(m) (GST_PIPELINE_CAST (GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (m)))
typedef struct _GstValidatePipelineMonitor GstValidatePipelineMonitor;
typedef struct _GstValidatePipelineMonitorClass GstValidatePipelineMonitorClass;
/**
* GstValidatePipelineMonitor:
*
* GStreamer Validate PipelineMonitor class.
*
* Class that wraps a #GstPipeline for Validate checks
*/
struct _GstValidatePipelineMonitor {
GstValidateBinMonitor parent;
/*< private >*/
gulong element_added_id;
guint print_pos_srcid;
gboolean buffering;
gboolean got_error;
/* TRUE if monitoring a playbin2 pipeline */
gboolean is_playbin;
/* TRUE if monitoring a playbin3 pipeline */
gboolean is_playbin3;
/* Latest collection received from GST_MESSAGE_STREAM_COLLECTION */
GstStreamCollection *stream_collection;
/* Latest GstStream received from GST_MESSAGE_STREAMS_SELECTED */
GList *streams_selected;
gulong deep_notify_id;
};
/**
* GstValidatePipelineMonitorClass:
* @parent_class: parent
*
* GStreamer Validate PipelineMonitor object class.
*/
struct _GstValidatePipelineMonitorClass {
GstValidateBinMonitorClass parent_class;
};
/* normal GObject stuff */
GST_VALIDATE_API
GType gst_validate_pipeline_monitor_get_type (void);
GST_VALIDATE_API
GstValidatePipelineMonitor * gst_validate_pipeline_monitor_new (GstPipeline * pipeline,
GstValidateRunner * runner, GstValidateMonitor * parent);
G_END_DECLS
#endif /* __GST_VALIDATE_PIPELINE_MONITOR_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,336 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-monitor-report.h - Validate Element report structures and functions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_REPORT_H__
#define __GST_VALIDATE_REPORT_H__
#include <glib-object.h>
typedef struct _GstValidateReport GstValidateReport;
typedef guintptr GstValidateIssueId;
#include <gst/gst.h>
#include <gst/validate/validate-prelude.h>
#include <gst/validate/gst-validate-reporter.h>
#include "gst-validate-types.h"
G_BEGIN_DECLS
GST_VALIDATE_API
GType gst_validate_report_get_type (void);
#define GST_TYPE_VALIDATE_REPORT (gst_validate_report_get_type ())
/**
* GstValidateDebugFlags:
* GST_VALIDATE_FATAL_DEFAULT:
* GST_VALIDATE_FATAL_ISSUES:
* GST_VALIDATE_FATAL_WARNINGS:
* GST_VALIDATE_FATAL_CRITICALS:
* GST_VALIDATE_PRINT_ISSUES:
* GST_VALIDATE_PRINT_WARNINGS:
* GST_VALIDATE_PRINT_CRITICALS:
*/
typedef enum {
GST_VALIDATE_FATAL_DEFAULT = 0,
GST_VALIDATE_FATAL_ISSUES = 1 << 0,
GST_VALIDATE_FATAL_WARNINGS = 1 << 1,
GST_VALIDATE_FATAL_CRITICALS = 1 << 2,
GST_VALIDATE_PRINT_ISSUES = 1 << 3,
GST_VALIDATE_PRINT_WARNINGS = 1 << 4,
GST_VALIDATE_PRINT_CRITICALS = 1 << 5
} GstValidateDebugFlags;
/**
* GstValidateReportLevel:
*/
typedef enum {
GST_VALIDATE_REPORT_LEVEL_CRITICAL,
GST_VALIDATE_REPORT_LEVEL_WARNING,
GST_VALIDATE_REPORT_LEVEL_ISSUE,
GST_VALIDATE_REPORT_LEVEL_IGNORE,
GST_VALIDATE_REPORT_LEVEL_UNKNOWN,
GST_VALIDATE_REPORT_LEVEL_EXPECTED,
GST_VALIDATE_REPORT_LEVEL_NUM_ENTRIES,
} GstValidateReportLevel;
#define _QUARK g_quark_from_static_string
#define BUFFER_BEFORE_SEGMENT _QUARK("buffer::before-segment")
#define BUFFER_IS_OUT_OF_SEGMENT _QUARK("buffer::is-out-of-segment")
#define BUFFER_TIMESTAMP_OUT_OF_RECEIVED_RANGE _QUARK("buffer::timestamp-out-of-received-range")
#define WRONG_FLOW_RETURN _QUARK("buffer::wrong-flow-return")
#define BUFFER_AFTER_EOS _QUARK("buffer::after-eos")
#define WRONG_BUFFER _QUARK("buffer::not-expected-one")
#define FLOW_ERROR_WITHOUT_ERROR_MESSAGE _QUARK("buffer::flow-error-without-error-message")
#define BUFFER_MISSING_DISCONT _QUARK("buffer::missing-discont")
#define PULL_RANGE_FROM_WRONG_THREAD _QUARK("threading::pull-range-from-wrong-thread")
#define CAPS_IS_MISSING_FIELD _QUARK("caps::is-missing-field")
#define CAPS_FIELD_HAS_BAD_TYPE _QUARK("caps::field-has-bad-type")
#define CAPS_EXPECTED_FIELD_NOT_FOUND _QUARK("caps::expected-field-not-found")
#define GET_CAPS_NOT_PROXYING_FIELDS _QUARK("caps::not-proxying-fields")
#define CAPS_FIELD_UNEXPECTED_VALUE _QUARK("caps::field-unexpected-value")
#define EVENT_NEWSEGMENT_NOT_PUSHED _QUARK("event::newsegment-not-pushed")
#define SERIALIZED_EVENT_WASNT_PUSHED_IN_TIME _QUARK("event::serialized-event-wasnt-pushed-in-time")
#define EOS_HAS_WRONG_SEQNUM _QUARK("event::eos-has-wrong-seqnum")
#define FLUSH_START_HAS_WRONG_SEQNUM _QUARK("event::flush-start-has-wrong-seqnum")
#define FLUSH_STOP_HAS_WRONG_SEQNUM _QUARK("event::flush-stop-has-wrong-seqnum")
#define SEGMENT_HAS_WRONG_SEQNUM _QUARK("event::segment-has-wrong-seqnum")
#define SEGMENT_HAS_WRONG_START _QUARK("event::segment-has-wrong-start")
#define EVENT_SERIALIZED_OUT_OF_ORDER _QUARK("event::serialized-out-of-order")
#define EVENT_NEW_SEGMENT_MISMATCH _QUARK("event::segment-mismatch")
#define EVENT_FLUSH_START_UNEXPECTED _QUARK("event::flush-start-unexpected")
#define EVENT_FLUSH_STOP_UNEXPECTED _QUARK("event::flush-stop-unexpected")
#define EVENT_CAPS_DUPLICATE _QUARK("event::caps-duplicate")
#define EVENT_SEEK_NOT_HANDLED _QUARK("event::seek-not-handled")
#define EVENT_SEEK_RESULT_POSITION_WRONG _QUARK("event::seek-result-position-wrong")
#define EVENT_SEEK_INVALID_SEQNUM _QUARK("event::seek-invalid_seqnum")
#define EVENT_EOS_WITHOUT_SEGMENT _QUARK("event::eos-without-segment")
#define EVENT_INVALID_SEQNUM _QUARK("event::invalid-seqnum")
#define STATE_CHANGE_FAILURE _QUARK("state::change-failure")
#define FILE_NO_STREAM_INFO _QUARK("file-checking::no-stream-info")
#define FILE_NO_STREAM_ID _QUARK("file-checking::no-stream-id")
#define FILE_TAG_DETECTION_INCORRECT _QUARK("file-checking::tag-detection-incorrect")
#define FILE_SIZE_INCORRECT _QUARK("file-checking::size-incorrect")
#define FILE_DURATION_INCORRECT _QUARK("file-checking::duration-incorrect")
#define FILE_SEEKABLE_INCORRECT _QUARK("file-checking::seekable-incorrect")
#define FILE_PROFILE_INCORRECT _QUARK("file-checking::profile-incorrect")
#define FILE_FRAMES_INCORRECT _QUARK("file-checking::frames-incorrect")
#define FILE_SEGMENT_INCORRECT _QUARK("file-checking::segment-incorrect")
#define ALLOCATION_FAILURE _QUARK("runtime::allocation-failure")
#define MISSING_PLUGIN _QUARK("runtime::missing-plugin")
#define NOT_NEGOTIATED _QUARK("runtime::not-negotiated")
#define WARNING_ON_BUS _QUARK("runtime::warning-on-bus")
#define ERROR_ON_BUS _QUARK("runtime::error-on-bus")
#define QUERY_POSITION_SUPERIOR_DURATION _QUARK("query::position-superior-duration")
#define QUERY_POSITION_OUT_OF_SEGMENT _QUARK("query::position-out-of-segment")
#define SCENARIO_NOT_ENDED _QUARK("scenario::not-ended")
#define SCENARIO_FILE_MALFORMED _QUARK("scenario::malformed")
#define SCENARIO_ACTION_EXECUTION_ERROR _QUARK("scenario::execution-error")
#define SCENARIO_ACTION_CHECK_ERROR _QUARK("scenario::check-error")
#define SCENARIO_ACTION_TIMEOUT _QUARK("scenario::action-timeout")
#define SCENARIO_ACTION_EXECUTION_ISSUE _QUARK("scenario::execution-issue")
#define CONFIG_LATENCY_TOO_HIGH _QUARK("config::latency-too-high")
#define CONFIG_TOO_MANY_BUFFERS_DROPPED _QUARK("config::too-many-buffers-dropped")
#define CONFIG_BUFFER_FREQUENCY_TOO_LOW _QUARK("config::buffer-frequency-too-low")
#define G_LOG_ISSUE _QUARK("g-log::issue")
#define G_LOG_WARNING _QUARK("g-log::warning")
#define G_LOG_CRITICAL _QUARK("g-log::critical")
/**
* GstValidateIssueFlags:
* GST_VALIDATE_ISSUE_FLAGS_NONE: No special flags for the issue type
* GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS: Always show all accurences of the issue in full details
* GST_VALIDATE_ISSUE_FLAGS_NO_BACKTRACE: Do not generate backtrace for the issue type
*/
typedef enum {
GST_VALIDATE_ISSUE_FLAGS_NONE = 0,
GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS = 1 << 0,
GST_VALIDATE_ISSUE_FLAGS_NO_BACKTRACE = 1 << 1,
/**
* GST_VALIDATE_ISSUE_FLAGS_FORCE_BACKTRACE:
*
* Always generate backtrace, even if not a critical issue
*
* Since: 1.20
*/
GST_VALIDATE_ISSUE_FLAGS_FORCE_BACKTRACE = 1 << 2,
} GstValidateIssueFlags;
typedef struct {
GstValidateIssueId issue_id;
/* Summary: one-liner translatable description of the issue */
gchar *summary;
/* description: multi-line translatable description of:
* * what the issue is (and why it's an issue)
* * what the source problem could be
* * pointers to fixing the issue
*/
gchar *description;
/* The name of the area of issue
* this one is in */
gchar *area;
/* The name of the issue type */
gchar *name;
/* default_level: The default level of severity for this
* issue. */
GstValidateReportLevel default_level;
gint refcount;
GstValidateIssueFlags flags;
gpointer _gst_reserved[GST_PADDING];
} GstValidateIssue;
GST_VALIDATE_API
GType gst_validate_issue_get_type (void);
struct _GstValidateReport {
GstMiniObject mini_object;
/* issue: The issue this report corresponds to (to get description, summary,...) */
GstValidateIssue *issue;
GstValidateReportLevel level;
/* The reporter that reported the issue (to get names, info, ...) */
GstValidateReporter *reporter;
/* timestamp: The time at which this issue happened since
* the process start (to stay in sync with gst logging) */
GstClockTime timestamp;
/* message: issue-specific message. Gives more detail on the actual
* issue. Can be NULL */
gchar *message;
/* When reporter->intercept_report returns KEEP, the report is not
* added to the runner. It can be added as a "shadow_report" to
* the upstream report, which is tracked by the runner. */
GMutex shadow_reports_lock;
GstValidateReport *master_report;
GList *shadow_reports;
/* Lists the reports that were repeated inside the same reporter */
GList *repeated_reports;
GstValidateReportingDetails reporting_level;
gchar *reporter_name;
gchar *trace;
gchar *dotfile_name;
gpointer _gst_reserved[GST_PADDING - 2];
};
void gst_validate_report_add_message (GstValidateReport *report,
const gchar *message);
#define GST_VALIDATE_ISSUE_FORMAT G_GUINTPTR_FORMAT " (%s) : %s: %s"
#define GST_VALIDATE_ISSUE_ARGS(i) gst_validate_issue_get_id (i), \
gst_validate_report_level_get_name (i->default_level), \
i->area, \
i->summary
#define GST_VALIDATE_ERROR_REPORT_PRINT_FORMAT GST_TIME_FORMAT " <%s>: %" GST_VALIDATE_ISSUE_FORMAT ": %s"
#define GST_VALIDATE_REPORT_PRINT_ARGS(r) GST_TIME_ARGS (r->timestamp), \
gst_validate_reporter_get_name (r->reporter), \
GST_VALIDATE_ISSUE_ARGS (r->issue), \
r->message
GST_VALIDATE_API
void gst_validate_report_init (void);
GST_VALIDATE_API
GstValidateIssue *gst_validate_issue_from_id (GstValidateIssueId issue_id);
GST_VALIDATE_API
GstValidateIssueId gst_validate_issue_get_id (GstValidateIssue * issue);
GST_VALIDATE_API
void gst_validate_issue_register (GstValidateIssue * issue);
GST_VALIDATE_API
GstValidateIssue *gst_validate_issue_new (GstValidateIssueId issue_id, const gchar * summary,
const gchar * description,
GstValidateReportLevel default_level);
GST_VALIDATE_API
GstValidateIssue* gst_validate_issue_new_full(GstValidateIssueId issue_id, const gchar* summary,
const gchar* description, GstValidateReportLevel default_level,
GstValidateIssueFlags flags);
GST_VALIDATE_API
void gst_validate_issue_set_default_level (GstValidateIssue *issue,
GstValidateReportLevel default_level);
GST_VALIDATE_API
GstValidateReport *gst_validate_report_new (GstValidateIssue * issue,
GstValidateReporter * reporter,
const gchar * message);
GST_VALIDATE_API
void gst_validate_report_unref (GstValidateReport * report);
GST_VALIDATE_API
GstValidateReport *gst_validate_report_ref (GstValidateReport * report);
GST_VALIDATE_API
GstValidateIssueId gst_validate_report_get_issue_id (GstValidateReport * report);
GST_VALIDATE_API
gboolean gst_validate_report_check_abort (GstValidateReport * report);
GST_VALIDATE_API
void gst_validate_report_printf (GstValidateReport * report);
GST_VALIDATE_API
void gst_validate_report_print_level (GstValidateReport *report);
GST_VALIDATE_API
void gst_validate_report_print_detected_on (GstValidateReport *report);
GST_VALIDATE_API
void gst_validate_report_print_details (GstValidateReport *report);
GST_VALIDATE_API
void gst_validate_report_print_description (GstValidateReport *report);
GST_VALIDATE_API
const gchar * gst_validate_report_level_get_name (GstValidateReportLevel level);
GST_VALIDATE_API
void gst_validate_printf (gpointer source,
const gchar * format,
...) G_GNUC_PRINTF (2, 3) G_GNUC_NO_INSTRUMENT;
GST_VALIDATE_API
void gst_validate_print_action (GstValidateAction *action, const gchar * message);
GST_VALIDATE_API
void gst_validate_printf_valist (gpointer source,
const gchar * format,
va_list args) G_GNUC_PRINTF (2, 0) G_GNUC_NO_INSTRUMENT;
GST_VALIDATE_API
gboolean gst_validate_report_should_print (GstValidateReport * report);
GST_VALIDATE_API
gboolean gst_validate_report_set_master_report(GstValidateReport *report, GstValidateReport *master_report);
GST_VALIDATE_API
void gst_validate_report_set_reporting_level (GstValidateReport *report, GstValidateReportingDetails level);
GST_VALIDATE_API
void gst_validate_report_add_repeated_report (GstValidateReport *report, GstValidateReport *repeated_report);
GST_VALIDATE_API
GstValidateReportLevel gst_validate_report_level_from_name (const gchar *level_name);
GST_VALIDATE_API
void gst_validate_print_position(GstClockTime position, GstClockTime duration, gdouble rate, gchar* extra_info);
GST_VALIDATE_API void gst_validate_print_issues (void);
GST_VALIDATE_API
void gst_validate_error_structure (gpointer action, const gchar* format, ...) G_GNUC_PRINTF (2, 3);
GST_VALIDATE_API
void gst_validate_abort (const gchar * format, ...) G_GNUC_PRINTF (1, 2);
GST_VALIDATE_API
void gst_validate_skip_test (const gchar* format, ...);
G_END_DECLS
#endif /* __GST_VALIDATE_REPORT_H__ */
@@ -0,0 +1,566 @@
/* GStreamer
*
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
*
* gst-validate-reporter.c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:gst-validate-reporter
* @title: GstValidateReporter
* @short_description: A #GInterface that allows #GObject to be used as originator of
* issues in the GstValidate reporting system
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <math.h>
#include "gst-validate-internal.h"
#include "gst-validate-reporter.h"
#include "gst-validate-report.h"
#define REPORTER_PRIVATE "gst-validate-reporter-private"
typedef struct _GstValidateReporterPrivate
{
GWeakRef runner;
GHashTable *reports;
char *name;
guint log_handler_id;
GMutex reports_lock;
} GstValidateReporterPrivate;
static GstValidateReporterPrivate *g_log_handler = NULL;
G_DEFINE_INTERFACE (GstValidateReporter, gst_validate_reporter, G_TYPE_OBJECT);
static void
gst_validate_reporter_default_init (GstValidateReporterInterface * iface)
{
g_object_interface_install_property (iface,
g_param_spec_object ("validate-runner", "Validate Runner",
"The Validate runner to report errors to",
GST_TYPE_VALIDATE_RUNNER,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
}
static void
_free_priv (GstValidateReporterPrivate * priv)
{
if (g_log_handler == priv) {
g_log_set_default_handler (g_log_default_handler, NULL);
g_log_handler = NULL;
}
g_hash_table_unref (priv->reports);
g_free (priv->name);
g_mutex_clear (&priv->reports_lock);
g_weak_ref_clear (&priv->runner);
g_slice_free (GstValidateReporterPrivate, priv);
}
static GstValidateReporterPrivate *
gst_validate_reporter_get_priv (GstValidateReporter * reporter)
{
GstValidateReporterPrivate *priv;
priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE);
if (priv == NULL) {
priv = g_slice_new0 (GstValidateReporterPrivate);
priv->reports = g_hash_table_new_full (g_direct_hash,
g_direct_equal, NULL, (GDestroyNotify) gst_validate_report_unref);
g_mutex_init (&priv->reports_lock);
g_object_set_data_full (G_OBJECT (reporter), REPORTER_PRIVATE, priv,
(GDestroyNotify) _free_priv);
}
return priv;
}
#define GST_VALIDATE_REPORTER_REPORTS_LOCK(r) \
G_STMT_START { \
(g_mutex_lock (&gst_validate_reporter_get_priv(GST_VALIDATE_REPORTER_CAST(r))->reports_lock)); \
} G_STMT_END
#define GST_VALIDATE_REPORTER_REPORTS_UNLOCK(r) \
G_STMT_START { \
(g_mutex_unlock (&gst_validate_reporter_get_priv(GST_VALIDATE_REPORTER_CAST(r))->reports_lock)); \
} G_STMT_END
static GstValidateInterceptionReturn
gst_validate_reporter_intercept_report (GstValidateReporter * reporter,
GstValidateReport * report)
{
GstValidateInterceptionReturn ret = GST_VALIDATE_REPORTER_REPORT;
GstValidateReporterInterface *iface =
GST_VALIDATE_REPORTER_GET_INTERFACE (reporter);
if (iface->intercept_report) {
ret = iface->intercept_report (reporter, report);
}
return ret;
}
GstValidateReportingDetails
gst_validate_reporter_get_reporting_level (GstValidateReporter * reporter)
{
GstValidateReportingDetails ret = GST_VALIDATE_SHOW_UNKNOWN;
GstValidateReporterInterface *iface =
GST_VALIDATE_REPORTER_GET_INTERFACE (reporter);
if (iface->get_reporting_level) {
ret = iface->get_reporting_level (reporter);
}
return ret;
}
/**
* gst_validate_reporter_get_pipeline:
* @reporter: The reporter to get the pipeline from
*
* Returns: (transfer full) (allow-none): The #GstPipeline
*/
GstPipeline *
gst_validate_reporter_get_pipeline (GstValidateReporter * reporter)
{
GstValidateReporterInterface *iface =
GST_VALIDATE_REPORTER_GET_INTERFACE (reporter);
if (iface->get_pipeline)
return iface->get_pipeline (reporter);
return NULL;
}
GstValidateReport *
gst_validate_reporter_get_report (GstValidateReporter * reporter,
GstValidateIssueId issue_id)
{
GstValidateReport *report;
GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter);
GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter);
report = g_hash_table_lookup (priv->reports, (gconstpointer) issue_id);
GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter);
return report;
}
void
gst_validate_report_valist (GstValidateReporter * reporter,
GstValidateIssueId issue_id, const gchar * format, va_list var_args)
{
GstValidateReport *report, *prev_report;
gchar *message, *combo;
va_list vacopy;
GstValidateIssue *issue;
GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter);
GstValidateInterceptionReturn int_ret;
GstValidateRunner *runner = NULL;
issue = gst_validate_issue_from_id (issue_id);
g_return_if_fail (issue != NULL);
g_return_if_fail (GST_IS_VALIDATE_REPORTER (reporter));
G_VA_COPY (vacopy, var_args);
message = gst_info_strdup_vprintf (format, vacopy);
report = gst_validate_report_new (issue, reporter, message);
#ifndef GST_DISABLE_GST_DEBUG
combo =
g_strdup_printf ("<%s> %" GST_VALIDATE_ISSUE_FORMAT " : %s", priv->name,
GST_VALIDATE_ISSUE_ARGS (issue), format);
G_VA_COPY (vacopy, var_args);
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_ERROR, __FILE__,
GST_FUNCTION, __LINE__, NULL, combo, vacopy);
} else if (report->level == GST_VALIDATE_REPORT_LEVEL_WARNING)
gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING, __FILE__,
GST_FUNCTION, __LINE__, NULL, combo, vacopy);
else if (report->level == GST_VALIDATE_REPORT_LEVEL_ISSUE)
gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_LOG, __FILE__,
GST_FUNCTION, __LINE__, (GObject *) NULL, combo, vacopy);
else
gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, __FILE__,
GST_FUNCTION, __LINE__, NULL, combo, vacopy);
g_free (combo);
#endif
va_end (vacopy);
int_ret = gst_validate_reporter_intercept_report (reporter, report);
if (int_ret == GST_VALIDATE_REPORTER_DROP) {
gst_validate_report_unref (report);
goto done;
}
prev_report = g_hash_table_lookup (priv->reports, (gconstpointer) issue_id);
runner = gst_validate_reporter_get_runner (reporter);
if (prev_report && prev_report->level != GST_VALIDATE_REPORT_LEVEL_EXPECTED) {
GstValidateReportingDetails reporter_level =
gst_validate_reporter_get_reporting_level (reporter);
GstValidateReportingDetails runner_level = GST_VALIDATE_SHOW_UNKNOWN;
if (runner)
runner_level = gst_validate_runner_get_default_reporting_level (runner);
if ((reporter_level == GST_VALIDATE_SHOW_ALL ||
(runner_level == GST_VALIDATE_SHOW_ALL &&
reporter_level == GST_VALIDATE_SHOW_UNKNOWN)) ||
(issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)) {
gst_validate_report_add_repeated_report (prev_report, report);
}
gst_validate_report_unref (report);
goto done;
}
GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter);
g_hash_table_insert (priv->reports, (gpointer) issue_id, report);
GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter);
if (runner && int_ret == GST_VALIDATE_REPORTER_REPORT) {
gst_validate_runner_add_report (runner, report);
}
if (gst_validate_report_check_abort (report)) {
if (runner)
gst_validate_runner_printf (runner);
gst_validate_abort ("Fatal report received: %"
GST_VALIDATE_ERROR_REPORT_PRINT_FORMAT,
GST_VALIDATE_REPORT_PRINT_ARGS (report));
}
done:
if (runner)
gst_object_unref (runner);
g_free (message);
}
static void
gst_validate_default_log_hanlder (const gchar * log_domain,
GLogLevelFlags log_level, const gchar * message, gpointer user_data)
{
gchar *trace = gst_debug_get_stack_trace (GST_STACK_TRACE_SHOW_FULL);
if (trace) {
gst_validate_printf (NULL, "\nStack trace:\n%s\n", trace);
g_free (trace);
}
g_log_default_handler (log_domain, log_level, message, user_data);
}
static void
gst_validate_reporter_destroyed (gpointer udata, GObject * freed_reporter)
{
g_log_set_handler ("GStreamer",
G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_default_log_hanlder, NULL);
g_log_set_handler ("GLib",
G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_default_log_hanlder, NULL);
g_log_set_handler ("GLib-GObject",
G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_default_log_hanlder, NULL);
}
static void
gst_validate_reporter_g_log_func (const gchar * log_domain,
GLogLevelFlags log_level, const gchar * message,
GstValidateReporter * reporter)
{
if (log_level & G_LOG_LEVEL_ERROR)
gst_validate_default_log_hanlder (log_domain, log_level, message, reporter);
else if (log_level & G_LOG_LEVEL_CRITICAL)
GST_VALIDATE_REPORT (reporter, G_LOG_CRITICAL, "%s", message);
else if (log_level & G_LOG_LEVEL_WARNING)
GST_VALIDATE_REPORT (reporter, G_LOG_WARNING, "%s", message);
}
/**
* gst_validate_report:
* @reporter: The source of the new report
* @issue_id: The #GstValidateIssueId of the issue
* @format: The format of the message describing the issue in a printf
* format followed by the parameters.
* @...: Substitution arguments for @format
*
* Reports a new issue in the GstValidate reporting system.
*
* You can also use #GST_VALIDATE_REPORT instead.
*/
void
gst_validate_report (GstValidateReporter * reporter,
GstValidateIssueId issue_id, const gchar * format, ...)
{
va_list var_args;
va_start (var_args, format);
gst_validate_report_valist (reporter, issue_id, format, var_args);
va_end (var_args);
}
/**
* gst_validate_report_action:
* @reporter: The source of the new report
* @action: The action reporting the issue
* @issue_id: The #GstValidateIssueId of the issue
* @format: The format of the message describing the issue in a printf
* format followed by the parameters.
* @...: Substitution arguments for @format
*
* Reports a new issue in the GstValidate reporting system specifying @action
* as failling action .
*
* You can also use #GST_VALIDATE_REPORT instead.
*/
void
gst_validate_report_action (GstValidateReporter * reporter,
GstValidateAction * action, GstValidateIssueId issue_id,
const gchar * format, ...)
{
va_list var_args, var_copy;
GString *f;
if (!action) {
f = g_string_new (format);
goto done;
}
f = g_string_new (NULL);
g_string_append_printf (f, "\n> %s:%d", GST_VALIDATE_ACTION_FILENAME (action),
GST_VALIDATE_ACTION_LINENO (action));
if (GST_VALIDATE_ACTION_N_REPEATS (action))
g_string_append_printf (f, " (repeat: %d/%d)",
action->repeat, GST_VALIDATE_ACTION_N_REPEATS (action));
g_string_append_printf (f, "\n%s", GST_VALIDATE_ACTION_DEBUG (action));
if (gst_validate_action_get_level (action)) {
gchar *subaction_str = gst_structure_to_string (action->structure);
g_string_append_printf (f, "\n |-> %s", subaction_str);
g_free (subaction_str);
}
g_string_append_printf (f, "\n >\n > %s", format);
done:
va_start (var_args, format);
G_VA_COPY (var_copy, var_args);
gst_validate_report_valist (reporter, issue_id, f->str, var_args);
if (action) {
gint i, indent = gst_validate_action_get_level (action) * 2;
gchar *message, **lines, *color = NULL;
const gchar *endcolor = "";
if (g_log_writer_supports_color (fileno (stderr))) {
color = gst_debug_construct_term_color (GST_DEBUG_FG_RED);
endcolor = "\033[0m";
}
gst_validate_printf (NULL, "%*s%s> Error%s:\n", indent, "",
color ? color : "", endcolor);
message = gst_info_strdup_vprintf (f->str, var_copy);
lines = g_strsplit (message, "\n", -1);
for (i = 1; lines[i]; i++)
gst_validate_printf (NULL, "%*s%s>%s %s\n", indent, "", color, endcolor,
lines[i]);
g_strfreev (lines);
g_free (message);
g_free (color);
}
va_end (var_args);
va_end (var_copy);
g_string_free (f, TRUE);
}
void
gst_validate_reporter_report_simple (GstValidateReporter * reporter,
GstValidateIssueId issue_id, const gchar * message)
{
gst_validate_report (reporter, issue_id, "%s", message);
}
/**
* gst_validate_reporter_set_name:
* @reporter: The reporter to set the name on
* @name: (transfer full): The name of the reporter
*
* Sets @ name on @reporter
*/
void
gst_validate_reporter_set_name (GstValidateReporter * reporter, gchar * name)
{
GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter);
g_free (priv->name);
priv->name = name;
}
const gchar *
gst_validate_reporter_get_name (GstValidateReporter * reporter)
{
GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter);
return priv->name;
}
/**
* gst_validate_reporter_get_runner:
* @reporter: The reporter to get the runner from
*
* Returns: (transfer full): The runner
*/
GstValidateRunner *
gst_validate_reporter_get_runner (GstValidateReporter * reporter)
{
GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter);
return g_weak_ref_get (&priv->runner);
}
void
gst_validate_reporter_set_runner (GstValidateReporter * reporter,
GstValidateRunner * runner)
{
GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter);
g_weak_ref_set (&priv->runner, runner);
g_object_notify (G_OBJECT (reporter), "validate-runner");
}
/**
* gst_validate_reporter_set_handle_g_logs:
* @reporter: The #GstValidateReporter to set has the handler for g_log
*
* Set @reporter has the 'source' of any g_log happening during the
* execution. Usually the monitor of the first #GstPipeline is used
* to handle g_logs.
*
* Basically this function is used in order to start tracking any
* issue reported with g_log in the process into GstValidate report
* in the GstValidate reporting system.
*/
void
gst_validate_reporter_set_handle_g_logs (GstValidateReporter * reporter)
{
g_log_set_default_handler ((GLogFunc) gst_validate_reporter_g_log_func,
reporter);
g_log_set_handler ("GStreamer",
G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_reporter_g_log_func, reporter);
g_log_set_handler ("GLib",
G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_reporter_g_log_func, reporter);
g_log_set_handler ("GLib-GObject",
G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_reporter_g_log_func, reporter);
g_log_handler = gst_validate_reporter_get_priv (reporter);
g_object_weak_ref (G_OBJECT (reporter), gst_validate_reporter_destroyed,
NULL);
}
/**
* gst_validate_reporter_get_reports:
* @reporter: a #GstValidateReporter
*
* Get the list of reports present in the reporter.
*
* Returns: (transfer full) (element-type GstValidateReport): the list of
* #GstValidateReport present in the reporter.
* The caller should unref each report once it is done with them.
*/
GList *
gst_validate_reporter_get_reports (GstValidateReporter * reporter)
{
GstValidateReporterPrivate *priv;
GList *reports, *tmp;
GList *ret = NULL;
priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE);
GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter);
reports = g_hash_table_get_values (priv->reports);
for (tmp = reports; tmp; tmp = tmp->next) {
ret =
g_list_append (ret,
gst_validate_report_ref ((GstValidateReport *) (tmp->data)));
}
g_list_free (reports);
GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter);
return ret;
}
/**
* gst_validate_reporter_get_reports_count:
* @reporter: a #GstValidateReporter
*
* Get the number of reports present in the reporter.
*
* Returns: the number of reports currently present in @reporter.
*/
gint
gst_validate_reporter_get_reports_count (GstValidateReporter * reporter)
{
GstValidateReporterPrivate *priv;
gint ret;
priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE);
GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter);
ret = g_hash_table_size (priv->reports);
GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter);
return ret;
}
/**
* gst_validate_reporter_purge_reports:
* @reporter: a #GstValidateReporter
*
* Remove all the #GstValidateReport from @reporter. This should be called
* before unreffing the reporter to break cyclic references.
*/
void
gst_validate_reporter_purge_reports (GstValidateReporter * reporter)
{
GstValidateReporterPrivate *priv;
priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE);
GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter);
g_hash_table_remove_all (priv->reports);
GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter);
}
@@ -0,0 +1,158 @@
/* GStreamer
*
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _GST_VALIDATE_REPORTER_
#define _GST_VALIDATE_REPORTER_
typedef struct _GstValidateReporter GstValidateReporter;
typedef struct _GstValidateReporterInterface GstValidateReporterInterface;
#include <glib-object.h>
#include <gst/validate/validate-prelude.h>
#include <gst/validate/gst-validate-report.h>
#include <gst/validate/gst-validate-runner.h>
#include <gst/validate/gst-validate-enums.h>
#include <gst/validate/gst-validate-scenario.h>
G_BEGIN_DECLS
/* GstValidateReporter interface declarations */
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_REPORTER (gst_validate_reporter_get_type ())
#define GST_VALIDATE_REPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_REPORTER, GstValidateReporter))
#define GST_IS_VALIDATE_REPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_REPORTER))
#define GST_VALIDATE_REPORTER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_VALIDATE_REPORTER, GstValidateReporterInterface))
#define GST_VALIDATE_REPORTER_CAST(obj) ((GstValidateReporter *) obj)
#endif
/**
* GST_VALIDATE_REPORT:
* @m: The #GstValidateReporter where the issue happened
* @issue_id: The #GstValidateIssueId of the issue
* @...: The format of the message describing the issue in a printf
* format, followed by the parameters.
*
* Reports a new issue in the GstValidate reporting system with @m
* as the source of that issue.
*/
#ifdef G_HAVE_ISO_VARARGS
#define GST_VALIDATE_REPORT(m, issue_id, ...) \
G_STMT_START { \
gst_validate_report (GST_VALIDATE_REPORTER (m), \
issue_id, \
__VA_ARGS__ ); \
} G_STMT_END
#define GST_VALIDATE_REPORT_ACTION(m, a, issue_id, ...) \
G_STMT_START { \
gst_validate_report_action (GST_VALIDATE_REPORTER (m), a, \
issue_id, \
__VA_ARGS__ ); \
} G_STMT_END
#else /* G_HAVE_GNUC_VARARGS */
#ifdef G_HAVE_GNUC_VARARGS
#define GST_VALIDATE_REPORT(m, issue_id, args...) \
G_STMT_START { \
gst_validate_report (GST_VALIDATE_REPORTER (m), \
issue_id, ##args ); \
} G_STMT_END
#define GST_VALIDATE_REPORT_ACTION(m, a, issue_id, args...) \
G_STMT_START { \
gst_validate_report_action (GST_VALIDATE_REPORTER (m), a, \
issue_id, ##args ); \
} G_STMT_END
#endif /* G_HAVE_ISO_VARARGS */
#endif /* G_HAVE_GNUC_VARARGS */
GST_VALIDATE_API
GType gst_validate_reporter_get_type (void);
/**
* GstValidateInterceptionReturn:
* @GST_VALIDATE_REPORTER_DROP: The report will be completely ignored.
* @GST_VALIDATE_REPORTER_KEEP: The report will be kept by the reporter,
* but not reported to the runner.
* @GST_VALIDATE_REPORTER_REPORT: The report will be kept by the reporter
* and reported to the runner.
*/
typedef enum
{
GST_VALIDATE_REPORTER_DROP,
GST_VALIDATE_REPORTER_KEEP,
GST_VALIDATE_REPORTER_REPORT
} GstValidateInterceptionReturn;
/**
* GstValidateReporter:
*/
struct _GstValidateReporterInterface
{
GTypeInterface parent;
GstValidateInterceptionReturn (*intercept_report) (GstValidateReporter * reporter,
GstValidateReport * report);
GstValidateReportingDetails (*get_reporting_level) (GstValidateReporter * reporter);
GstPipeline * (*get_pipeline) (GstValidateReporter *reporter);
};
GST_VALIDATE_API
void gst_validate_reporter_set_name (GstValidateReporter * reporter,
gchar * name);
GST_VALIDATE_API
const gchar * gst_validate_reporter_get_name (GstValidateReporter * reporter);
GST_VALIDATE_API
GstValidateRunner * gst_validate_reporter_get_runner (GstValidateReporter *reporter);
GST_VALIDATE_API
void gst_validate_reporter_init (GstValidateReporter * reporter, const gchar *name);
GST_VALIDATE_API
void gst_validate_report (GstValidateReporter * reporter, GstValidateIssueId issue_id,
const gchar * format, ...) G_GNUC_PRINTF (3, 4) G_GNUC_NO_INSTRUMENT;
GST_VALIDATE_API
void gst_validate_report_action (GstValidateReporter * reporter,
GstValidateAction *action,
GstValidateIssueId issue_id,
const gchar * format, ...) G_GNUC_PRINTF (4, 5) G_GNUC_NO_INSTRUMENT;
GST_VALIDATE_API
void gst_validate_report_valist (GstValidateReporter * reporter, GstValidateIssueId issue_id,
const gchar * format, va_list var_args) G_GNUC_PRINTF (3, 0);
GST_VALIDATE_API void
gst_validate_reporter_report_simple (GstValidateReporter * reporter, GstValidateIssueId issue_id,
const gchar * message);
GST_VALIDATE_API
void gst_validate_reporter_set_runner (GstValidateReporter * reporter, GstValidateRunner *runner);
GST_VALIDATE_API
void gst_validate_reporter_set_handle_g_logs (GstValidateReporter * reporter);
GST_VALIDATE_API
GstValidateReport * gst_validate_reporter_get_report (GstValidateReporter *reporter,
GstValidateIssueId issue_id);
GST_VALIDATE_API
GList * gst_validate_reporter_get_reports (GstValidateReporter * reporter);
GST_VALIDATE_API
gint gst_validate_reporter_get_reports_count (GstValidateReporter *reporter);
GST_VALIDATE_API
GstValidateReportingDetails gst_validate_reporter_get_reporting_level (GstValidateReporter *reporter);
GST_VALIDATE_API
void gst_validate_reporter_purge_reports (GstValidateReporter * reporter);
GST_VALIDATE_API
GstPipeline * gst_validate_reporter_get_pipeline (GstValidateReporter * reporter);
G_END_DECLS
#endif /* _GST_VALIDATE_REPORTER_ */
@@ -0,0 +1,989 @@
/* GStreamer
*
* Copyright (C) 2013-2016 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* gst-validate-runner.c - Validate Runner class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "validate.h"
#include "gst-validate-internal.h"
#include "gst-validate-report.h"
#include "gst-validate-monitor-factory.h"
#include "gst-validate-override-registry.h"
#include "gst-validate-runner.h"
#include "gst-validate-reporter.h"
#include "gst-validate-mockdecryptor.h"
GST_DEBUG_CATEGORY_STATIC (gst_validate_runner_debug);
#undef GST_CAT_DEFAULT
#define GST_CAT_DEFAULT gst_validate_runner_debug
static gboolean element_created = FALSE;
/* We create a GstValidateRunner on _init ()
* so that we keep backward compatibility when
* the user create a Runner after creating the pipeline
* but the runner was actually already ready to be used.
*/
static GstValidateRunner *first_runner = NULL;
/**
* SECTION:gst-validate-runner
* @title: GstValidateRunner
* @short_description: Class that runs Gst Validate tests for a pipeline
*
* Allows you to test a pipeline within GstValidate. It is the object where
* all issue reporting is done.
*
* In the tools using GstValidate the only minimal code to be able to monitor
* your pipelines is:
*
* |[
* GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline");
* GstValidateRunner *runner = gst_validate_runner_new ();
* GstValidateMonitor *monitor = gst_validate_monitor_factory_create (
* GST_OBJECT (pipeline), runner, NULL);
*
* // Run the pipeline and do whatever you want with it
*
* // In that same order
* gst_object_unref (pipeline);
* gst_object_unref (runner);
* gst_object_unref (monitor);
* ]|
*
*/
struct _GstValidateRunnerPrivate
{
GMutex mutex;
GList *reports;
GstValidateReportingDetails default_level;
GHashTable *reports_by_type;
/* A list of PatternLevel */
GList *report_pattern_levels;
/* Whether the runner was create with GST_TRACERS=validate or not) */
gboolean user_created;
gchar *pipeline_names;
gchar **pipeline_names_strv;
GList *expected_issues;
};
/* Describes the reporting level to apply to a name pattern */
typedef struct _PatternLevel
{
GPatternSpec *pattern;
GstValidateReportingDetails level;
} PatternLevel;
#define GST_VALIDATE_RUNNER_LOCK(r) \
G_STMT_START { \
GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
(g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \
GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
} G_STMT_END
#define GST_VALIDATE_RUNNER_UNLOCK(r) \
G_STMT_START { \
GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
(g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \
GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
} G_STMT_END
#define gst_validate_runner_parent_class parent_class
G_DEFINE_TYPE_WITH_PRIVATE (GstValidateRunner, gst_validate_runner,
GST_TYPE_TRACER);
/* signals */
enum
{
REPORT_ADDED_SIGNAL,
STOPPING_SIGNAL,
/* add more above */
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_PARAMS,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
static guint _signals[LAST_SIGNAL] = { 0, };
static gboolean
gst_validate_runner_should_monitor (GstValidateRunner * self,
GstElement * element)
{
gint i;
GstValidateMonitor *monitor;
if (!GST_IS_PIPELINE (element)) {
return FALSE;
}
if (self->priv->user_created)
return FALSE;
if (!self->priv->pipeline_names_strv)
return TRUE;
monitor = gst_validate_get_monitor (G_OBJECT (element));
if (monitor) {
GST_ERROR_OBJECT (self, "Pipeline %" GST_PTR_FORMAT " is already"
" monitored by %" GST_PTR_FORMAT " using runner: %" GST_PTR_FORMAT
" NOT monitoring again.",
element, monitor,
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
}
for (i = 0; self->priv->pipeline_names_strv[i]; i++) {
if (g_pattern_match_simple (self->priv->pipeline_names_strv[i],
GST_OBJECT_NAME (element)))
return TRUE;
}
return FALSE;
}
static void
do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
{
element_created = TRUE;
if (gst_validate_runner_should_monitor (self, element)) {
/* the reference to the monitor is lost */
gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), self, NULL);
}
}
static gboolean
_parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
{
if (!str)
return FALSE;
/* works in place */
g_strstrip (str);
if (g_ascii_isdigit (str[0])) {
unsigned long l;
char *endptr;
l = strtoul (str, &endptr, 10);
if (endptr > str && endptr[0] == 0) {
*level = (GstValidateReportingDetails) l;
} else {
return FALSE;
}
} else if (g_ascii_strcasecmp (str, "smart") == 0) {
*level = GST_VALIDATE_SHOW_SMART;
} else if (g_ascii_strcasecmp (str, "none") == 0) {
*level = GST_VALIDATE_SHOW_NONE;
} else if (g_ascii_strcasecmp (str, "synthetic") == 0) {
*level = GST_VALIDATE_SHOW_SYNTHETIC;
} else if (g_ascii_strcasecmp (str, "subchain") == 0) {
*level = GST_VALIDATE_SHOW_SUBCHAIN;
} else if (g_ascii_strcasecmp (str, "monitor") == 0) {
*level = GST_VALIDATE_SHOW_MONITOR;
} else if (g_ascii_strcasecmp (str, "all") == 0) {
*level = GST_VALIDATE_SHOW_ALL;
} else
return FALSE;
return TRUE;
}
static void
_free_report_pattern_level (PatternLevel * pattern_level)
{
g_pattern_spec_free (pattern_level->pattern);
g_free (pattern_level);
}
static void
_set_reporting_level_for_name (GstValidateRunner * runner,
const gchar * pattern, GstValidateReportingDetails level)
{
PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel));
GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
pattern_level->pattern = pattern_spec;
pattern_level->level = level;
/* Allow the user to single out a pad with the "element-name__pad-name" syntax
*/
if (g_strrstr (pattern, "__"))
runner->priv->report_pattern_levels =
g_list_prepend (runner->priv->report_pattern_levels, pattern_level);
else
runner->priv->report_pattern_levels =
g_list_append (runner->priv->report_pattern_levels, pattern_level);
}
static void
_replace_double_colons (gchar * word)
{
while (word) {
word = strstr (word, "::");
if (word) {
word[0] = '_';
word[1] = '_';
}
}
}
static void
_set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
{
gchar **split;
gchar **walk;
g_assert (list);
GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
split = g_strsplit (list, ",", 0);
for (walk = split; *walk; walk++) {
_replace_double_colons (*walk);
if (strchr (*walk, ':')) {
gchar **values = g_strsplit (*walk, ":", 2);
if (values[0] && values[1]) {
GstValidateReportingDetails level;
if (_parse_reporting_level (values[1], &level))
_set_reporting_level_for_name (self, values[0], level);
}
g_strfreev (values);
} else {
GstValidateReportingDetails level;
if (_parse_reporting_level (*walk, &level))
self->priv->default_level = level;
}
}
g_strfreev (split);
}
static void
_init_report_levels (GstValidateRunner * self)
{
const gchar *env;
env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS");
if (env)
_set_report_levels_from_string (self, env);
}
static void
_unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
{
g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
}
static void
gst_validate_runner_finalize (GObject * object)
{
GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
if (!runner->priv->user_created)
gst_validate_runner_exit (runner, TRUE);
g_list_free_full (runner->priv->reports,
(GDestroyNotify) gst_validate_report_unref);
g_list_free_full (runner->priv->report_pattern_levels,
(GDestroyNotify) _free_report_pattern_level);
g_mutex_clear (&runner->priv->mutex);
g_free (runner->priv->pipeline_names);
g_strfreev (runner->priv->pipeline_names_strv);
g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc)
_unref_report_list, NULL);
g_hash_table_destroy (runner->priv->reports_by_type);
G_OBJECT_CLASS (parent_class)->finalize (object);
if (!runner->priv->user_created)
gst_validate_deinit ();
}
static GObject *
gst_validate_runner_constructor (GType type, guint n_construct_params,
GObjectConstructParam * construct_params)
{
GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
n_construct_params, construct_params);
if (!gst_validate_is_initialized ()) {
first_runner = GST_VALIDATE_RUNNER (runner);
gst_validate_init ();
first_runner = NULL;
return runner;
}
return runner;
}
static void
gst_validate_runner_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstValidateRunner *runner;
runner = GST_VALIDATE_RUNNER (object);
switch (prop_id) {
case PROP_PARAMS:
{
g_value_set_string (value, runner->priv->pipeline_names);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_runner_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstValidateRunner *runner;
runner = GST_VALIDATE_RUNNER (object);
switch (prop_id) {
case PROP_PARAMS:
{
g_free (runner->priv->pipeline_names);
g_strfreev (runner->priv->pipeline_names_strv);
runner->priv->pipeline_names = g_value_dup_string (value);
if (runner->priv->pipeline_names)
runner->priv->pipeline_names_strv =
g_strsplit (runner->priv->pipeline_names, ",", -1);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_runner_class_init (GstValidateRunnerClass * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gst_validate_runner_finalize;
gobject_class->set_property = gst_validate_runner_set_property;
gobject_class->get_property = gst_validate_runner_get_property;
gobject_class->constructor = gst_validate_runner_constructor;
properties[PROP_PARAMS] =
g_param_spec_string ("params", "Params", "Extra configuration parameters",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
_signals[REPORT_ADDED_SIGNAL] =
g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
GST_TYPE_VALIDATE_REPORT);
_signals[STOPPING_SIGNAL] =
g_signal_new ("stopping", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
NULL, NULL, NULL, G_TYPE_NONE, 0);
GST_DEBUG_CATEGORY_INIT (gst_validate_runner_debug, "gstvalidaterunner",
GST_DEBUG_FG_YELLOW, "Gst validate runner");
}
static void
gst_validate_runner_init (GstValidateRunner * runner)
{
runner->priv = gst_validate_runner_get_instance_private (runner);
runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
g_direct_equal);
runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
_init_report_levels (runner);
runner->priv->expected_issues = gst_validate_get_test_file_expected_issues ();
gst_tracing_register_hook (GST_TRACER (runner), "element-new",
G_CALLBACK (do_element_new));
gst_element_register (NULL, GST_MOCKDECRYPTOR_NAME, GST_RANK_MARGINAL,
GST_TYPE_MOCKDECRYPTOR);
}
/**
* gst_validate_runner_new:
*
* Create a new #GstValidateRunner
*
* Returns: A newly created #GstValidateRunner
*/
GstValidateRunner *
gst_validate_runner_new (void)
{
GstValidateRunner *runner;
if (first_runner) {
runner = first_runner;
first_runner = NULL;
} else if (element_created) {
gst_validate_abort
("Should never create a GstValidateRunner after a GstElement "
"has been created in the same process.");
return NULL;
} else {
runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
runner->priv->user_created = TRUE;
}
{
GstValidateOverrideRegistry *registry =
gst_validate_override_registry_get ();
GList *all_overrides =
gst_validate_override_registry_get_override_list (registry);
GList *i;
for (i = all_overrides; i; i = i->next) {
GstValidateOverride *override = (GstValidateOverride *) i->data;
gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (override),
runner);
}
g_list_free (all_overrides);
}
return runner;
}
/*
* gst_validate_runner_get_default_reporting_level:
*
* Returns: the default #GstValidateReportingDetails used to output a report.
*/
GstValidateReportingDetails
gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
{
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
GST_VALIDATE_SHOW_UNKNOWN);
return runner->priv->default_level;
}
#if !GLIB_CHECK_VERSION(2,70,0)
#define g_pattern_spec_match_string g_pattern_match_string
#endif
/*
* gst_validate_runner_get_reporting_level_for_name:
*
* Returns: the #GstValidateReportingDetails that will be applied for a given name.
* If no pattern was set for such a name, this function will return
* #GST_VALIDATE_SHOW_UNKNOWN, and reporting for that name will
* default to the global reporting level.
*/
GstValidateReportingDetails
gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
const gchar * name)
{
GList *tmp;
gchar *fixed_name;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
GST_VALIDATE_SHOW_UNKNOWN);
fixed_name = g_strdup (name);
_replace_double_colons (fixed_name);
for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) {
PatternLevel *pattern_level = (PatternLevel *) tmp->data;
if (g_pattern_spec_match_string (pattern_level->pattern, fixed_name)) {
g_free (fixed_name);
return pattern_level->level;
}
}
g_free (fixed_name);
return GST_VALIDATE_SHOW_UNKNOWN;
}
static void
synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
{
GstValidateIssueId issue_id;
GList *reports;
issue_id = report->issue->issue_id;
GST_VALIDATE_RUNNER_LOCK (runner);
reports =
g_hash_table_lookup (runner->priv->reports_by_type,
(gconstpointer) issue_id);
reports = g_list_append (reports, gst_validate_report_ref (report));
g_hash_table_insert (runner->priv->reports_by_type, (gpointer) issue_id,
reports);
GST_VALIDATE_RUNNER_UNLOCK (runner);
}
static void
_dot_pipeline (GstValidateReport * report, GstStructure * config)
{
GstPipeline *pipeline = gst_validate_reporter_get_pipeline (report->reporter);
if (pipeline) {
gint details = GST_DEBUG_GRAPH_SHOW_ALL;
gchar *reporter_basename =
g_path_get_basename (gst_validate_reporter_get_name (report->reporter));
report->dotfile_name =
g_strdup_printf ("%" GST_TIME_FORMAT "-validate-report-%s-on-%s-%s",
GST_TIME_ARGS (GST_CLOCK_DIFF (_priv_start_time,
gst_util_get_timestamp ())),
gst_validate_report_level_get_name (report->level), reporter_basename,
g_quark_to_string (report->issue->issue_id));
g_free (reporter_basename);
if (config)
gst_structure_get_int (config, "details", &details);
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline),
GST_DEBUG_GRAPH_SHOW_ALL, report->dotfile_name);
gst_object_unref (pipeline);
}
}
static void
gst_validate_runner_maybe_dot_pipeline (GstValidateRunner * runner,
GstValidateReport * report)
{
GList *config;
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL ||
gst_validate_report_check_abort (report)) {
_dot_pipeline (report, NULL);
return;
}
for (config = gst_validate_plugin_get_config (NULL);
config; config = config->next) {
if (gst_structure_has_name (config->data, "core")) {
GstValidateReportLevel level;
const gchar *level_str,
*action = gst_structure_get_string (config->data, "action");
if (g_strcmp0 (action, "dot-pipeline"))
continue;
level_str = gst_structure_get_string (config->data, "report-level");
level = level_str ? gst_validate_report_level_from_name (level_str) :
GST_VALIDATE_REPORT_LEVEL_CRITICAL;
if (level >= report->level) {
_dot_pipeline (report, config->data);
return;
}
}
}
}
static gboolean
check_report_expected (GstValidateRunner * runner, GstValidateReport * report)
{
GList *tmp;
#define GET_STR(name) gst_structure_get_string (known_issue, name)
for (tmp = runner->priv->expected_issues; tmp; tmp = tmp->next) {
GstStructure *known_issue = tmp->data;
const gchar *id = GET_STR ("issue-id");
if (!id || g_quark_from_string (id) == report->issue->issue_id) {
const gchar *summary = GET_STR ("summary");
if (!summary || !g_strcmp0 (summary, report->issue->summary)) {
const gchar *details = GET_STR ("details");
if (!details || g_regex_match_simple (details, report->message, 0, 0)) {
const gchar *detected_on = GET_STR ("detected-on");
if (!detected_on || !g_strcmp0 (detected_on, report->reporter_name)) {
const gchar *level = GET_STR ("level");
const gchar *report_level =
gst_validate_report_level_get_name (report->level);
if (!detected_on || !g_strcmp0 (level, report_level)) {
gboolean is_sometimes;
if (!gst_structure_get_boolean (known_issue, "sometimes",
&is_sometimes) || !is_sometimes) {
runner->priv->expected_issues =
g_list_remove (runner->priv->expected_issues, known_issue);
gst_structure_free (known_issue);
}
return TRUE;
}
}
}
}
}
#undef GET_STR
}
return FALSE;
}
void
gst_validate_runner_add_report (GstValidateRunner * runner,
GstValidateReport * report)
{
GstValidateReportingDetails details, reporter_details, issue_type_details;
g_return_if_fail (GST_IS_VALIDATE_RUNNER (runner));
if (report->level == GST_VALIDATE_REPORT_LEVEL_IGNORE)
return;
if (check_report_expected (runner, report)) {
GST_INFO_OBJECT (runner, "Found expected issue: %p", report);
report->level = GST_VALIDATE_REPORT_LEVEL_EXPECTED;
}
gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
report));
gst_validate_runner_maybe_dot_pipeline (runner, report);
details = reporter_details =
gst_validate_reporter_get_reporting_level (report->reporter);
issue_type_details =
gst_validate_runner_get_reporting_level_for_name (runner,
g_quark_to_string (report->issue->issue_id));
if (reporter_details == GST_VALIDATE_SHOW_UNKNOWN)
details = issue_type_details;
/* Let's use our own reporting strategy */
if (details == GST_VALIDATE_SHOW_UNKNOWN) {
gst_validate_report_set_reporting_level (report,
runner->priv->default_level);
switch (runner->priv->default_level) {
case GST_VALIDATE_SHOW_NONE:
return;
case GST_VALIDATE_SHOW_SMART:
if (!gst_validate_report_check_abort (report) &&
report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
!report->trace) {
synthesize_reports (runner, report);
return;
}
break;
case GST_VALIDATE_SHOW_SYNTHETIC:
if (!report->trace) {
synthesize_reports (runner, report);
return;
}
default:
break;
}
} else if (details == GST_VALIDATE_SHOW_NONE) {
GST_DEBUG ("Not reporting.");
return;
}
GST_VALIDATE_RUNNER_LOCK (runner);
runner->priv->reports =
g_list_append (runner->priv->reports, gst_validate_report_ref (report));
GST_VALIDATE_RUNNER_UNLOCK (runner);
g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
}
/**
* gst_validate_runner_get_reports_count:
* @runner: The $GstValidateRunner to get the number of reports from
*
* Get the number of reports present in the runner:
*
* Returns: The number of reports present in the runner.
*/
guint
gst_validate_runner_get_reports_count (GstValidateRunner * runner)
{
GList *tmp;
guint l;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 0);
GST_VALIDATE_RUNNER_LOCK (runner);
l = g_list_length (runner->priv->reports);
for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
GstValidateReport *report = (GstValidateReport *) tmp->data;
l += g_list_length (report->repeated_reports);
}
l += g_hash_table_size (runner->priv->reports_by_type);
GST_VALIDATE_RUNNER_UNLOCK (runner);
return l;
}
/**
* gst_validate_runner_get_reports:
* @runner: The #GstValidateRunner
*
* Return: (element-type GstValidateReport)(transfer full): all the reports
*/
GList *
gst_validate_runner_get_reports (GstValidateRunner * runner)
{
GList *ret;
GST_VALIDATE_RUNNER_LOCK (runner);
ret =
g_list_copy_deep (runner->priv->reports,
(GCopyFunc) gst_validate_report_ref, NULL);
GST_VALIDATE_RUNNER_UNLOCK (runner);
return ret;
}
static GList *
_do_report_synthesis (GstValidateRunner * runner)
{
GHashTableIter iter;
GList *reports, *tmp;
gpointer key, value;
GList *criticals = NULL;
/* Take the lock so the hash table won't be modified while we are iterating
* over it */
GST_VALIDATE_RUNNER_LOCK (runner);
g_hash_table_iter_init (&iter, runner->priv->reports_by_type);
while (g_hash_table_iter_next (&iter, &key, &value)) {
GstValidateReport *report;
reports = (GList *) value;
if (!reports)
continue;
report = (GstValidateReport *) (reports->data);
gst_validate_report_print_level (report);
gst_validate_report_print_detected_on (report);
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
criticals = g_list_append (criticals, report);
gst_validate_report_print_details (report);
} else if (report->issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)
gst_validate_report_print_details (report);
for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
report = (GstValidateReport *) tmp->data;
gst_validate_report_print_detected_on (report);
if ((report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) ||
(report->issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)) {
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
criticals = g_list_append (criticals, report);
gst_validate_report_print_details (report);
}
}
report = (GstValidateReport *) (reports->data);
gst_validate_report_print_description (report);
gst_validate_printf (NULL, "\n");
}
GST_VALIDATE_RUNNER_UNLOCK (runner);
return criticals;
}
/**
* gst_validate_runner_printf:
* @runner: The #GstValidateRunner to print all the reports for
*
* Prints all the reports on the terminal or on wherever is set
* in the `GST_VALIDATE_FILE` env variable.
*
* Returns: 0 if no critical error has been found and 18 if a critical
* error has been detected. That return value is usually to be used as
* exit code of the application.
*/
int
gst_validate_runner_printf (GstValidateRunner * runner)
{
GList *reports, *tmp;
int ret = 0;
GList *criticals = NULL;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
criticals = _do_report_synthesis (runner);
reports = gst_validate_runner_get_reports (runner);
for (tmp = reports; tmp; tmp = tmp->next) {
GstValidateReport *report = (GstValidateReport *) tmp->data;
if (gst_validate_report_should_print (report))
gst_validate_report_printf (report);
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
criticals = g_list_append (criticals, report);
}
}
if (criticals) {
GList *iter;
g_printerr ("\n\n**Got criticals. Return value set to 18**:\n");
ret = 18;
for (iter = criticals; iter; iter = iter->next) {
g_printerr (" * critical error %s\n",
((GstValidateReport *) (iter->data))->message);
}
g_printerr ("\n");
}
g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
g_list_free (criticals);
gst_validate_printf (NULL, "Issues found: %u\n",
gst_validate_runner_get_reports_count (runner));
return ret;
}
int
gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
{
gint ret = 0;
GList *tmp, *configs;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
if (print_result) {
ret = gst_validate_runner_printf (runner);
} else {
GList *tmp;
for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
GstValidateReport *report = (GstValidateReport *) tmp->data;
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
ret = 18;
}
}
configs = gst_validate_get_config (NULL);
for (tmp = configs; tmp; tmp = tmp->next) {
if (!gst_structure_has_field (tmp->data, "__n_usages__")) {
gst_validate_error_structure (tmp->data,
"Unused config: '%" GST_PTR_FORMAT "'", tmp->data);
}
}
g_list_free (configs);
for (tmp = runner->priv->expected_issues; tmp; tmp = tmp->next) {
GstStructure *known_issue = tmp->data;
gboolean is_sometimes;
if (!gst_structure_get_boolean (known_issue, "sometimes", &is_sometimes)
|| !is_sometimes) {
GstStructure *tmpstruct = gst_structure_copy (known_issue);
gst_structure_remove_fields (tmpstruct, "__debug__", "__lineno__",
"__filename__", NULL);
/* Ideally we should report an issue here.. but we do not have a reporter */
gst_validate_error_structure (known_issue,
"Expected issue didn't happen: '%" GST_PTR_FORMAT "'", tmpstruct);
gst_structure_free (tmpstruct);
}
}
g_list_free_full (runner->priv->expected_issues,
(GDestroyNotify) gst_structure_free);
runner->priv->expected_issues = NULL;
return ret;
}
void
gst_validate_init_runner (void)
{
if (!first_runner) {
first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
first_runner->priv->user_created = TRUE;
} /* else the first runner has been created through the GST_TRACERS system */
}
void
gst_validate_deinit_runner (void)
{
g_clear_object (&first_runner);
}
GstValidateReportingDetails
gst_validate_runner_get_default_reporting_details (GstValidateRunner * runner)
{
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
GST_VALIDATE_SHOW_UNKNOWN);
return runner->priv->default_level;
}
#ifdef __GST_VALIDATE_PLUGIN
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
"GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
#endif /* __GST_VALIDATE_PLUGIN */
@@ -0,0 +1,104 @@
/* GStreamer
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-runner.h - Validate Runner class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_RUNNER_H__
#define __GST_VALIDATE_RUNNER_H__
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/gsttracer.h>
typedef struct _GstValidateRunner GstValidateRunner;
typedef struct _GstValidateRunnerClass GstValidateRunnerClass;
#include <gst/validate/gst-validate-report.h>
#include <gst/validate/gst-validate-enums.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_RUNNER (gst_validate_runner_get_type ())
#define GST_IS_VALIDATE_RUNNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_RUNNER))
#define GST_IS_VALIDATE_RUNNER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_RUNNER))
#define GST_VALIDATE_RUNNER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_RUNNER, GstValidateRunnerClass))
#define GST_VALIDATE_RUNNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_RUNNER, GstValidateRunner))
#define GST_VALIDATE_RUNNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_RUNNER, GstValidateRunnerClass))
#define GST_VALIDATE_RUNNER_CAST(obj) ((GstValidateRunner*)(obj))
#define GST_VALIDATE_RUNNER_CLASS_CAST(klass) ((GstValidateRunnerClass*)(klass))
#endif
typedef struct _GstValidateRunnerPrivate GstValidateRunnerPrivate;
/**
* GstValidateRunner:
*
* GStreamer Validate Runner class.
*
* Class that manages a Validate test run for some pipeline
*/
struct _GstValidateRunner {
GstTracer object;
/* <private> */
GstValidateRunnerPrivate *priv;
};
/**
* GstValidateRunnerClass:
* @parent_class: parent
*
* GStreamer Validate Runner object class.
*/
struct _GstValidateRunnerClass {
GstTracerClass parent_class;
};
/* normal GObject stuff */
GST_VALIDATE_API
GType gst_validate_runner_get_type (void);
GST_VALIDATE_API
GstValidateRunner * gst_validate_runner_new (void);
GST_VALIDATE_API
void gst_validate_runner_add_report (GstValidateRunner * runner, GstValidateReport * report);
GST_VALIDATE_API
guint gst_validate_runner_get_reports_count (GstValidateRunner * runner);
GST_VALIDATE_API
GList * gst_validate_runner_get_reports (GstValidateRunner * runner);
GST_VALIDATE_API
int gst_validate_runner_printf (GstValidateRunner * runner);
GST_VALIDATE_API
int gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result);
GST_VALIDATE_API
GstValidateReportingDetails gst_validate_runner_get_default_reporting_level (GstValidateRunner *runner);
GST_VALIDATE_API
GstValidateReportingDetails gst_validate_runner_get_reporting_level_for_name (GstValidateRunner *runner,
const gchar *name);
G_END_DECLS
#endif /* __GST_VALIDATE_RUNNER_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,425 @@
/* GStreamer
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
*
* gst-validate-runner.c - Validate Runner class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_SCENARIO_H__
#define __GST_VALIDATE_SCENARIO_H__
#include <glib.h>
#include <glib-object.h>
#include "gst-validate-types.h"
#include <gst/validate/gst-validate-runner.h>
G_BEGIN_DECLS
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_SCENARIO (gst_validate_scenario_get_type ())
#define GST_VALIDATE_SCENARIO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenario))
#define GST_VALIDATE_SCENARIO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenarioClass))
#define GST_IS_VALIDATE_SCENARIO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_SCENARIO))
#define GST_IS_VALIDATE_SCENARIO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_SCENARIO))
#define GST_VALIDATE_SCENARIO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenarioClass))
#endif
typedef struct _GstValidateScenarioPrivate GstValidateScenarioPrivate;
typedef struct _GstValidateActionParameter GstValidateActionParameter;
/**
* GstValidateActionReturn:
* GST_VALIDATE_EXECUTE_ACTION_ERROR:
* GST_VALIDATE_EXECUTE_ACTION_OK:
* GST_VALIDATE_EXECUTE_ACTION_ASYNC:
* GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
* GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
* GST_VALIDATE_EXECUTE_ACTION_NONE:
* GST_VALIDATE_EXECUTE_ACTION_DONE:
*/
typedef enum
{
GST_VALIDATE_EXECUTE_ACTION_ERROR,
GST_VALIDATE_EXECUTE_ACTION_OK,
GST_VALIDATE_EXECUTE_ACTION_ASYNC,
/**
* GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
*
* The action will be executed asynchronously without blocking further
* actions to be executed
*
* Since: 1.20
*/
GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING,
/**
* GST_VALIDATE_EXECUTE_ACTION_INTERLACED:
*
* Deprecated: 1.20: Use #GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING instead.
*/
GST_VALIDATE_EXECUTE_ACTION_INTERLACED = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING,
GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED,
GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS,
GST_VALIDATE_EXECUTE_ACTION_NONE,
GST_VALIDATE_EXECUTE_ACTION_DONE,
} GstValidateActionReturn;
const gchar *gst_validate_action_return_get_name (GstValidateActionReturn r);
/* TODO 2.0 -- Make it an actual enum type */
#define GstValidateExecuteActionReturn gint
/**
* GstValidateExecuteAction:
* @scenario: The #GstValidateScenario from which the @action is executed
* @action: The #GstValidateAction being executed
*
* A function that executes a #GstValidateAction
*
* Returns: a #GstValidateExecuteActionReturn
*/
typedef GstValidateExecuteActionReturn (*GstValidateExecuteAction) (GstValidateScenario * scenario, GstValidateAction * action);
/**
* GstValidatePrepareAction:
* @action: The #GstValidateAction to prepare before execution
*
* A function that prepares @action so it can be executed right after.
* Most of the time this function is used to parse and set fields with
* equations in the action structure.
*
* Returns: %TRUE if the action could be prepared and is ready to be run
* , %FALSE otherwise
*/
typedef GstValidateExecuteActionReturn (*GstValidatePrepareAction) (GstValidateAction * action);
typedef struct _GstValidateActionPrivate GstValidateActionPrivate;
#define GST_VALIDATE_ACTION_LINENO(action) (((GstValidateAction*) action)->ABI.abi.lineno)
#define GST_VALIDATE_ACTION_FILENAME(action) (((GstValidateAction*) action)->ABI.abi.filename)
#define GST_VALIDATE_ACTION_DEBUG(action) (((GstValidateAction*) action)->ABI.abi.debug)
#define GST_VALIDATE_ACTION_N_REPEATS(action) (((GstValidateAction*) action)->ABI.abi.n_repeats)
#define GST_VALIDATE_ACTION_RANGE_NAME(action) (((GstValidateAction*) action)->ABI.abi.rangename)
/**
* GstValidateAction:
* @type: The type of the #GstValidateAction, which is the name of the
* GstValidateActionType registered with
* #gst_validate_register_action_type
* @name: The name of the action, set from the user in the scenario
* @structure: the #GstStructure defining the action
* @scenario: The scenario for this action. This is not thread-safe
* and should be accessed exclusively from the main thread.
* If you need to access it from another thread use the
* #gst_validate_action_get_scenario method
*
* The GstValidateAction defined to be executed as part of a scenario
*
* Only access it from the default main context.
*/
struct _GstValidateAction
{
GstMiniObject mini_object;
/*< public > */
const gchar *type;
const gchar *name;
GstStructure *structure;
/* < private > */
guint action_number;
gint repeat;
GstClockTime playback_time;
GstValidateActionPrivate *priv;
union {
gpointer _gst_reserved[GST_PADDING_LARGE - 1]; /* ->priv */
struct {
gint lineno;
gchar *filename;
gchar *debug;
gint n_repeats;
const gchar *rangename;
} abi;
} ABI;
};
GST_VALIDATE_API
void gst_validate_action_set_done (GstValidateAction *action);
GST_VALIDATE_API
GstValidateScenario * gst_validate_action_get_scenario (GstValidateAction *action);
GST_VALIDATE_API
GstValidateAction * gst_validate_action_new (GstValidateScenario * scenario,
GstValidateActionType * action_type,
GstStructure *structure,
gboolean add_to_lists);
GST_VALIDATE_API
GstValidateAction* gst_validate_action_ref (GstValidateAction * action);
GST_VALIDATE_API
void gst_validate_action_unref (GstValidateAction * action);
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_ACTION (gst_validate_action_get_type ())
#define GST_IS_VALIDATE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_ACTION))
#define GST_VALIDATE_ACTION_GET_TYPE(obj) ((GstValidateActionType*)gst_validate_get_action_type(((GstValidateAction*)obj)->type))
#endif
GST_VALIDATE_API
GType gst_validate_action_get_type (void);
/**
* GstValidateActionTypeFlags:
* @GST_VALIDATE_ACTION_TYPE_NONE: No special flag
* @GST_VALIDATE_ACTION_TYPE_CONFIG: The action is a config
* @GST_VALIDATE_ACTION_TYPE_ASYNC: The action can be executed ASYNC
* @GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION: The action will be executed on 'element-added'
* for a particular element type if no playback-time
* is specified
* @GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK: The pipeline will need to be synchronized with the clock
* for that action type to be used.
* @GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL: Do not consider the non execution of the action
* as a fatal error.
* @GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL: The action can use the 'optional' keyword. Such action
* instances will have the #GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL
* flag set and won't be considered as fatal if they fail.
* @GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG: The action can be used in config files even if it is not strictly a config
* action (ie. it needs a scenario to run).
*/
typedef enum
{
GST_VALIDATE_ACTION_TYPE_NONE = 0,
GST_VALIDATE_ACTION_TYPE_CONFIG = 1 << 1,
GST_VALIDATE_ACTION_TYPE_ASYNC = 1 << 2,
GST_VALIDATE_ACTION_TYPE_NON_BLOCKING = 1 << 3,
/**
* GST_VALIDATE_ACTION_TYPE_INTERLACED:
*
* Deprecated: 1.20: Use #GST_VALIDATE_ACTION_TYPE_NON_BLOCKING instead.
*/
GST_VALIDATE_ACTION_TYPE_INTERLACED = 1 << 3,
/**
* GST_VALIDATE_ACTION_TYPE_NON_BLOCKING:
*
* The action can be executed asynchronously but without blocking further
* actions execution.
*
* Since: 1.20
*/
GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION = 1 << 4,
GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK = 1 << 5,
GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL = 1 << 6,
GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL = 1 << 7,
GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE = 1 << 8,
GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG = 1 << 9,
} GstValidateActionTypeFlags;
typedef struct _GstValidateActionTypePrivate GstValidateActionTypePrivate;
/**
* GstValidateActionType:
* @name: The name of the new action type to add
* @implementer_namespace: The namespace of the implementer of the action type
* @execute: The function to be called to execute the action
* @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
* @description: A description of the new type
* @flags: The flags of the action type
*/
struct _GstValidateActionType
{
GstMiniObject mini_object;
gchar *name;
gchar *implementer_namespace;
GstValidatePrepareAction prepare;
GstValidateExecuteAction execute;
GstValidateActionParameter *parameters;
gchar *description;
GstValidateActionTypeFlags flags;
GstRank rank;
GstValidateActionType *overriden_type;
GstValidateActionTypePrivate* priv;
/*< private >*/
gpointer _gst_reserved[GST_PADDING_LARGE - sizeof (GstRank) - 2];
};
#ifndef __GI_SCANNER__
#define GST_TYPE_VALIDATE_ACTION_TYPE (gst_validate_action_type_get_type ())
#define GST_IS_VALIDATE_ACTION_TYPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_ACTION_TYPE))
#define GST_VALIDATE_ACTION_TYPE(obj) ((GstValidateActionType*) obj)
#endif
GST_VALIDATE_API
GType gst_validate_action_type_get_type (void);
GST_VALIDATE_API
gboolean gst_validate_print_action_types (const gchar ** wanted_types, gint num_wanted_types);
/**
* GstValidateActionParameter:
* @name: The name of the parameter
* @description: The description of the parameter
* @mandatory: Whether the parameter is mandatory for
* a specific action type
* @types: The types the parameter can take described as a
* string. It can be precisely describing how the typing works
* using '\n' between the various acceptable types.
* NOTE: The types should end with `(GstClockTime)` if its final
* type is a GstClockTime, this way it will be processed when preparing
* the actions.
* @possible_variables: The name of the variables that can be
* used to compute the value of the parameter.
* For example for the start value of a seek
* action, we will accept to take 'duration'
* which will be replace by the total duration
* of the stream on which the action is executed.
* @def: The default value of a parameter as a string, should be %NULL
* for mandatory streams.
*/
struct _GstValidateActionParameter
{
const gchar *name;
const gchar *description;
gboolean mandatory;
const gchar *types;
const gchar *possible_variables;
const gchar *def;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstValidateScenarioClass
{
GstObjectClass parent_class;
/*< public >*/
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
/**
* GstValidateScenario:
*/
struct _GstValidateScenario
{
GstObject parent;
/*< public >*/
GstStructure *description;
/*< private >*/
GstValidateScenarioPrivate *priv;
union {
gpointer _gst_reserved[GST_PADDING];
struct {
GMutex eos_handling_lock;
} abi;
} ABI;
};
/* Some actions may trigger EOS during their execution. Unlocked this
* could cause a race condition as the main thread may terminate the test
* in response to the EOS message in the bus while the action is still
* going in a different thread.
* To avoid this, the handling of the EOS message is protected with this
* lock. Actions expecting to cause an EOS can hold the lock for their
* duration so that they are guaranteed to finish before the EOS
* terminates the test. */
#define GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK(scenario) (g_mutex_lock(&(scenario)->ABI.abi.eos_handling_lock))
#define GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK(scenario) (g_mutex_unlock(&(scenario)->ABI.abi.eos_handling_lock))
GST_VALIDATE_API
GType gst_validate_scenario_get_type (void);
GST_VALIDATE_API
GstValidateScenario * gst_validate_scenario_factory_create (GstValidateRunner *runner,
GstElement *pipeline,
const gchar *scenario_name);
GST_VALIDATE_API gboolean
gst_validate_list_scenarios (gchar **scenarios,
gint num_scenarios,
gchar * output_file);
GST_VALIDATE_API GstValidateActionType *
gst_validate_get_action_type (const gchar *type_name);
GST_VALIDATE_API GstValidateActionType *
gst_validate_register_action_type (const gchar *type_name,
const gchar *implementer_namespace,
GstValidateExecuteAction function,
GstValidateActionParameter * parameters,
const gchar *description,
GstValidateActionTypeFlags flags);
GST_VALIDATE_API GstValidateActionType *
gst_validate_register_action_type_dynamic (GstPlugin *plugin,
const gchar * type_name,
GstRank rank,
GstValidateExecuteAction function,
GstValidateActionParameter * parameters,
const gchar * description,
GstValidateActionTypeFlags flags);
GST_VALIDATE_API
gboolean gst_validate_action_get_clocktime (GstValidateScenario * scenario,
GstValidateAction *action,
const gchar * name,
GstClockTime * retval);
GST_VALIDATE_API GstValidateExecuteActionReturn
gst_validate_scenario_execute_seek (GstValidateScenario *scenario,
GstValidateAction *action,
gdouble rate,
GstFormat format,
GstSeekFlags flags,
GstSeekType start_type,
GstClockTime start,
GstSeekType stop_type,
GstClockTime stop);
GST_VALIDATE_API GList *
gst_validate_scenario_get_actions (GstValidateScenario *scenario);
GST_VALIDATE_API GstValidateExecuteActionReturn
gst_validate_execute_action (GstValidateActionType * action_type,
GstValidateAction * action);
GST_VALIDATE_API GstState
gst_validate_scenario_get_target_state (GstValidateScenario *scenario);
GST_VALIDATE_API GstElement *
gst_validate_scenario_get_pipeline (GstValidateScenario * scenario);
GST_VALIDATE_API
void gst_validate_scenario_deinit (void);
G_END_DECLS
#endif /* __GST_VALIDATE_SCENARIOS__ */
@@ -0,0 +1,30 @@
/* GStreamer
*
* Copyright (C) 2015 Thibault Saunier <thibault.saunier@collabora.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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_VALIDATE_TYPES_H__
#define __GST_VALIDATE_TYPES_H__
typedef struct _GstValidateScenario GstValidateScenario;
typedef struct _GstValidateScenarioClass GstValidateScenarioClass;
typedef struct _GstValidateAction GstValidateAction;
typedef struct _GstValidateActionType GstValidateActionType;
#endif /* __GST_VALIDATE_TYPES_ */
File diff suppressed because it is too large Load Diff

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