--- /dev/null
+{
+ "phabricator.uri" : "https://phabricator.freedesktop.org/",
+ "repository.callsign" : "GES",
+ "project": "GStreamer Editing Services",
+ "default-reviewers": "thiblahute,Mathieu_Du"
+}
--- /dev/null
+*.o
+*.bak
+*.orig
+*.diff
+*.patch
+*.so
+*.a
+*.la
+*.lo
+*.pyc
+*.page
+*.swp
+build*
+*~
+core.*
+
+Makefile
+Makefile.in
+core
+log
+.deps
+.libs
+.dirstamp
+
+/INSTALL
+
+/aclocal.m4
+/autom4te.cache
+/autoregen.sh
+/compile
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.status
+/config.sub
+/configure
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/py-compile
+/stamp-h.in
+/stamp-h1
+
+/ges/gesmarshal.h
+/ges/gesmarshal.c
+/ges/ges-version.h
+
+/docs/version.entities
+
+/bindings/python/ges.c
+
+/ges/lex.priv_ges_parse_yy.c
+/ges/ges-parse-lex.h
+
+/m4
+
+/test-driver
+/tests/check/test-registry.reg
+/tests/check/*.log
+/tests/check/*.trs
+/tests/check/*/*.log
+/tests/check/*/*.trs
+
+/tests/check/ges/group
+/tests/check/ges/material
+/tests/check/ges/mixers
+/tests/check/ges/timelineedition
+/tests/check/ges/timelinegroup
+/tests/check/ges/tempochange
+/tests/check/ges/uriclip
+/tests/check/integration
+/tests/benchmarks/timeline
+/tests/examples/materials
+
+/tests/check/nle/simple
+/tests/check/nle/complex
+/tests/check/nle/nlecomposition
+/tests/check/nle/nleoperation
+/tests/check/nle/nlesource
+/tests/check/nle/tempochange
+
+/tools/ges-launch-1.0
+/tests/check/ges/project
+/tests/examples/assets
+
+/tests/check/coverage/
+*gcno
+*BACKUP*
+*REMOTE*
+*LOCAL*
+*BASE*
+*anjuta*
--- /dev/null
+include: "https://gitlab.freedesktop.org/gstreamer/gst-ci/raw/1.16/gitlab/ci_template.yml"
--- /dev/null
+[submodule "common"]
+ path = common
+ url = https://gitlab.freedesktop.org/gstreamer/common.git
--- /dev/null
+Edward Hervey <edward.hervey@collabora.co.uk>
+Brandon Lewis <brandon.lewis@collabora.co.uk>
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 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 library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ 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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the 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 a program 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.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ 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, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 6. As an exception to the Sections above, you may also compile 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) 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.
+
+ c) 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.
+
+ d) 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 source code 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.
+\f
+ 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 to
+this License.
+\f
+ 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 Library 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.
+\f
+ 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
+\f
+ Appendix: 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 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.
+
+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!
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 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 library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ 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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the 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 a program 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.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ 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, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 6. As an exception to the Sections above, you may also compile 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) 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.
+
+ c) 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.
+
+ d) 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 source code 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.
+\f
+ 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 to
+this License.
+\f
+ 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 Library 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.
+\f
+ 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
+\f
+ Appendix: 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 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.
+
+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!
--- /dev/null
+=== release 1.16.2 ===
+
+2019-12-03 11:17:11 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.16.2
+
+2019-10-01 18:02:27 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-internal.h:
+ * ges/ges.c:
+ ges: Hide internal debug category behind a GOnce
+ Otherwise it might be used (e.g. by the plugin loader via the GES
+ plugin!) before ges_init() is called.
+
+=== release 1.16.1 ===
+
+2019-09-23 11:18:40 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.16.1
+
+2019-05-26 09:55:03 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-validate.c:
+ validate: Add action type to copy/paste clips
+
+2019-05-25 20:20:07 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-container.c:
+ * tests/check/python/test_timeline.py:
+ container: Handle children pasting failures
+
+2019-05-25 18:51:08 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * tests/check/python/test_timeline.py:
+ clip: Fix layer managament when copying a clip that was pasted
+
+2019-05-25 16:05:00 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-timeline-element.c:
+ * tests/check/python/test_timeline.py:
+ element: Properly handle the fact that pasting can return NULL
+ And fix paste annotation
+
+2019-05-16 15:06:14 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges.c:
+ ges: Sprinkle around some Since: 1.16 markers
+
+2019-05-05 11:38:28 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * tools/ges-launcher.c:
+ launch: Fix caps restriction short names
+
+2019-05-04 10:47:07 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * bindings/python/gi/overrides/GES.py:
+ * tests/check/python/test_timeline.py:
+ python: Avoid warning about using deprecated methods
+ Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/69
+
+2019-05-02 12:35:36 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * .gitlab-ci.yml:
+ ci: use template from 1.16 branch
+
+2019-04-20 01:36:10 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * plugins/ges/meson.build:
+ meson: Generate a pkgconfig file for the GES plugin
+ This was missing due to a typo.
+
+=== release 1.16.0 ===
+
+2019-04-19 00:35:57 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.16.0
+
+2019-04-18 16:44:31 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ asset: Avoid unrefing a task we do not own
+
+2019-04-17 23:53:14 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-clip.c:
+ clip: Optimize set_top_effect_index by checking parent sooner
+
+2019-04-17 23:51:13 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-clip.c:
+ clip: Return TRUE when the the effect index does not change
+
+2019-04-13 20:03:52 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-clip.c:
+ clip: Remove obsolete FIXME
+
+2019-04-11 23:58:48 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-container.c:
+ container: Call _remove_child when cannot set parent
+ ges_container_add removes the child being added if the call to
+ ges_timeline_element_set_parent fails. In this case, subclasses should
+ be given the chance to revert the effects of the add_child vmethod which
+ has just been called.
+
+2019-04-11 23:45:13 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-container.c:
+ ges: Remove unused nb_effects field
+
+2019-04-12 17:30:14 +0300 Mart Raudsepp <mart.raudsepp@collabora.com>
+
+ * ges/ges-track.c:
+ track: Avoid various sorting operations before timeline commit
+ These are showing up in performance profile of 1000+ clips looped addition.
+ All this is done at commit time as well, so let that do only one update and
+ sorting.
+
+2019-04-15 17:03:49 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ asset: Do not take an extra ref on asset when already initialized
+ The task already has a ref so this one doesn't make sense and leads to leaks
+
+2019-04-12 18:31:07 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-project.c:
+ * tests/check/ges/project.c:
+ xml-formatter: Fix some asset leaks
+
+2019-04-09 08:58:24 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/ges/clip.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/mixers.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/timelineedition.c:
+ tests: Plug misc leaks
+
+2019-04-09 08:56:49 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/gstframepositioner.c:
+ framepositioner: Plug caps leak
+
+2019-04-09 08:56:08 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Plug some leaks
+
+2019-04-08 16:25:59 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline.c:
+ timeline: Plug leak of the auto transition asset
+
+2019-04-08 16:25:44 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline-tree.c:
+ tree: Plug a GList leak
+
+2019-04-08 16:25:29 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Plug pad leak
+
+2019-04-08 16:23:18 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-title-source.c:
+ * tests/check/ges/titles.c:
+ title: Deprecate method that return newly allocated `const gchar*`
+ This is just plain broken 190643508f14a64e36f085a69de819505e79dadb
+ but we can't do anything about it.
+
+2019-04-05 11:24:39 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-project.c:
+ Plug some GError leaks when loading assets
+
+2019-04-01 11:52:43 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ xml-formatter: Free pending clips on disposal
+
+2019-03-28 13:51:36 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ asset: Plug a GError leak
+
+2019-03-28 13:08:55 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/ges/mixers.c:
+ tests: Avoid random timeout and let the launcher set it up for us
+
+2019-03-28 13:08:01 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ asset: s/unsure/ensure
+
+2019-03-28 13:06:37 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-project.c:
+ asset: Plug a leak of EncodingProfiles
+
+2019-03-28 13:06:16 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * tests/check/ges/group.c:
+ xml-formatter: Plug leaks of pending groups
+
+2019-03-28 13:05:45 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ asset: plug a GTask leak
+
+2019-03-28 11:29:05 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-track-element.c:
+ * tests/check/ges/clip.c:
+ Fix splitting control bindings leaks
+
+2019-03-28 11:09:13 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/ges/asset.c:
+ tests: Fix a leak in the 'asset' test
+
+2019-03-28 11:08:58 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline-tree.c:
+ tree: Fixup some GList leaks
+
+2019-04-15 18:37:58 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Ensure that the discoverer stops on deinit.
+ Discoverer maintain a referernce on the discoverer object while
+ the async timeout callback is alive to prevent a potential crash
+ if the object is freed while the callback is pending.
+ But if g_main_context is released before calling the timeout callback,
+ the discoverer pointer which was weak referenced from GESUriClipAssetClass
+ will not be disposed because the discoverer object is not finalized.
+
+=== release 1.15.90 ===
+
+2019-04-11 00:37:00 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.15.90
+
+2019-03-23 19:21:31 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ g-i: pass --quiet to g-ir-scanner
+ This suppresses the annoying 'g-ir-scanner: link: cc ..' output
+ that we get even if everything works just fine.
+ We still get g-ir-scanner warnings and compiler warnings if
+ we pass this option.
+
+2019-03-19 16:39:20 +0100 Jakub Adam <jakub.adam@collabora.com>
+
+ * ges/ges-video-source.c:
+ videosource: Expose video-direction child property
+
+2019-03-15 16:24:16 +0100 Jakub Adam <jakub.adam@collabora.com>
+
+ * ges/ges-video-source.c:
+ videosource: auto-flip the image according to image-orientation tag
+ If there's image-orientation tag, make sure the image is correctly
+ oriented before we scale it.
+
+2019-03-16 15:04:29 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/Makefile.am:
+ Fix autotools build
+
+2019-03-08 17:45:27 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ clip: Make sure to set the pasted clip start before adding to layer
+ And handle the fact that adding to a layer can fail.
+ Also plug some leaks in the dispose method (and use the dispose
+ vmethod instead of finalize as appropriate).
+
+2019-03-08 12:28:31 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ clip: Emit signals while splitting in a way the operation is undoable
+ Basically if we do not emit a "duration" change of the clip being
+ splitted first when executing the 'reverse' operations would lead
+ to fully overallaping clips.
+
+2019-03-01 19:32:19 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-container.c:
+ * ges/ges-group.c:
+ * ges/ges-internal.h:
+ * ges/ges-layer.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-tree.c:
+ * ges/ges-timeline-tree.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-uri-clip.c:
+ * ges/meson.build:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/check/python/common.py:
+ * tests/check/python/test_group.py:
+ * tests/check/python/test_timeline.py:
+ Reimplement the timeline editing API
+ This is implemented on top of a Tree that represents the whole timeline.
+ SourceClips can not fully overlap anymore and the tests have been
+ updated to take that into account. Some new tests were added to verify
+ that behaviour in greater details
+
+2019-03-03 21:18:53 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * examples/c/gessrc.c:
+ * plugins/ges/gesdemux.c:
+ Some copyright fixing
+
+2019-03-03 20:59:12 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline.c:
+ timeline: Rename group_id to stream_start_group_id
+
+2019-03-01 19:30:41 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/python/common.py:
+ tests: Add utilities to print the timeline
+ Making debugging tests simpler
+
+2019-03-01 19:08:39 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-group.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-track-element.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/timelineedition.c:
+ timeline-element: Add a method to retrieve layer priority
+ Each timeline element is in a layer (potentially spanning
+ over several), it is very often useful to retrieve an element
+ layer priority (from an app perspective more than the element
+ priority itself as that is a bit of an implementation detail
+ in the end).
+ Port tests to it
+
+2019-02-11 20:30:31 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * bindings/python/gi/overrides/GES.py:
+ python: Implement TimelineElement.__repr__
+
+2019-02-28 13:56:50 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ Add API to get the GESEdge names
+
+2019-02-09 18:59:08 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline-element.c:
+ ges: Move GESClipFlags to GESTimelineElementFlags
+ Keeping it internal
+ And add an internal method to get layer priority for GESTimelineElements
+ (dirty implementation to make it simple for now)
+
+2019-02-08 17:50:04 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/python/test_timeline.py:
+ tests:python: assertEquals is deprecated, use assertEqual
+
+2019-02-08 17:48:26 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-layer.c:
+ layer: factor out a method to remove an object without signaling it
+
+2019-02-08 17:47:48 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/python/common.py:
+ * tests/check/python/test_timeline.py:
+ tests: python: Move assertTimelineTopology to the baseclass
+
+2019-02-08 17:46:31 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline.c:
+ timeline: No error when moving an object as part of the context
+ It will just happen from the context
+
+2019-02-08 17:44:40 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline.c:
+ timeline: Not being able to trim and object is an error
+ So error out when that happens.
+
+2019-02-08 17:43:34 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline.c:
+ timeline: Setting duration to the same value is valid
+ And should not be advertised as if the operation failed.
+
+2019-02-08 17:37:39 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline.c:
+ timeline: Do not ripple if resulting duration would be 0
+
+2019-02-08 16:44:39 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-auto-transition.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-internal.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * tests/check/ges/group.c:
+ clip: Add a method to get the priority of the layer it is in
+ Just an helper method to get the 'priority of a the clip'
+
+2019-02-08 16:05:18 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-source-clip.c:
+ * tests/check/ges/timelineedition.c:
+ clip: Rollback moving clips when moving a contained TrackElement fails
+ And fix unit tests to match the correct behaviour
+
+2019-02-09 00:07:08 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-internal.h:
+ * tests/check/ges/test-utils.h:
+ Shorten GES_FORMAT output
+
+2019-02-21 17:24:51 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * tests/check/python/test_clip.py:
+ clip: Make sure to remove and re add effects when adding clips to layer
+ And make re add them in the same order.
+ And enhance tests to check that
+
+2019-03-01 22:57:48 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-source.c:
+ source: No checks when linking default elements
+
+2019-03-15 18:31:30 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * plugins/nle/nlesource.c:
+ nlesource: Use gst_element_call_async as appropriate
+
+2019-03-15 17:07:06 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * plugins/nle/nlesource.c:
+ nlesource: Protect seeks from tear down
+ Otherwise there is a race where we trigger the seek at the exact
+ same time the composition is being teared down potentially leading
+ to basesrc restarting its srcpad task which ends up being leaked.
+ Fixes ges.playback.scrub_backward_seeking.test_title.audio_video.vorbis_theora_ogg
+ and probably all its friends timeouting with the following stack trace:
+ (gdb) t a a bt
+ Thread 4 (Thread 0x7f5962acd700 (LWP 19997)):
+ #0 0x00007f5976713efd in syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
+ #1 0x00007f5976a9d3f3 in g_cond_wait (cond=cond@entry=0x7f5938125410, mutex=mutex@entry=0x7f59381253c8) at gthread-posix.c:1402
+ #2 0x00007f5976c9e26b in gst_task_func (task=0x7f59381253b0 [GstTask]) at ../subprojects/gstreamer/gst/gsttask.c:313
+ #3 0x00007f5976a7ecb3 in g_thread_pool_thread_proxy (data=<optimized out>) at gthreadpool.c:307
+ #4 0x00007f5976a7e2aa in g_thread_proxy (data=0x7f5954071d40) at gthread.c:784
+ #5 0x00007f59767ea58e in start_thread (arg=<optimized out>) at pthread_create.c:486
+ #6 0x00007f59767196a3 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
+ Thread 3 (Thread 0x7f5963fff700 (LWP 19995)):
+ #0 0x00007f597670e421 in __GI___poll (fds=0xe32da0, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
+ #1 0x00007f5976a553a6 in g_main_context_poll (priority=<optimized out>, n_fds=2, fds=0xe32da0, timeout=<optimized out>, context=0xe31ff0) at gmain.c:4221
+ #2 0x00007f5976a553a6 in g_main_context_iterate (context=0xe31ff0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3915
+ #3 0x00007f5976a55762 in g_main_loop_run (loop=0xe32130) at gmain.c:4116
+ #4 0x00007f59768db10a in gdbus_shared_thread_func (user_data=0xe31fc0) at gdbusprivate.c:275
+ #5 0x00007f5976a7e2aa in g_thread_proxy (data=0xe1b8a0) at gthread.c:784
+ #6 0x00007f59767ea58e in start_thread (arg=<optimized out>) at pthread_create.c:486
+ #7 0x00007f59767196a3 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
+ Thread 2 (Thread 0x7f5968dcc700 (LWP 19994)):
+ #0 0x00007f597670e421 in __GI___poll (fds=0xe1bcc0, nfds=1, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
+ #1 0x00007f5976a553a6 in g_main_context_poll (priority=<optimized out>, n_fds=1, fds=0xe1bcc0, timeout=<optimized out>, context=0xe1b350) at gmain.c:4221
+ #2 0x00007f5976a553a6 in g_main_context_iterate (context=context@entry=0xe1b350, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3915
+ #3 0x00007f5976a554d0 in g_main_context_iteration (context=0xe1b350, may_block=may_block@entry=1) at gmain.c:3981
+ #4 0x00007f5976a55521 in glib_worker_main (data=<optimized out>) at gmain.c:5861
+ #5 0x00007f5976a7e2aa in g_thread_proxy (data=0xe1b800) at gthread.c:784
+ #6 0x00007f59767ea58e in start_thread (arg=<optimized out>) at pthread_create.c:486
+ #7 0x00007f59767196a3 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
+ Thread 1 (Thread 0x7f5975df4fc0 (LWP 19993)):
+ #0 0x00007f5976713efd in syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
+ #1 0x00007f5976a9d3f3 in g_cond_wait (cond=cond@entry=0xe34020, mutex=0xe39b80) at gthread-posix.c:1402
+ #2 0x00007f5976a7f41c in g_thread_pool_free (pool=0xe34000, immediate=0, wait_=<optimized out>) at gthreadpool.c:776
+ #3 0x00007f5976c9f1ca in default_cleanup (pool=0xe256b0 [GstTaskPool]) at ../subprojects/gstreamer/gst/gsttaskpool.c:89
+ #4 0x00007f5976c9e32d in init_klass_pool (klass=<optimized out>) at ../subprojects/gstreamer/gst/gsttask.c:161
+ #5 0x00007f5976c9e502 in gst_task_cleanup_all () at ../subprojects/gstreamer/gst/gsttask.c:381
+ #6 0x00007f5976c214f4 in gst_deinit () at ../subprojects/gstreamer/gst/gst.c:1095
+ #7 0x000000000040394f in main (argc=6, argv=<optimized out>) at ../subprojects/gst-editing-services/tools/ges-launch.c:94
+
+2019-02-08 18:26:19 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/benchmarks/meson.build:
+ * tests/meson.build:
+ meson: Build benchmarks
+
+2019-03-11 19:56:09 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-uri-asset.c:
+ asset-uri: Create a specific discoverer when discovering sync
+ To allow 'reintrancy'.
+ This was a 'regression' introduced in bad64296d9b497a13f5f7fe91d568d85ed236265
+ Fixes https://gitlab.gnome.org/GNOME/pitivi/issues/2278
+
+2019-02-22 17:31:06 -0800 Pat DeSantis <pdesantis3@gmail.com>
+
+ * ges/ges-formatter.h:
+ Mark ges_timeline_load_from_uri as deprecated
+
+2019-02-20 20:17:55 -0800 Pat DeSantis <pdesantis3@gmail.com>
+
+ * ges/ges-formatter.h:
+ Update deprecation warning to match GTK style
+
+2019-02-20 17:17:14 -0800 Pat DeSantis <pdesantis3@gmail.com>
+
+ * ges/ges-formatter.h:
+ Mark ges_formatter_save_to_uri as deprecated
+
+2019-01-29 13:45:49 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/negative.c:
+ * tests/check/meson.build:
+ tests: Add inconsistent init/deinit test case
+
+2019-01-28 20:45:11 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-asset.c:
+ * ges/ges.c:
+ ges: Enhance ges_{init/deinit} documentation
+ Add some init/deinit related comment and make assertion when
+ ges_deinit() is called from unexpected thread.
+
+2019-02-06 19:49:14 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/python/common.py:
+ tests:python: Use proper GES.Project constructor
+ Avoiding a g_critical
+
+2019-02-08 13:54:06 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Suppress error from child during sync state with parent
+ This commit is to ensure cleanup internal elements on state change failure.
+ nlecomposition posts its own error message after cleanup child.
+ If we don't suppress child error, meanwhile, an application
+ triggered downward state change (resulting from child error message)
+ might be able to reach nlecomposition before internal cleaning child up.
+ That eventually results to downward state change failure.
+
+2019-02-05 17:29:00 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlesource.c:
+ nlesource: Don't leak pending seek event on dispose
+
+2019-02-01 15:37:42 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Don't keep sync state of child on activation failure
+ This will result in downward state change failure eventually
+ when user is finalizing top level (i.g., gespipeline) bin.
+
+2019-03-04 11:09:33 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * examples/.gitignore:
+ examples: add gessrc example binary to .gitignore
+
+2019-03-04 11:07:51 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * plugins/ges/gesdemux.c:
+ gesdemux: don't use deprecated gst_uri_construct()
+ Fixes #64
+
+2019-03-04 09:14:25 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * meson.build:
+ Back to development
+
+2019-02-28 13:09:38 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/ges/Makefile.am:
+ ges: Link ges plugin to libgstpbutils
+ /usr/bin/ld: .libs/libgstges_la-gesdemux.o: in function `ges_timeline_new_from_uri_from_main_thread':
+ ./plugins/ges/gesdemux.c:279: undefined reference to `gst_discoverer_new'
+ /usr/bin/ld: ./plugins/ges/gesdemux.c:288: undefined reference to `gst_discoverer_start'
+
+=== release 1.15.2 ===
+
+2019-02-26 11:59:49 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.15.2
+
+2019-02-26 14:12:13 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * examples/c/Makefile.am:
+ examples: add new gessrc example, so sourcefile gets disted
+
+2019-02-26 13:57:17 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ * plugins/Makefile.am:
+ * plugins/ges/Makefile.am:
+ plugins: add autotools build for new ges plugin
+
+2019-02-20 22:11:54 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * examples/python/keyframes.py:
+ examples: Add an example about using keyframes in python
+
+2019-02-11 18:26:04 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-meta-container.c:
+ ges-meta-container: Fix g-i annotation
+ ges-meta-container.c:516: Warning: GES: invalid "allow-none" annotation:
+ only valid for pointer types and out parameters
+
+2019-02-06 00:30:35 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * plugins/ges/gesdemux.c:
+ misc: Fix warnings on Cerbero's ancient MinGW
+ gesdemux.c:297:3: error: value computed is not used [-Werror=unused-value]
+
+2019-01-23 09:07:58 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-group.c:
+ Fix segfault when adding clips to group outside a timeline
+ Making sure that objects are inside a timeline before adding/removing them from it
+ Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/35
+
+2019-01-30 15:58:33 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/python/test_timeline.py:
+ tests: Fix usage of undefined class
+
+2019-01-28 19:09:03 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tools/ges-launcher.c:
+ launcher: Add options to set tracks restriction caps
+
+2019-01-28 00:55:27 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ tests: ges: Fix various leak
+
+2019-01-26 19:50:48 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * ges/ges-layer.c:
+ layer: Fix asset leak
+
+2019-01-26 16:44:09 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * ges/ges-effect.c:
+ effect: Fix string leak
+
+2019-01-29 11:52:43 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Implement dispose vmethod for GESUriSourceAsset
+ ... and fix DiscovererStreamInfo leak
+
+2019-01-26 16:27:27 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Don't forget to unref DiscovererInfo on dispose
+ Dispose() must unref DiscovererInfo ownership
+ taken by ges_uri_clip_asset_set_info().
+
+2019-01-25 18:21:43 +0900 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
+
+ * ges/ges-video-transition.c:
+ video-transition: Fix GstPad leak
+ Returned Gstpad by link_element_to_mixer_with_smpte()
+ has increased refcount in ges_smart_mixer_get_mixer_pad().
+
+2018-11-27 04:55:17 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-timeline.c:
+ * tests/check/python/common.py:
+ * tests/check/python/test_timeline.py:
+ timeline: Better handle loading inconsistent timelines
+ Auto transition when having 3 overlapping clips in a same point in the
+ timeline is not supported as we can't handle it in a nice way. Before we
+ to avoid creating 2 overlapping transitions (which is plain broken in
+ NLE) were completely disabling `auto-transition` and removing all
+ auto-transitions in the timeline but this is pretty weird for the end
+ user. This commit changes and now makes sure 2 transitions are not
+ created in the same place.
+ Also cleanup previous test case.
+
+2019-01-18 17:25:11 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-project.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-xml-formatter.c:
+ s/accured/occurred/g
+
+2019-01-18 17:12:42 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Minor debug enhancement
+
+2019-01-18 09:52:47 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * plugins/ges/gesdemux.c:
+ * plugins/ges/gesdemux.h:
+ * plugins/ges/gesplugin.c:
+ plugins: Add an a gesdemux element to 'demux' serialized timelines
+
+2015-03-14 20:52:47 +0000 Thibault Saunier <tsaunier@gnome.org>
+
+ * examples/c/gessrc.c:
+ * examples/c/meson.build:
+ * examples/python/gst-player.py:
+ * plugins/ges/gesplugin.c:
+ * plugins/ges/gessrc.c:
+ * plugins/ges/gessrc.h:
+ * plugins/ges/meson.build:
+ * plugins/meson.build:
+ plugins: implement a gessrc element useable from playbin
+ This is a new simple GstBin that can handle the ges:// uris
+ and will directly expose the srcppads of the tracks present in the
+ timeline.
+
+2019-01-18 15:45:39 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges.c:
+ * tests/check/python/test_assets.py:
+ uri-asset: Use the same code path for sync discovery as the async one
+ And start handling relocated assets.
+ Also expose the discoverer callback as a vmethod so that we can
+ overridde the discoverer when necessary (to handle discovering of
+ timeline through gesdemux for example)
+
+2019-01-17 15:12:42 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pipeline.c:
+ * plugins/nle/nlecomposition.c:
+ * tests/check/nle/nlecomposition.c:
+ nlecomposition: Get overall pipeline position by recursing up
+ And handle NLEComposition inside NLEComposition
+ Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/39
+
+2018-09-30 17:22:13 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-smart-video-mixer.c:
+ videomixer: Drop allocation query after the compositor
+ Working around https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/310
+
+2019-01-28 18:59:40 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-layer.c:
+ * tests/check/python/test_timeline.py:
+ layer: Resort clips before syncing priorities
+ We set the priorities making the assumption that `start_clips` is properly
+ ordered by start!
+ Fixes https://gitlab.gnome.org/GNOME/pitivi/issues/2254
+
+2019-01-28 12:58:06 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/ges/asset.c:
+ tests: asset: Add test async asset request with custom GMainContext
+ ... and test call ges_{init/deinit} multiple times in a unit test.
+
+2019-01-28 17:22:10 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges.c:
+ Revert "ges: Add missing type unref on deinit"
+ This reverts commit e939cfebaf4deeabf21ba799ddc3eeaa87e7cf9a.
+ Class might not be initialized if they were already registered
+ when ges_init() was called, but were not created until ges_deinit() called.
+
+2019-01-28 17:12:54 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges.c:
+ ges: Add check sync/async discoverer
+ To support ges_{init/deinit} multiple times in a process,
+ there should be a method for setting up internal object/table of
+ GESUriClipAssetClass. because *_class_init() will be called
+ only once in process lifecycle.
+
+2019-01-28 17:15:19 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Add missing GHashTable cleanup
+ ... and use g_object_unref() for GFile object, it's not a GstObject.
+
+2019-01-28 16:34:15 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges.c:
+ ges: Print initialize error reasons
+
+2019-01-28 12:19:30 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/ges/asset.c:
+ tests: asset: Remove out-of-date comment
+
+2019-01-28 12:17:00 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/ges/asset.c:
+ tests: asset: Remove pointless gst_init()
+ It's done by GST_CHECK_MAIN() already
+
+2019-01-28 11:24:29 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/ges/asset.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/mixers.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/tempochange.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/track.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/check/nle/complex.c:
+ * tests/check/nle/nlecomposition.c:
+ * tests/check/nle/nleoperation.c:
+ * tests/check/nle/simple.c:
+ tests: init/deinit per test case
+ ... in order to verify init/deinit pair.
+
+2019-01-28 11:07:36 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges.c:
+ ges: Simplify init/deinit flag
+ In theory, GES can be init/deinit multiple times in a process.
+ To simplify that use-case, let's trace only "ges_initialized" flag.
+
+2019-01-21 11:53:44 +0100 Corentin Noël <corentin.noel@collabora.com>
+
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ uri-asset: Add ges_uri_clip_asset_finish to get better introspection
+ Vala requires a matching _finish function to correctly bind the method with the right finish method.
+
+2019-01-21 14:14:06 +0100 Corentin Noël <corentin.noel@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: fix two issues in the documentation
+
+2019-01-15 09:59:59 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Do not forget to serialize clips metadata
+
+2019-01-15 09:38:14 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Serialize groups metadatas
+
+2019-01-15 00:15:28 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Don't leak GstDiscovererInfo
+
+2019-01-15 00:13:24 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Impl. dispose vfunc
+ ... and fix GList/GESAsset leak
+
+2019-01-14 23:38:19 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-transition-clip.c:
+ transition-clip: Don't leak GESAsset
+ Returned GESAsset from ges_asset_request should be freed since
+ ges_extractable_set_asset doesn't take ownership
+
+2019-01-14 22:14:18 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/gstframepositioner.c:
+ framepositioner: Fix invalid memory access
+ The GstFramePositioner might be finalized before the notify callback
+ Without this commit,
+ gst-editing-services / ges_basic / test_ges_timeline_remove_track
+ can reproduce the case.
+
+2019-01-14 15:06:26 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nleoperation.c:
+ nleoperation: Fix GstPad leak
+ Returned GstPad by nle_object_remove_ghost_pad() has increased
+ refcount.
+
+2019-01-14 14:10:32 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ * plugins/nle/nleghostpad.c:
+ nleghostpad: Fix GstEvent leak
+
+2019-01-14 12:52:47 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Don't leak GNode
+ Clear the last node before update
+
+2019-01-14 13:22:13 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/nle/complex.c:
+ * tests/check/nle/nlecomposition.c:
+ * tests/check/nle/tempochange.c:
+ tests: nle: Fix various leak
+ Don't leak GError and GstPad object
+
+2019-01-14 11:34:20 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/nle/common.c:
+ * tests/check/nle/common.h:
+ * tests/check/nle/complex.c:
+ * tests/check/nle/nleoperation.c:
+ * tests/check/nle/simple.c:
+ * tests/check/nle/tempochange.c:
+ tests: nle: Fix GList leak
+
+2019-01-13 00:12:42 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/ges/asset.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/layer.c:
+ tests: ges: Fix various leak
+
+2019-01-12 22:24:55 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-asset.c:
+ asset: Fix various leak
+
+2019-01-12 21:59:20 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-enums.c:
+ enums: Add missing unref
+
+2019-01-12 21:52:16 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Clear all members before chaining up to parent on finalize
+
+2019-01-12 21:51:36 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-structure-parser.c:
+ structure-parser: Add missig chain up code
+
+2019-01-12 20:53:38 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges.c:
+ ges: Add missing type unref on deinit
+
+2019-01-12 20:23:50 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-internal.h:
+ * ges/ges.c:
+ ges: Cleanup internal hash table on deinit
+ System-wide once allocated but it makes tracing leak hard
+
+2019-01-12 19:57:37 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges.c:
+ ges: Make init/deinit thread safe
+ Although it might be uncommon use case, init/deinit could be called
+ in non-main thread.
+
+2019-01-12 19:23:25 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-asset.c:
+ asset: Use static lock
+ The mutex life cycle follows processs.
+
+2019-01-14 10:16:18 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * tests/check/meson.build:
+ tests: Increase timeout value to 360 sec
+ Use consistent timeout value with core and other plugins.
+ Otherwise, valgrind sometimes timed out with default timeout 30sec.
+
+2019-01-14 12:45:29 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Don't try dump null stack
+ Fixes following assertion
+ Unexpected critical/warning: g_node_traverse: assertion 'root != NULL' failed
+
+=== release 1.15.1 ===
+
+2019-01-17 02:30:06 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.15.1
+
+2019-01-14 18:32:23 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-formatter.c:
+ ges: Register formatters during meta registration
+ So that formatters implemented outside GES itself are registered
+
+2019-01-14 18:30:38 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges.c:
+ * ges/ges.h:
+ ges: Add a ges_is_initialized function
+
+2019-01-14 18:28:52 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-formatter.c:
+ formatter: sink ref of the temporary GESFormatter
+ To accomodate formatters implemented with bindings/in python
+
+2019-01-09 17:11:37 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Ensure timeline state to be NULL on dispose
+ The GESTimeline's state might not be synced with parent
+
+2019-01-09 16:23:54 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Handle state change failure
+ Whatever the reason for failure, try cleanup child elements
+ and internal thread.
+
+2019-01-05 00:23:20 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-meta-container.c:
+ ges-meta-container: Fix warning message
+
+2019-01-04 05:31:39 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-meta-container.c:
+ ges-meta-container: Minor documentation fixes
+
+2019-01-04 12:36:20 +0100 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-track-element.c:
+ track-element: Ignore writability for whitlisted children props
+ If the property was explicitely whitelisted, we should expose it
+ in any case.
+ This was a regression from 835d69374978208bc73a8f823b899f624dda9479
+
+2018-12-30 19:49:44 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-video-mixer.c:
+ ges: avoid use of G_DECLARE_FINAL_TYPE which requires GLib 2.44
+ Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/48
+
+2018-12-27 10:54:28 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-container.c:
+ container: Fix GHashTable leak
+
+2018-12-27 00:15:30 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-container.c:
+ container: Fix wrong finalize() usage
+ finalize must chain up to parent's finalize(), not dispose()
+
+2018-12-27 00:14:03 +0900 Seungha Yang <seungha.yang@navercorp.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Chain up to parent impl. on dispose()
+ ... as documented in glib
+
+2018-09-24 15:41:24 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-transition.c:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-auto-transition.c:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-transition-clip.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip-asset.c:
+ * ges/ges-clip.c:
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-container.c:
+ * ges/ges-effect-asset.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-enums.c:
+ * ges/ges-extractable.c:
+ * ges/ges-formatter.c:
+ * ges/ges-group.c:
+ * ges/ges-image-source.c:
+ * ges/ges-layer.c:
+ * ges/ges-meta-container.c:
+ * ges/ges-multi-file-source.c:
+ * ges/ges-operation-clip.c:
+ * ges/ges-operation.c:
+ * ges/ges-overlay-clip.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-prelude.h:
+ * ges/ges-project.c:
+ * ges/ges-screenshot.c:
+ * ges/ges-smart-adder.c:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-source.c:
+ * ges/ges-structure-parser.c:
+ * ges/ges-structured-interface.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element-asset.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-utils.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-track.c:
+ * ges/ges-video-transition.c:
+ * ges/ges-video-uri-source.c:
+ * ges/ges-xml-formatter.c:
+ * ges/meson.build:
+ * meson.build:
+ WIP: ges: fix API export/import and 'inconsistent linkage' on MSVC
+ Export GES library API in headers when we're building the
+ library itself, otherwise import the API from the headers.
+ This fixes linker warnings on Windows when building with MSVC.
+ Fix up some missing config.h includes when building the lib which
+ is needed to get the export api define from config.h
+ Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/42
+
+2018-12-10 13:28:16 +1100 Matthew Waters <matthew@centricular.com>
+
+ * ges/meson.build:
+ build: also allow building static libraries for e.g. Android/iOS
+
+2018-12-05 17:25:04 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * common:
+ Automatic update of common submodule
+ From ed78bee to 59cb678
+
+2018-11-30 12:41:04 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges.c:
+ * ges/ges.h:
+ * ges/meson.build:
+ * meson.build:
+ * meson_options.txt:
+ Add a way to disable xptv support
+ This formatter is in very bad shape and is generally not useful.
+ It has been deprecated since 1.0... and I bet noone uses it.
+
+2018-11-28 05:48:37 +0200 Jordan Petridis <jordan@centricular.com>
+
+ * examples/c/play_timeline_with_one_clip.c:
+ Run gst-indent through the files
+ This is required before we enabled an indent test in the CI.
+ https://gitlab.freedesktop.org/gstreamer/gstreamer-project/issues/33
+
+2018-11-27 12:09:20 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-track-element.c:
+ track: Fix documentation about "binding_type"
+
+2018-11-26 17:18:25 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * meson.build:
+ Revert "meson: Fix the reference to libxml2 path"
+ It seemed to be what the wrap file expected but in the end it is
+ just a bug in meson which is now fixed.
+ This reverts commit cc5d74d0be30dab92d1540ed749eaf4dcedd9171.
+
+2018-11-26 15:57:30 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * meson.build:
+ meson: Fix the reference to libxml2 path
+
+2018-11-26 14:50:29 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * meson.build:
+ meson: Add a fallback for libxml2
+
+2018-11-26 14:50:03 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-validate.c:
+ validate: cleanup the playback-time from validate structures
+ Otherwise we might fail on them in the ges-structure-interface
+
+2018-11-23 11:22:03 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-effect.c:
+ effect: Create ghost pads ourself
+ As we can have effects with several pads and the default ghosting
+ doesn't allow that.
+ This way we also filter the pads to ghost to match our track type.
+
+2018-11-23 11:20:00 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-effect-asset.c:
+ effect: Consider the "Filter" classification to determine effect media type
+
+2018-11-12 12:47:02 +0200 Jordan Petridis <jordan@centricular.com>
+
+ * .gitlab-ci.yml:
+ Add Gitlab CI configuration
+ This commit adds a .gitlab-ci.yml file, which uses a feature
+ to fetch the config from a centralized repository. The intent is
+ to have all the gstreamer modules use the same configuration.
+ The configuration is currently hosted at the gst-ci repository
+ under the gitlab/ci_template.yml path.
+ Part of https://gitlab.freedesktop.org/gstreamer/gstreamer-project/issues/29
+
+2018-10-09 00:45:29 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * tests/check/python/common.py:
+ * tests/check/python/test_clip.py:
+ * tests/check/python/test_group.py:
+ clip: Emit additional signals after child-removed
+ When removing an effect from a clip, first the notify::priority signals
+ were being emitted for the remaining effects which changed priority, and only
+ at the end the child-removed signal. Now the child-removed signal is emitted
+ first.
+
+2018-11-05 13:57:25 +0100 Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ timeline-element: Fix compilation errors
+ There were some code errors introduced in commit 6b738b7a
+
+2018-11-04 20:47:01 +1100 Matthew Waters <matthew@centricular.com>
+
+ * meson.build:
+ * plugins/nle/meson.build:
+ nle: install pkg-config file for plugin
+
+2018-11-05 11:00:58 +0100 Corentin Noël <corentin.noel@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ timeline-element: Align virtual methods and invokers prototypes
+
+2018-11-05 05:51:47 +0000 Matthew Waters <matthew@centricular.com>
+
+ * .gitmodules:
+ * gst-editing-services.doap:
+ Update git location to gitlab
+
+2018-11-02 14:32:04 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pipeline.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ ges: Check the thread from which our API is used
+ And add some missing API guards
+
+2018-11-02 09:30:28 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ ges: Mark GValue in child property setters as const
+ We can't change the vmethod at this point so just cast. This makes
+ the API more explicit so it is better in all cases.
+
+2018-10-31 10:38:59 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/Makefile.am:
+ * ges/ges.h:
+ * ges/meson.build:
+ Keep GESSmartVideoMixer out of the Gir and add geseffectasset.h to ges.h
+ Fixing gstreamer-sys rust bindings.
+
+2018-10-31 10:06:08 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/Makefile.am:
+ * ges/meson.build:
+ Fix the `package` name in the gir to match the `.pc` filename
+
+2018-10-28 15:55:23 +0000 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-video-transition.c:
+ * ges/gstframepositioner.c:
+ video-transition: Port to the new 'operator' API in compositor
+ Now subclassing a ghostpad with an alpha property so that
+ we can multiply the alpha of the frame positioning meta
+ and the alpha of that pad, setting it on the compositor pad.
+ https://bugzilla.gnome.org/show_bug.cgi?id=797169
+
+2018-10-28 15:33:31 +0000 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-video-transition.c:
+ Revert "video-transition: Make use of the new `compositor::crossfade-ratio` property"
+ This reverts commit 57be9b67998bf5fef81a61c645b167c3857ed35b.
+
+2018-10-28 13:29:43 +0000 Thibault Saunier <tsaunier@igalia.com>
+
+ * bindings/python/gi/__init__.py:
+ python: Remove __init__.py
+ It is not needed with latest python
+
+2018-09-30 17:44:08 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-source.c:
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Add a function that prints stacks as debug info
+
+2018-10-28 11:05:38 +0000 Philippe Normand <philn@igalia.com>
+
+ * bindings/python/gi/overrides/__init__.py:
+ python: Remove debug print
+
+2018-10-22 08:13:07 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-xml-formatter.c:
+ ges: Fix compilation with latest GLib
+ g_clear_pointer() is now preserving the type of its arguments for the
+ free function.
+ ges-xml-formatter.c: In function ‘_dispose’:
+ ges-xml-formatter.c:1635:7: error: function called through a non-compatible type [-Werror]
+ (GDestroyNotify) g_hash_table_unref);
+ /usr/include/glib-2.0/glib/gmem.h:121:8: note: in definition of macro ‘g_clear_pointer’
+ (destroy) (_ptr); \
+ ^~~~~~~
+ https://bugzilla.gnome.org/show_bug.cgi?id=797310
+
+2018-10-08 23:25:21 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: use 'python' module to find python instead of deprecated 'python3' one
+ https://github.com/mesonbuild/meson/pull/4169
+
+2018-09-05 22:55:02 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-transition.c:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-transition-clip.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip-asset.c:
+ * ges/ges-clip.c:
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-container.c:
+ * ges/ges-effect-asset.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-formatter.c:
+ * ges/ges-group.c:
+ * ges/ges-image-source.c:
+ * ges/ges-layer.c:
+ * ges/ges-multi-file-source.c:
+ * ges/ges-operation-clip.c:
+ * ges/ges-operation.c:
+ * ges/ges-overlay-clip.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-source.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element-asset.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-track.c:
+ * ges/ges-video-transition.c:
+ * ges/ges-video-uri-source.c:
+ * ges/ges-xml-formatter.c:
+ * plugins/nle/nlecomposition.c:
+ * plugins/nle/nlesource.c:
+ * tools/ges-launcher.c:
+ Update for g_type_class_add_private() deprecation in recent GLib
+
+2018-09-05 21:49:09 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * tests/check/python/test_clip.py:
+ clip: Resync priorities when removing an effect
+ When removing a top effect in the list of top effects, other
+ effects priorities need to take that into account to avoid
+ holes in the indices.
+
+2018-08-03 14:02:58 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges.c:
+ ges: Check that nle is avalaible when initializing
+
+2018-09-01 12:17:08 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * meson.build:
+ * meson_options.txt:
+ * tests/meson.build:
+ meson: Add a feature option for tests
+ This autodetection is needed on iOS inside Cerbero where
+ gstreamer-check-1.0 is not available.
+
+2018-08-31 14:44:58 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * ges/meson.build:
+ * meson.build:
+ meson: Maintain macOS ABI through dylib versioning
+ Requires Meson 0.48, but the feature will be ignored on older versions
+ so it's safe to add it without bumping the requirement.
+ Documentation:
+ https://github.com/mesonbuild/meson/blob/master/docs/markdown/Reference-manual.md#shared_library
+
+2018-08-15 19:14:30 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * tests/check/meson.build:
+ meson: There is no gstreamer-plugins-good-1.0.pc
+ There is no installed version of that, only an uninstalled version.
+
+2018-07-29 16:20:50 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-xml-formatter.c:
+ formatter: Fix mixup in variable check
+
+2018-07-28 14:29:11 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Bump format version
+ Previous commit makes the format not forward compat.
+
+2018-07-28 12:16:36 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-container.c:
+ * ges/ges-internal.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges-xml-formatter.c:
+ * tests/check/python/test_clip.py:
+ formatter: Serialize Transition border and invert properties
+ Marking them as children properties and properly allow serializing
+ clips children properties.
+ This doesn't handle several TrackElement of a same type with
+ different property values but this require more worked already
+ marked as fixme to allow specifying full path of elements in the
+ children properties API.
+ See https://gitlab.gnome.org/GNOME/pitivi/issues/1687
+
+2018-07-27 22:11:33 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-project.c:
+ project: Compute relocation URIs in missing-uri signal
+ Until know we were doing it outside of the signal and subclasses didn't
+ have a chance to know that some assets was relocated.
+ This is required so that Pitivi can handle proxy delation and relocated
+ assets.
+ Required for https://gitlab.gnome.org/GNOME/pitivi/issues/2203
+
+2018-07-25 17:20:02 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * docs/libs/meson.build:
+ * meson.build:
+ * meson_options.txt:
+ meson: Convert common options to feature options
+ The remaining automagic options are in tests and examples.
+ https://bugzilla.gnome.org/show_bug.cgi?id=795107
+
+2018-07-23 00:07:07 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Let testsuites define scenarios path
+ The code was not taking into account the fact that testsuite could be
+ located in a different folder that the default one.
+ Now the testsuite is responsible for providing a path if it wants
+ to set extra scenarios or the user can set one by hand.
+
+2018-07-14 09:00:51 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitivi-formatter: Do not g_file_test on a NULL pointer
+
+2018-07-18 12:38:04 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Rename outside_segment to seek_segment
+ This segment is representing the last seek received
+ inside the composition. Or a simply initialized segment
+ if need seek occurred.
+
+2018-07-18 12:52:59 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/nle/tempochange.c:
+ tests: Minor assertion enahncements
+
+2018-07-19 10:55:31 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-project.c:
+ project: Do not emit 'error-loading-asset' when we are trying to update the ID
+
+2018-07-19 22:06:54 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Handle scenario only based tests
+ Meaning tests that do not need project at all
+
+2018-07-12 13:53:44 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-track.c:
+ track: Set restriction caps when update_restriction before caps being set
+ And stop leaking intermediary restriction caps.
+ https://bugzilla.gnome.org/show_bug.cgi?id=796802
+
+2018-07-08 16:09:46 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * bindings/python/gi/overrides/GES.py:
+ python:overrides: Remove spurious print
+
+2018-07-08 10:36:36 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * bindings/python/gi/overrides/GES.py:
+ * bindings/python/meson.build:
+ * meson.build:
+ * meson_options.txt:
+ * tests/check/python/overrides_hack.py:
+ * tests/check/python/test_clip.py:
+ * tests/check/python/test_group.py:
+ * tests/check/python/test_timeline.py:
+ python: Fix GES.Timelineset_child_property
+ Implementing it in the overrides as PyGObject won't be able to properly
+ convert python values to GValues in some cases. Using
+ g_object_set_property works as some logic is implemented inside
+ PyGObject for that particular case. This is a "regression" due
+ to https://bugzilla.gnome.org/review?bug=769789&attachment=348766 were
+ we end up with an OverflowError while setting G_TYPE_UINT children
+ properties.
+
+2018-04-01 16:22:16 +0200 Bastian Köcher <git@kchr.de>
+
+ * ges/meson.build:
+ meson: fix install dir for configure files
+ Nixos configures a custom includedir.
+ https://bugzilla.gnome.org/show_bug.cgi?id=794856
+
+2018-07-01 16:22:24 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/Makefile.am:
+ * meson.build:
+ Set GLib log domain to GES
+
+2018-07-01 12:21:54 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-group.c:
+ group: Handle clips that get readded to a layer and inside a group
+
+2018-06-26 16:21:22 +0200 Mathieu Duponchelle <mathieu@centricular.com>
+
+ * ges/ges-asset.c:
+ asset: documentation fix
+
+2018-06-15 16:49:55 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * configure.ac:
+ * tests/check/Makefile.am:
+ * tests/check/meson.build:
+ tests: Use gst-validate-launcher to run python tests
+
+2018-06-14 17:07:10 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * meson.build:
+ * meson_options.txt:
+ meson: Rename the gtkdoc option to gtk_doc
+ This is what other modules use
+
+2018-05-20 23:48:39 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * examples/c/Makefile.am:
+ examples: override -Werror
+ Don't want to error out on deprecated API warnings and such.
+ Just drop -Werror for the examples until someone updates them
+ to recent gtk3 API. Maybe showing the warnings will motivate
+ someone.
+ https://bugzilla.gnome.org/show_bug.cgi?id=796243
+
+2018-05-20 23:47:14 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ examples: always build against gtk3
+ Drop gtk2 option.
+ https://bugzilla.gnome.org/show_bug.cgi?id=796243
+
+2018-05-20 23:46:42 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * examples/c/ges-ui.c:
+ examples: ges-ui: fix some gtk2-ism
+ Still lots of deprecated API to update.
+ https://bugzilla.gnome.org/show_bug.cgi?id=796243
+
+2018-05-13 21:12:35 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * tests/check/python/test_timeline.py:
+ clip: Make sure to never snap when splitting clips
+ It makes no sense to snap in that context.
+ https://gitlab.gnome.org/GNOME/pitivi/issues/2193
+
+2018-05-13 16:37:08 -0400 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Run IQA tests when possible
+ Meaning that a reference file has to be present on disk with a
+ `.expected_result` extension.
+
+2018-04-20 17:56:15 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Stop forcing I420 in profiles restriction caps
+ This was a workaround for encoders bad behavior in the reconfigure case.
+ https://bugzilla.gnome.org/show_bug.cgi?id=795420
+
+2018-05-05 19:34:14 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * meson.build:
+ * meson_options.txt:
+ meson: Update option names to omit disable_ and with- prefixes
+ Also yield common options to the outer project (gst-build in our case)
+ so that they don't have to be set manually.
+
+2018-04-25 11:01:01 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: use -Wl,-Bsymbolic-functions where supported
+ Just like the autotools build.
+
+2018-04-20 18:45:19 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Properly error out when linking fails
+ In the rendering case we were getting random issues and often the
+ pipeline was not be able to preroll as some pad were not linked inside
+ encodebin.
+ https://bugzilla.gnome.org/show_bug.cgi?id=795422
+
+2018-04-20 17:54:12 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-track-element.c:
+ track-element: Fix the way we look for properties on simple elements
+ Refactor so that the same code is used to add children properties from
+ bin children and when inspecting a single element.
+
+2018-04-20 17:36:55 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Update caps only when rendering as comment suggests
+ We used to update caps for any more because of missing brackets.
+
+2018-04-20 17:35:06 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-effect.c:
+ effect: Allow setting properties on any element specified by the user
+ Those are the elements he cares about and we should expose their APIs
+ as is, event if they are not classified as effects. For example if
+ the user want to use a capsfilter as effect, he should be able to set
+ its caps.
+
+2018-04-20 17:34:17 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-base-xml-formatter.c:
+ xml-formatter: Print error if an effect can't be set when deserializing
+
+2018-04-16 10:53:57 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 3fa2c9e to ed78bee
+
+2018-03-31 13:39:54 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-layer.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/layer.c:
+ Deprecate ges_layer_set_priority
+ Keep old behaviour but deprecate the method and property as
+ ges_timeline_move_layer should be used instead.
+
+2015-12-12 11:29:50 +0000 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Add a method to move layers around
+ summary_:
+ This way the timeline can handle all priorities for the user
+ making the API simpler to use.
+ API:
+ + ges_timeline_move_layer
+ reviewers_: Mathieu_Du
+ Differential Revision: https://phabricator.freedesktop.org/D232
+
+2018-03-31 11:24:23 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-timeline-element.h:
+ timeline-element: Fix ABI breakage
+ New fields in structure should be added in place of the padding
+
+2018-03-31 10:38:19 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * docs/libs/ges-sections.txt:
+ docs: Move timeline related doc to the timeline section
+ It wrongly was in the layers
+
+2018-03-30 18:17:13 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-structure-parser.c:
+ * ges/ges-structured-interface.c:
+ * ges/parse.l:
+ ges-launcher: Add support for titles
+
+2018-03-30 17:41:49 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-command-line-formatter.c:
+ command-line-formatter: Refactor to generate the documentation automatically
+ https://bugzilla.gnome.org/show_bug.cgi?id=794837
+
+2018-03-26 12:13:25 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-effect.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ ges: Update the media-duration-factor each time a child property is set
+ Otherwise the changes won't be reflected in the NLE backend.
+ This makes speed changes working inside ges-launch-1.0
+ ges-launch-1.0 +clip /path/to/file i=10 d=5 +effect videorate set-rate 5.0
+ https://bugzilla.gnome.org/show_bug.cgi?id=794699
+
+2018-03-26 18:56:03 +0530 Suhas Nayak <suhas2go@gmail.com>
+
+ * ges/ges-effect.c:
+ ges: Register videorate::rate as a rate changing property
+ https://bugzilla.gnome.org/show_bug.cgi?id=794699
+
+2018-03-20 10:24:35 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * meson.build:
+ Back to development
+
+=== release 1.14.0 ===
+
+2018-03-19 20:28:10 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.14.0
+
+2018-03-19 08:57:47 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-video-source.c:
+ doc: Remove documentation about GESVideoSource::zorder as it doesn't exist
+ The zorder is controled through the GESLayer priority API, not directly
+ on the sources.
+
+2018-03-18 11:03:00 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline.c:
+ * tests/check/python/common.py:
+ * tests/check/python/test_timeline.py:
+ clip: Make sure to create transition after a clip is splitted
+ In the (now tested) scenario where we have a transition on the right
+ side of a clip we are splitting, auto transitions can't be created
+ because we resize the clip after adding the new one, meaning that
+ there are 3 elements in the "transition zone", we need to force
+ auto transition creation after the splitting.
+ Fixes https://gitlab.gnome.org/GNOME/pitivi/issues/2142
+
+2018-03-14 20:59:04 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-group.c:
+ group: Handle clips being removed from their layers
+
+=== release 1.13.91 ===
+
+2018-03-13 19:29:44 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.13.91
+
+2018-03-13 14:14:57 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-asset.h:
+ * ges/ges-audio-source.h:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-audio-track.h:
+ * ges/ges-audio-transition.h:
+ * ges/ges-audio-uri-source.h:
+ * ges/ges-base-effect-clip.h:
+ * ges/ges-base-effect.h:
+ * ges/ges-base-transition-clip.h:
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-clip-asset.h:
+ * ges/ges-clip.h:
+ * ges/ges-command-line-formatter.h:
+ * ges/ges-container.h:
+ * ges/ges-effect-asset.h:
+ * ges/ges-effect-clip.h:
+ * ges/ges-effect.h:
+ * ges/ges-enums.h:
+ * ges/ges-extractable.h:
+ * ges/ges-formatter.h:
+ * ges/ges-group.h:
+ * ges/ges-image-source.h:
+ * ges/ges-internal.h:
+ * ges/ges-layer.h:
+ * ges/ges-meta-container.h:
+ * ges/ges-multi-file-source.h:
+ * ges/ges-operation-clip.h:
+ * ges/ges-operation.h:
+ * ges/ges-overlay-clip.h:
+ * ges/ges-pipeline.h:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-prelude.h:
+ * ges/ges-project.h:
+ * ges/ges-screenshot.h:
+ * ges/ges-smart-adder.h:
+ * ges/ges-source-clip.h:
+ * ges/ges-source.h:
+ * ges/ges-test-clip.h:
+ * ges/ges-text-overlay-clip.h:
+ * ges/ges-text-overlay.h:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.h:
+ * ges/ges-title-clip.h:
+ * ges/ges-title-source.h:
+ * ges/ges-track-element-asset.h:
+ * ges/ges-track-element.h:
+ * ges/ges-track.h:
+ * ges/ges-transition-clip.h:
+ * ges/ges-transition.h:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.h:
+ * ges/ges-utils.h:
+ * ges/ges-video-source.h:
+ * ges/ges-video-test-source.h:
+ * ges/ges-video-track.h:
+ * ges/ges-video-transition.h:
+ * ges/ges-video-uri-source.h:
+ * ges/ges-xml-formatter.h:
+ * ges/ges.h:
+ GST_GES_API -> GES_API
+
+2018-03-13 13:45:24 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * docs/libs/meson.build:
+ * ges/Makefile.am:
+ * ges/ges-asset.h:
+ * ges/ges-audio-source.h:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-audio-track.h:
+ * ges/ges-audio-transition.h:
+ * ges/ges-audio-uri-source.h:
+ * ges/ges-base-effect-clip.h:
+ * ges/ges-base-effect.h:
+ * ges/ges-base-transition-clip.h:
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-clip-asset.h:
+ * ges/ges-clip.h:
+ * ges/ges-command-line-formatter.h:
+ * ges/ges-container.h:
+ * ges/ges-effect-asset.h:
+ * ges/ges-effect-clip.h:
+ * ges/ges-effect.h:
+ * ges/ges-enums.h:
+ * ges/ges-extractable.h:
+ * ges/ges-formatter.h:
+ * ges/ges-group.h:
+ * ges/ges-image-source.h:
+ * ges/ges-internal.h:
+ * ges/ges-layer.h:
+ * ges/ges-meta-container.h:
+ * ges/ges-multi-file-source.h:
+ * ges/ges-operation-clip.h:
+ * ges/ges-operation.h:
+ * ges/ges-overlay-clip.h:
+ * ges/ges-pipeline.h:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-prelude.h:
+ * ges/ges-project.h:
+ * ges/ges-screenshot.h:
+ * ges/ges-smart-adder.h:
+ * ges/ges-source-clip.h:
+ * ges/ges-source.h:
+ * ges/ges-test-clip.h:
+ * ges/ges-text-overlay-clip.h:
+ * ges/ges-text-overlay.h:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.h:
+ * ges/ges-title-clip.h:
+ * ges/ges-title-source.h:
+ * ges/ges-track-element-asset.h:
+ * ges/ges-track-element.h:
+ * ges/ges-track.h:
+ * ges/ges-transition-clip.h:
+ * ges/ges-transition.h:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.h:
+ * ges/ges-utils.h:
+ * ges/ges-video-source.h:
+ * ges/ges-video-test-source.h:
+ * ges/ges-video-track.h:
+ * ges/ges-video-transition.h:
+ * ges/ges-video-uri-source.h:
+ * ges/ges-xml-formatter.h:
+ * ges/ges.h:
+ * ges/meson.build:
+ ges: GST_EXPORT -> GST_GES_API
+ We need different export decorators for the different libs.
+ For now no actual change though, just rename before the release,
+ and add prelude headers to define the new decorator to GST_EXPORT.
+
+2018-03-11 11:13:05 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/ges-clip.c:
+ clip: Snapping should happen with one and only one TrackElement
+ This was leading to clip with TrackElements that were not at the
+ same position in their container, and weird bugs, see:
+ https://gitlab.gnome.org/GNOME/pitivi/issues/2133
+
+=== release 1.13.90 ===
+
+2018-03-03 23:09:36 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.13.90
+
+2018-02-26 04:01:33 +0530 Harish Fulara <harish14143@iiitd.ac.in>
+
+ * ges/ges-internal.h:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ Added paste functionality to GESTimeline class
+ https://bugzilla.gnome.org/show_bug.cgi?id=793820
+
+2018-03-01 18:56:05 +0100 Mathieu Duponchelle <mathieu@centricular.com>
+
+ * meson.build:
+ meson: enable more warnings
+
+2018-02-27 10:00:32 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * tests/check/ges/layer.c:
+ test: Plug minor leaks
+
+2018-02-27 15:26:29 +0530 Harish Fulara <harish14143@iiitd.ac.in>
+
+ * ges/ges-layer.c:
+ ges: Fix ges_layer_get_clips_in_interval(start, end) refcount handling
+ The documentation states that it returns a (transfer full) list
+ of GESClip but it was returning a (transfer container) list. Make
+ sure to actually make it (transfer full).
+ https://bugzilla.gnome.org/show_bug.cgi?id=793874
+
+2018-01-29 17:46:06 -0300 Thibault Saunier <tsaunier@igalia.com>
+
+ * ges/meson.build:
+ meson: Explicitely include GObject-2.0 in the gir
+
+2018-02-21 19:42:19 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: simplify GST_DISABLE_GST_DEBUG check some more
+
+2018-02-21 19:20:56 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: don't use add_global_arguments()
+ .. and tighten check for disabled gst debugging sytem.
+ add_global_arguments() can't be used in subprojects. It's
+ entirely possible that ges is a subproject but gstreamer
+ is picked up from an installed location, so we should
+ really use add_project_arguments() in both cases.
+
+2018-02-15 19:44:30 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ * meson.build:
+ Back to development
+
+=== release 1.13.1 ===
+
+2018-02-15 17:20:22 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * NEWS:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.13.1
+
+2018-02-08 19:16:26 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: make version numbers ints and fix int/string comparison
+ WARNING: Trying to compare values of different types (str, int).
+ The result of this is undefined and will become a hard error
+ in a future Meson release.
+
+2018-02-04 12:26:48 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ autotools: use -fno-strict-aliasing where supported
+ https://bugzilla.gnome.org/show_bug.cgi?id=769183
+
+2018-01-30 20:35:33 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: use -fno-strict-aliasing where supported
+ https://bugzilla.gnome.org/show_bug.cgi?id=769183
+
+2018-01-11 10:57:30 +0100 Edward Hervey <edward@centricular.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-timeline-element.c:
+ ges: Fix sizeof() usage
+ The entries of the array are "gchar *" and not "gchar **"
+ CID #1427091
+ CID #1427120
+
+2017-12-20 14:28:33 +0100 Edward Hervey <edward@centricular.com>
+
+ * tests/check/ges/asset.c:
+ check: Fix minor leak in test
+
+2017-12-19 23:28:53 +0100 Mathieu Duponchelle <mathieu@centricular.com>
+
+ * ges/ges-smart-adder.c:
+ * ges/ges-smart-adder.h:
+ ges-smart-adder: use capsfilter instead of GstAudioMixer:caps
+ The property has been removed, and using a capsfilter instead
+ is the appropriate solution.
+
+2017-12-14 14:53:41 +1100 Matthew Waters <matthew@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From e8c7a71 to 3fa2c9e
+
+2017-11-27 11:49:04 +0100 Edward Hervey <edward@centricular.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-effect-asset.c:
+ * ges/ges-effect.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-xml-formatter.c:
+ ges: Fix a bunch of leaks
+ There are definitely more left, but don't have time for more debugging
+
+2017-11-27 20:18:55 +1100 Matthew Waters <matthew@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 3f4aa96 to e8c7a71
+
+2017-11-26 13:31:02 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ configure: remove c++ compiler bits that are unused
+
+2017-11-26 13:29:33 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * Makefile.am:
+ * ges/meson.build:
+ * meson.build:
+ * win32/MANIFEST:
+ * win32/common/libges.def:
+ win32: remove .def file with exports
+ They're no longer needed, symbol exporting is now explicit
+ via GST_EXPORT in all cases (autotools, meson, incl. MSVC).
+
+2017-11-26 13:25:06 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ autotools: stop controlling symbol visibility with -export-symbols-regex
+ Instead, use -fvisibility=hidden and explicit exports via GST_EXPORT.
+ This should result in consistent behaviour for the autotools and
+ Meson builds.
+
+2017-11-26 13:26:13 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * .gitignore:
+ .gitignore: ignore test registry
+
+2017-11-25 15:56:36 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-clip.c:
+ uri-clip: Copy previous track elements bindings when setting a new asset
+ Fixes https://phabricator.freedesktop.org/T7862
+
+2017-11-23 15:49:48 +0100 Edward Hervey <edward@centricular.com>
+
+ * tests/check/Makefile.am:
+ check: Actually define a registry to use for tests
+ Otherwise every single run of every single test would recreate
+ a registry
+
+2017-11-07 12:04:03 +0530 Ashish Kumar <kr.ashish@samsung.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-layer.c:
+ GESAsset, GESLayer: add some function guards
+ https://bugzilla.gnome.org/show_bug.cgi?id=789521
+
+2017-11-07 11:26:58 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-auto-transition.c:
+ auto-transition: Fix debug printf format
+
+2017-11-07 10:15:58 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-auto-transition.c:
+ * ges/ges-timeline.c:
+ timeline: Do not snap object within the moving context
+ Reviewed-by: Alex Băluț <<alexandru.balut@gmail.com>>
+ Differential Revision: https://phabricator.freedesktop.org/D1873
+
+2017-10-31 12:05:08 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/gstframepositioner.c:
+ * ges/gstframepositioner.h:
+ ges: Sync 'par' to track restriction caps in the frame positionner
+ Allowing GES users to have control over how compositing is done
+
+2017-09-20 12:59:40 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-source.c:
+ ges-source: Fix caps memory leak and compiler warnings when compiling without debug logging
+
+2017-09-07 12:08:40 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Always execute seeks
+ We have an optiominisation to avoid double seeks when a seek is passed
+ the end of the current stack. The problem, is that we no longer flush
+ the pipeline when this code is reached. This patch comments out this
+ optimization adding a FIXME. As mention, flushing the stack instead of
+ seeking would work, but does not seem trivial considering all the
+ mechanic inplace to forward or not the events.
+ https://bugzilla.gnome.org/show_bug.cgi?id=787405
+
+2017-09-07 12:08:40 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Also start task on allocation query
+ The allocation query may block on the sink when in pause. As a side effect, we
+ may never get a buffer now that tee does forward the allocation query.
+ This would often lead in a pipeline stall.
+ https://bugzilla.gnome.org/show_bug.cgi?id=787405
+
+2017-09-07 12:08:40 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * tests/validate/scenarios/Makefile.am:
+ make: Fix validate scenario install directory
+
+2017-09-07 12:07:03 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * tests/meson.build:
+ * tests/validate/meson.build:
+ * tests/validate/scenarios/meson.build:
+ meson: Install validate helpers and scenarios
+ This fixes the usage of gst-validate-launcher ges with an installed
+ version of GES.
+
+2017-08-17 07:28:46 +0000 Stefan Popa <stefanpopa2209@gmail.com>
+
+ * ges/ges-track-element.c:
+ track_element: Always emit "control-binding-removed" signal.
+ When setting a new control binding on a track element, the old control
+ binding (if any) is going to be removed. Make sure the
+ "control-binding-removed" signal is emitted in this case.
+ Fixes https://phabricator.freedesktop.org/T7340#95666
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1842
+
+2017-08-29 22:23:57 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ Revert "timeline: Return FALSE when commiting an empty timeline"
+ This commit means that we do not get ASYNC_DONE anymore when commiting
+ an empty timeline, which means that we need to special case that.
+ This actually broke some code and does not bring in much.
+ Fixes https://phabricator.freedesktop.org/T7802
+ Fixes https://phabricator.freedesktop.org/T7797
+ This reverts commit e570d1e08009992a0dd6a24bb4cda4427b2b460f.
+ Thanks @stefanzzz for investigating!
+
+2017-08-22 14:23:45 +0000 Stefan Popa <stefanpopa2209@gmail.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Save encoder and muxer advanced settings
+ Added support for saving/loading encoder and muxer advanced settings.
+ Differential Revision: https://phabricator.freedesktop.org/D1837
+
+2017-08-19 11:42:57 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Allow up to 2 seconds queueing in the playsink video queue
+ In playsink the default video queue max size is 3 buffers, which is
+ sometimes not enough for our use case.
+ Allow up to 2 seconds of buffered data, giving us more time to do
+ the transition between clips, and thus avoiding dropping frames in
+ the sink when bringing up new clip takes too much time.
+ Differential Revision: https://phabricator.freedesktop.org/D1854
+
+2017-08-18 23:39:38 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ timeline: Add from first element with wanted start to the move context
+ We need to iterate over the previous element from trackelement_iter
+ to find the first element that is at the moving point. Several
+ elements can have the same start as the one initiating the move,
+ and we need to take all of them into account.
+ Fixes https://phabricator.freedesktop.org/T7819
+
+2017-08-18 23:18:10 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-validate.c:
+ validate: Add an action type to ungroup containers
+
+2017-08-17 12:26:24 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 48a5d85 to 3f4aa96
+
+2017-08-12 10:04:03 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-video-mixer.h:
+ * win32/common/libges.def:
+ Hide ges_smart_mixer_* API
+ The header file isn't installed anyway.
+
+2017-08-12 10:01:51 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ configure: bump gst-validate requirement to 1.12.1
+ For gst_validate_scenario_get_pipeline().
+
+2017-08-11 22:27:48 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: hide symbols by default unless explicitly exported
+
+2017-08-11 21:41:52 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-internal.h:
+ * tests/check/ges/asset.c:
+ tests: don't use private debug category in asset test
+ That will lead to undefined symbol errors once it no
+ longer gets exported.
+
+2017-08-11 21:40:14 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-adder.h:
+ * ges/ges-smart-video-mixer.h:
+ * ges/ges-timeline-element.h:
+ * ges/ges-video-track.h:
+ ges: sprinkle more GST_EXPORT
+
+2017-08-10 15:05:09 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-smart-video-mixer.c:
+ * plugins/nle/nlecomposition.c:
+ smartmixer: Give a unique name to each compositor instances
+
+2017-08-10 21:38:04 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Check subprocess return code in rendering tests
+
+2017-08-10 15:18:22 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * tests/validate/geslaunch.py:
+ validate: fix error message
+
+2017-08-10 13:46:03 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * docs/libs/meson.build:
+ * docs/meson.build:
+ meson: fix a few warnings
+
+2017-08-07 15:35:58 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-asset.h:
+ * ges/ges-audio-source.h:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-audio-track.h:
+ * ges/ges-audio-transition.h:
+ * ges/ges-audio-uri-source.h:
+ * ges/ges-base-effect-clip.h:
+ * ges/ges-base-effect.h:
+ * ges/ges-base-transition-clip.h:
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-clip-asset.h:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-command-line-formatter.h:
+ * ges/ges-container.h:
+ * ges/ges-effect-asset.h:
+ * ges/ges-effect-clip.h:
+ * ges/ges-effect.h:
+ * ges/ges-enums.h:
+ * ges/ges-extractable.h:
+ * ges/ges-formatter.h:
+ * ges/ges-group.h:
+ * ges/ges-image-source.h:
+ * ges/ges-internal.h:
+ * ges/ges-layer.h:
+ * ges/ges-meta-container.h:
+ * ges/ges-multi-file-source.h:
+ * ges/ges-operation-clip.h:
+ * ges/ges-operation.h:
+ * ges/ges-overlay-clip.h:
+ * ges/ges-pipeline.h:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-project.h:
+ * ges/ges-screenshot.h:
+ * ges/ges-smart-video-mixer.h:
+ * ges/ges-source-clip.h:
+ * ges/ges-source.h:
+ * ges/ges-test-clip.h:
+ * ges/ges-text-overlay-clip.h:
+ * ges/ges-text-overlay.h:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.h:
+ * ges/ges-title-clip.h:
+ * ges/ges-title-source.h:
+ * ges/ges-track-element-asset.h:
+ * ges/ges-track-element.h:
+ * ges/ges-track.h:
+ * ges/ges-transition-clip.h:
+ * ges/ges-transition.h:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-clip.h:
+ * ges/ges-utils.h:
+ * ges/ges-video-source.h:
+ * ges/ges-video-test-source.h:
+ * ges/ges-video-track.h:
+ * ges/ges-video-transition.h:
+ * ges/ges-video-uri-source.h:
+ * ges/ges-xml-formatter.h:
+ * ges/ges.h:
+ * win32/common/libges.def:
+ Mark symbols explicitly for export with GST_EXPORT
+ With two exceptions:
+ * ges_clip_create_track_elements_func
+ * ges_uri_clip_set_uri
+ which were never declared in headers and should always have been static.
+
+2017-08-03 17:03:31 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-video-transition.c:
+ transition: Fix usage of transition types != crossfade
+ We need to make sure the crossfade ratio is disabled in that case.
+
+2017-07-10 11:43:11 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-video-transition.c:
+ video-transition: Make use of the new `compositor::crossfade-ratio` property
+ To achieve a real transition about to handle several level of layers.
+ https://bugzilla.gnome.org/show_bug.cgi?id=784827
+
+2017-07-31 14:52:20 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * win32/common/libges.def:
+ win32: Update .def file
+
+2017-07-31 12:54:25 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-asset.h:
+ asset: Add a function to stop proxying an asset
+ And remove any reference as it beeing a proxy.
+
+2017-07-31 12:55:53 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-asset.c:
+ asset: Clear loading error when reload is requested
+
+2017-07-27 21:15:34 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Rename segment_start to current_stack_start
+ It is still not exactly precise, but gives a much better understanding
+ of what it is.
+
+2016-01-12 17:05:48 +0000 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Properly update segment->start/stop on commit
+ Otherwise they will just be the ones from the previous seek event/
+ stack setup and be meaningless.
+ Also document the priv->segment meaning.
+ Fixes https://phabricator.freedesktop.org/T7796
+
+2017-07-27 15:57:31 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ timeline: Remove trackelements initating move from the moving context
+ They are handled specially when moving the context and having them
+ part of the context can lead to weird behaviours.
+ Fixes https://phabricator.freedesktop.org/T7693
+
+2017-07-21 16:41:26 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Serialize encoding profiles in reverse order
+ So they are reloaded in the right order.
+
+2017-07-24 10:32:47 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Make sure tracks are unlinked on NULL->NULL state changes
+
+2017-07-24 10:34:48 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-pipeline.c:
+ * ges/ges-source.c:
+ Enhance some pad linking issue debug logging.
+
+2017-07-13 16:38:04 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Fix test names now that the launcher handles adding manager name
+
+2017-07-11 11:40:55 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/tempochange.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/track.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ tests: Initialize GES only once in the main process
+ Fixing calling deinit in a process where init was not called
+ when libcheck is forking.
+
+2017-07-10 21:42:21 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * ges/ges.c:
+ ges: Ref the GES class to avoid later deadlock
+ This ensure that that all class are initialized from the main thread,
+ avoid class initialization in random thread, which may cause deadlocks.
+ https://bugzilla.gnome.org/show_bug.cgi?id=784769
+
+2017-07-07 12:27:16 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: find python3 via python3 module
+ https://bugzilla.gnome.org/show_bug.cgi?id=783198
+
+2017-07-03 18:33:39 +0300 Stefan Popa <stefanpopa2209@gmail.com>
+
+ * ges/ges-timeline-element.c:
+ "deep-notify" signal gets emitted only from the main thread
+ https://bugzilla.gnome.org/show_bug.cgi?id=784414
+
+2017-06-30 16:18:17 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-video-transition.c:
+ video-transition: Make sure crossfade output never contains alpha
+ Otherwise it would get mixed with lower layers, which is totally
+ unexpected.
+ Fixes T7773
+ Differential Revision: https://phabricator.freedesktop.org/D1764
+
+2017-06-23 16:18:36 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * meson.build:
+ meson: Allow using glib as a subproject
+
+2017-06-23 16:04:01 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-timeline-element.c:
+ ges: Handle g_object_newv deprecation in latest GLib
+
+2017-06-09 20:15:26 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * Makefile.am:
+ Don't dist config.meson.h as it no longer exist
+
+2017-06-09 21:37:48 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * config.h.meson:
+ * meson.build:
+ meson: remove config.h.meson
+
+2017-06-07 12:08:00 -0400 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/meson.build:
+ meson: Do not use path separator in test names
+ Avoiding warnings like:
+ WARNING: Target "elements/audioamplify" has a path separator in its name.
+
+2017-04-28 16:41:42 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-validate.c:
+ validate: Port to new GstValidate API for pipeline retrieval
+
+2017-05-15 09:13:38 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-layer.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ ges: Correctly handling floating references
+ If we ref_sink() a parameter, it must be marked as (transfer floating)
+ and it also has to be handled consistently between error and normal cases.
+ See https://bugzilla.gnome.org/show_bug.cgi?id=782499
+ https://bugzilla.gnome.org/show_bug.cgi?id=782652
+
+2017-05-16 14:37:03 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * configure.ac:
+ * plugins/nle/Makefile.am:
+ Remove plugin specific static build option
+ Static and dynamic plugins now have the same interface. The standard
+ --enable-static/--enable-shared toggle are sufficient.
+
+2017-05-04 18:59:14 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ * meson.build:
+ Back to development
+
+=== release 1.12.0 ===
+
+2017-05-04 15:43:12 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.12.0
+
+=== release 1.11.91 ===
+
+2017-04-27 17:47:16 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.11.91
+
+2017-04-24 20:30:46 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 60aeef6 to 48a5d85
+
+2017-04-10 23:51:18 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From 39ac2f5 to 60aeef6
+
+=== release 1.11.90 ===
+
+2017-04-07 16:35:23 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ * meson.build:
+ Release 1.11.90
+
+2017-04-04 16:27:33 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * data/completions/ges-launch-1.0:
+ completion: Fix previous commit
+
+2017-04-04 16:20:17 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * data/completions/ges-launch-1.0:
+ completion: Fix listing commands
+ Executing a single string does not work in this context. Fixed using
+ a bash function instead.
+
+2017-04-04 15:48:05 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * data/completions/ges-launch-1.0:
+ completion: Remove incorrect fixmes
+ In fact the fixmes are incorrect since these are options that are
+ available when built against gst-validate, which I didn't. There is
+ reference to these options in the HELP_SUMMARY that refers to these
+ options stating "if ges-launch is built with gst-validate ..." and these
+ get picked by the regex that list the options.
+
+2017-04-04 14:55:18 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * data/completions/ges-launch-1.0:
+ completion: Update to new gstreamer core helpers
+ Also fix regressions, and mark lost features and problems with fixmes.
+
+2017-04-02 23:03:18 +0200 Corentin Noël <corentin@elementary.io>
+
+ * ges/ges-layer.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-timeline.c:
+ Tiny fixes in the documentation
+ https://bugzilla.gnome.org/show_bug.cgi?id=780854
+
+2017-03-30 19:57:06 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * win32/common/libges.def:
+ Add missing win32 definition
+ This should fix dist check. ges_layer_get_clips_in_interval() was
+ added recently but missing from the list.
+
+2017-03-28 14:25:06 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/meson.build:
+ meson: Use get_pkgconfig_variable instead of calling pkg-config ourself
+ It is avalaible in meson 0.36 which is now are requirement
+ Nothing happens on not found dependencies.
+
+2017-03-25 10:47:16 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/meson.build:
+ meson: test: Fix environment object usage
+ And make sure to bring -good plugins in.
+
+2017-03-17 19:02:56 +0000 suhas2go <suhas2go@gmail.com>
+
+ * ges/ges-layer.c:
+ * ges/ges-layer.h:
+ * tests/check/ges/layer.c:
+ layer: Add ability to get clips in a given interval
+ Reviewed-by: Alex Băluț <<alexandru.balut@gmail.com>>
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1689
+
+2017-03-13 09:30:39 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-audio-source.c:
+ * ges/ges-title-source.c:
+ * ges/ges-transition.c:
+ * ges/ges-video-source.c:
+ docs: Fix generation using markdown for titles around tables
+
+2017-03-10 19:46:33 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/hotdoc/images/layer_track_overview.png:
+ * docs/hotdoc/index.md:
+ * docs/hotdoc/meson.build:
+ * docs/hotdoc/sitemap.txt:
+ * docs/meson.build:
+ Revert "doc: Build documentation with hotdoc"
+ This reverts commit 8857e004f78ea009e1c87a93da5cf3e25dbde07f.
+ This was not meant to be pushed yet.
+
+2017-03-10 19:46:24 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/hotdoc/index.md:
+ * docs/hotdoc/meson.build:
+ * ges/meson.build:
+ * meson.build:
+ Revert "Fhotdoc"
+ This reverts commit 220618ecc7c061a2146e00e0063123b8dbaeb734.
+ This was not meant to be pushed.
+
+2017-03-10 19:46:09 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/hotdoc/base-classes.md:
+ * docs/hotdoc/low_level.md:
+ * docs/hotdoc/meson.build:
+ * docs/hotdoc/sitemap.txt:
+ Revert "HOTDOC"
+ This reverts commit 5e251483ee6777b6a74a7988b5969bf95f6ecab6.
+ This was not meant to be pushed.
+
+2017-03-10 19:34:21 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ ges: Minor GESTimeline documentation fix
+
+2017-03-09 17:49:44 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/hotdoc/base-classes.md:
+ * docs/hotdoc/low_level.md:
+ * docs/hotdoc/meson.build:
+ * docs/hotdoc/sitemap.txt:
+ HOTDOC gi
+
+2017-03-09 13:11:37 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-asset.h:
+ * ges/ges-extractable.h:
+ extractable: Typedef only in its own .h
+
+2017-03-08 18:26:42 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/hotdoc/index.md:
+ * docs/hotdoc/meson.build:
+ * ges/meson.build:
+ * meson.build:
+ Fhotdoc
+
+2017-03-08 18:13:48 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-transition.c:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-transition-clip.c:
+ * ges/ges-clip-asset.c:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-enums.c:
+ * ges/ges-extractable.c:
+ * ges/ges-formatter.c:
+ * ges/ges-gerror.h:
+ * ges/ges-group.c:
+ * ges/ges-image-source.c:
+ * ges/ges-layer.c:
+ * ges/ges-layer.h:
+ * ges/ges-multi-file-source.c:
+ * ges/ges-operation-clip.c:
+ * ges/ges-operation.c:
+ * ges/ges-overlay-clip.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-source.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-test-clip.h:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay-clip.h:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ * ges/ges-track-element-asset.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-utils.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-track.c:
+ * ges/ges-video-transition.c:
+ * ges/ges-video-uri-source.c:
+ docs: Port all docstring to gtk-doc markdown
+
+2017-03-08 18:02:47 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/hotdoc/images/layer_track_overview.png:
+ * docs/hotdoc/index.md:
+ * docs/hotdoc/meson.build:
+ * docs/hotdoc/sitemap.txt:
+ * docs/meson.build:
+ doc: Build documentation with hotdoc
+
+2017-03-06 08:53:00 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ timeline: Return FALSE when commiting an empty timeline
+ Meaning that ASYNC_DONE/COMMITED is always emited when TRUE is returned
+
+2017-02-28 15:39:27 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/gstframepositioner.c:
+ framepositioner: Prevent division by zero
+ CID 1369046
+ CID 1369047
+
+2017-02-28 15:36:46 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-timeline.c:
+ ges-timeline: Document intentional case-fall-through
+ CID 1364754
+
+2017-02-28 13:02:44 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * examples/c/ges-ui.c:
+ ges-ui: Ensure that string is \0-terminated
+ CID 1320699
+
+2017-02-28 12:59:35 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-project.c:
+ ges-project: Check for set/unset error correctly by dereferencing
+ ... or simply calling g_clear_error() on it which does that for us.
+ CID 1257630
+
+2017-02-28 12:50:31 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * examples/c/ges-ui.c:
+ examples/ges-ui: Remove useless NULL check
+ g_new0() will abort if allocation fails.
+ CID 1139842
+
+2017-02-24 15:44:36 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-container.h:
+ * ges/ges-internal.h:
+ * ges/ges-uri-asset.h:
+ * win32/common/libges.def:
+ ges: Keep internal symbols internal
+ The following implementation details where exposed as public symbols:
+ - _ges_container_get_priority_offset
+ - _ges_container_set_height
+ - _ges_container_set_priority_offset
+ - _ges_uri_asset_cleanup
+ but it was not correct and that should never have been used outside
+ GES.
+ Moving those declarations to the internal header and marking as
+ internal.
+
+2017-02-24 16:00:09 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * meson.build:
+ meson: Update version
+
+2017-02-24 15:37:55 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.11.2 ===
+
+2017-02-24 15:10:01 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.11.2
+
+2017-02-15 12:52:24 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * meson.build:
+ * meson_options.txt:
+ meson: Add an option to disable doc generation
+
+2017-02-15 12:51:51 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-meta-container.c:
+ Minor documentation fix
+
+2017-02-15 00:58:52 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * Makefile.am:
+ meson: dist meson build files
+ Ship meson build files in tarballs, so people who use tarballs
+ in their builds can start playing with meson already.
+
+2017-02-07 11:18:58 +0100 Edward Hervey <edward@centricular.com>
+
+ * win32/common/libges.def:
+ win32: Update def file
+
+2017-02-06 13:18:32 +0100 Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
+
+ * tests/check/ges/mixers.c:
+ mixers: fix leaks in tests
+ - GstMessage and GstBus references were lost
+ - Need to call gst_bus_remove_signal_watch() for each
+ gst_bus_add_signal_watch_full() call
+ https://bugzilla.gnome.org/show_bug.cgi?id=778248
+
+2017-02-06 10:05:11 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-meta-container.c:
+ ges-meta: Minor documenation fix
+
+2017-02-06 12:07:26 +0100 Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/mixers.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/tempochange.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/track.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/check/nle/complex.c:
+ * tests/check/nle/nlecomposition.c:
+ * tests/check/nle/nleoperation.c:
+ * tests/check/nle/simple.c:
+ * tests/check/nle/tempochange.c:
+ introduce ges_deinit()
+ GstDiscoverer objects were leaked by tests making the leaks detector
+ unusable.
+ Introduce ges_deinit(), similiar to gst_deinit(), doing some cleanup
+ before exiting the process.
+ https://bugzilla.gnome.org/show_bug.cgi?id=776805
+
+2017-02-04 20:15:55 +0000 namanyadav12 <namanyadav128@gmail.com>
+
+ * ges/ges-uri-asset.c:
+ uri-clip-asset: Add file-size metadata
+ Add file-size metadata to GESUriClipAsset.
+ Reviewed-by: Thibault Saunier <thibault.saunier@osg.samsung.com>
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1645
+
+2017-02-03 12:50:11 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ timeline: Cleanup the moved_clip list before rolling back
+ Otherwise we might end up using an already freed pointer
+ Differential Revision: https://phabricator.freedesktop.org/D1640
+
+2017-01-13 12:41:51 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ Revert "meson: don't use subproject fallback for gst-validate if it won't work"
+ This reverts commit 6760e5e0b1b2f28fb04e9c430506af56c15432b9.
+ This was not supposed to be pushed and should not be needed any more.
+
+2017-01-13 12:39:42 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: bump version
+
+2016-10-29 16:24:53 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: don't use subproject fallback for gst-validate if it won't work
+ gst-validate has a hard-dep on json-glib-1.0 so maintain optionality of
+ it all by only dragging it in as a fallback if we know we can satisfy
+ the dependencies.
+
+2017-01-12 16:33:06 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.11.1 ===
+
+2017-01-12 16:20:08 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.11.1
+
+2017-01-09 12:12:34 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-timeline-element.c:
+ * tests/check/ges/timelineedition.c:
+ clip: Make sure that clip start change is notified before children changes
+ Fixes https://phabricator.freedesktop.org/T7577
+ Differential Revision: https://phabricator.freedesktop.org/D1600
+
+2016-10-19 15:36:49 +0000 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * tests/check/python/test_timeline.py:
+ tests_: Check the order of signals when a transition is created
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1391
+
+2016-12-21 12:22:31 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-asset.c:
+ asset: Fix set_proxy to abort when an error happens
+ Differential Revision: https://phabricator.freedesktop.org/D1574
+
+2016-12-21 11:39:12 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-asset.c:
+ asset: Reuse local variable
+ Differential Revision: https://phabricator.freedesktop.org/D1573
+
+2017-01-04 15:55:36 +0100 Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
+
+ * pkgconfig/Makefile.am:
+ * pkgconfig/gst-editing-services-uninstalled.pc.in:
+ * pkgconfig/meson.build:
+ meson: generate pkg-config -uninstalled pc files
+ Generating those files is useful for users building the GStreamer stack
+ using meson and having to link it to another project which is still
+ using the autotools.
+ https://bugzilla.gnome.org/show_bug.cgi?id=776810
+
+2016-12-23 15:08:06 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tools/utils.c:
+ ges-launch: Use standard GstEncodingProfile deserialization function
+
+2016-12-22 10:00:06 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Remove space breaking muting ges-launch
+
+2016-12-22 09:48:58 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-video-transition.c:
+ video-mixer: Fix the way we release mixer pads
+ We were using the actual mixer pad to release the smart mixer
+ pad, which seemed to be on purpose, but was not properly handle,
+ moreover, it is now forbiden to pass a pad not inside a GstElement
+ when releasing it.
+ Also properly remove ghost pads from Smart mixer, we were planly
+ failling at it.
+
+2016-12-22 08:44:07 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tools/meson.build:
+ meson: Install ges-launch
+
+2016-12-16 17:29:59 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * .gitignore:
+ * Makefile.am:
+ * configure.ac:
+ * gst-editing-services.spec.in:
+ Remove generated .spec file
+ Likely extremely bitrotten, and we should not ship this anyway.
+
+2016-12-16 14:04:06 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/text_properties.c:
+ * tests/check/meson.build:
+ tests: Remove now meaningless empty testsuite
+
+2016-12-13 16:05:17 +0100 Antonio Ospite <ao2@ao2.it>
+
+ * tools/Makefile.am:
+ * tools/ges-launch-1.0.1:
+ * tools/meson.build:
+ ges: add a basic unix man page for ges-launch-1.0
+ Do not list all the possible options in the man page but only the help
+ options.
+ This is in order to avoid duplication and prevent the man page from
+ becoming obsolete in case the options change in the code but do not get
+ updated in the man page.
+ https://bugzilla.gnome.org/show_bug.cgi?id=776063
+
+2016-12-13 15:10:26 +0100 Antonio Ospite <ao2@ao2.it>
+
+ * ges/ges.c:
+ ges: fix the description of the --help-GES command line option
+ Use "Show GES Options" which is more appropriate and avoids duplication
+ with --help-gst which already says "Show GStreamer Options".
+ https://bugzilla.gnome.org/show_bug.cgi?id=776063
+
+2016-12-12 16:59:08 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/ges/uriclip.c:
+ tests: Make sure tests can be listed
+ Initializing GstCheck before creating the testsuite
+
+2016-12-13 23:26:23 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tools/ges-launcher.c:
+ ges-launch: Make sure GStreamer is always initialized
+ https://bugzilla.gnome.org/show_bug.cgi?id=776064
+
+2016-12-09 17:50:28 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * meson.build:
+ meson: Support building without Gst debug
+
+2016-12-03 08:21:07 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * common:
+ Automatic update of common submodule
+ From 1416c16 to 39ac2f5
+
+2016-12-01 17:08:43 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ element: Rework set_child_property_by_pspec
+ It was making no sense to loose the information about the pspec itself
+ to retrieve the child associated to it and was failling when we were
+ forcing the AssociateType::prop synthax
+
+2016-12-01 15:46:51 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/nle/nleoperation.c:
+ test:nle: Soften check on refcount
+ The composition might already have taken a new ref processing the
+ source.
+
+2016-11-30 09:53:38 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Deactivate current stack in PAUSED_READY state
+ To avoid a race when tearing down the composition (PAUSED_TO_READY),
+ we should make sure to tear down the current stack and let the GstBin
+ class handle the remaining thing to do during the change state.
+ We should still ignore any error happening when tearing down the
+ bin state just in case.
+ https://bugzilla.gnome.org/show_bug.cgi?id=775051
+
+2016-11-30 09:53:21 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Deinit Gst before exiting
+
+2016-11-29 10:37:11 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * meson.build:
+ meson: Do not print error logs when building the gir
+
+2016-11-28 17:07:39 -0800 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * examples/c/assets.c:
+ examples: remove #include <ges-internal.h> from assets.c
+ It is not needed and pulling it in is causing a link problem with msvc.
+ Including ges-internal.h sets the default debug category in assets.c to
+ _ges_debug. Because _ges_debug is marked as DATA in the libges.def, it
+ will only be linked from libges.dll if it is marked in the source with
+ dllimport. Instead of messing with that we can just remove this include.
+ https://bugzilla.gnome.org/show_bug.cgi?id=775295
+
+2016-11-26 11:25:41 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * .gitmodules:
+ common: use https protocol for common submodule
+ https://bugzilla.gnome.org/show_bug.cgi?id=775110
+
+2016-11-23 18:42:27 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-timeline.c:
+ ges-timeline: Properly calculate absolute diff of two unsigned integers
+ CID 1394491.
+
+2016-11-23 18:28:35 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges.c:
+ ges: Add NULL check before dereferencing
+ CID 1394494.
+
+2016-11-20 15:34:46 +0100 Philippe Renon <philippe_renon@yahoo.fr>
+
+ * ges/ges-uri-asset.c:
+ ges-uri-asset: fix compile error 'timeout' may be used uninitialized
+ https://bugzilla.gnome.org/show_bug.cgi?id=774751
+
+2016-11-18 10:21:45 -0800 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * Makefile.am:
+ * win32/MANIFEST:
+ * win32/common/libges.def:
+ make: include common/win32.mak
+ With the addition of the .def file for libges we need to make
+ sure the check-export script from common gets executed so that the
+ .def stays up to date.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774641
+
+2016-11-18 16:55:17 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * common:
+ Update common submodule
+
+2016-11-17 10:31:50 -0800 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * ges/meson.build:
+ * meson.build:
+ * win32/common/libges.def:
+ Enable building with MSVC
+ https://bugzilla.gnome.org/show_bug.cgi?id=774641
+
+2016-11-17 10:40:05 -0800 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * ges/gstframepositioner.c:
+ Cast away const from GstMetaInfo in *_get_meta_info() functions
+ MSVC warns about the const in the implicit argument conversion in the
+ calls to g_once_init_{enter,leave}. It's OK so explicitly cast it.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774641
+
+2016-11-17 10:39:01 -0800 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-validate.c:
+ Pass gint/guint pointers instead of enum pointers
+ The underlying integer type for enums are implementation defined and may
+ not be the same size as gint/guint. So implicitly casting from pointers-
+ to-enum-types to pointers-to-int-types is unsafe. MSVC warns on these.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774641
+
+2016-11-17 10:35:50 -0800 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * ges/ges-command-line-formatter.c:
+ * ges/parse.l:
+ parse: Don't #include <unistd.h>
+ It isn't needed and isn't present in non-posix environments like windows
+ with MSVC or mingw.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774641
+
+2016-11-17 09:40:38 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlecomposition.c:
+ Revert "nlecomposition: Start task and initialize the stack after chaining up to parent's change state function"
+ This reverts commit 57d40bec1a3c5048baaad08403d7b7e641a9c55c.
+ Apparently it causes timeouts in the unit tests on Jenkins and
+ Thibault's machine, and in the gst-validate tests.
+ Caused by elements staying in PAUSED and waiting to be set to PLAYING.
+ Needs further investigation.
+
+2016-11-17 09:40:33 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nleobject.c:
+ Revert "nleobject: Start up in NULL->READY->PAUSED after the parent class did"
+ This reverts commit 5f7943c59d9def8c2dc9983936463462c1cdf63f.
+
+2016-11-16 18:11:00 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nleobject.c:
+ nleobject: Start up in NULL->READY->PAUSED after the parent class did
+ This keeps everything in a more consistent order and makes sure that the
+ base class is already set up completely before we start doing anything.
+ It also prevents from doing any setup if the base class fails, and
+ possibly not shutting things down again then.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774480
+
+2016-11-15 17:56:00 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Start task and initialize the stack after chaining up to parent's change state function
+ Otherwise we could set the state of the children to PAUSED already (i.e.
+ start dataflow) from the composition's task, while the composition
+ itself is currently chaining up to the parent class' change state
+ function and did not activate the pads yet. This causes buffers and
+ events to be discarded, and everything to stop with a not-negotiated
+ error.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774480
+
+2016-11-15 18:34:44 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/meson.build:
+ * tests/check/nose2-junit-xml.cfg.in:
+ tests: Fix running python unit tests
+ Adding missing nose2-junit-xml.cfg.in file and minor fixes in
+ the way we call nose2 also making sure the .xunit files end
+ up in the right place.
+
+2016-11-15 15:09:10 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-uri-asset.c:
+ * ges/ges.c:
+ ges: Check if GstDiscoverer could be created at init time
+ And fail initialization if it is not the case, we make the assumption
+ it worked all around the codebase so we should really concider it fatal.
+
+2016-11-10 15:17:50 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Fix small remaining race in previous commit
+ The seek action might currently be handled (in which case it is not in
+ the actions list and the action lock is not locked), but not actually
+ handled completely yet (the seqnum is not stored yet).
+ To prevent this, we remember what the current action is that is being
+ handled, and also compare to that.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774149
+
+2016-10-19 16:34:56 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * configure.ac:
+ * tests/check/Makefile.am:
+ * tests/check/meson.build:
+ tests_: Use nose2 instead of nosetests
+ Differential Revision: https://phabricator.freedesktop.org/D1394
+
+2016-10-19 12:36:45 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ ges: Fix documentation and debug comments
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1393
+
+2016-01-12 14:51:55 +0000 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-timeline.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/python/test_clip.py:
+ * tests/check/python/test_timeline.py:
+ timeline: reimplement snap_to_position a bit more appropriately.
+ It could yet be made be simpler, but it would require
+ touching the rest of the timeline editing code.
+ Fixes https://phabricator.freedesktop.org/T7587
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D657
+
+2016-11-09 17:14:19 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: De-duplicate seek events based on their sequence number
+ If there are e.g. multiple video sinks, we would get the same seek event
+ multiple times. But we only want to handle it once.
+ https://bugzilla.gnome.org/show_bug.cgi?id=774149
+
+2016-11-07 18:01:51 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/validate/geslaunch.py:
+ test:validate: Port to python3
+
+2016-11-09 11:48:09 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-timeline.c:
+ ges-timeline: Fix typo in debug messages
+
+2016-10-08 10:43:07 +0200 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ * tests/check/python/test_timeline.py:
+ timeline: Avoid creating extra transition when rippling clips
+ In some cases when rippling clip we could get the algo lost because
+ a transition existed between two clips (for example at the end of c1
+ and at the begining of c2) but while rippling it would have required
+ a transition at the end of c2 and beginning of c1, and we were properly
+ not destroying the old one (as the two clips were in the moving context)
+ but we were still creating the other transition in the end...
+ Reviewed-by: Alex Băluț <alexandru.balut@gmail.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1362
+
+2016-10-07 15:31:40 +0200 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-timeline.c:
+ * tests/check/python/test_timeline.py:
+ timeline: Make sure transitions between rippled clips are never deleted
+ Reviewed-by: Alex Băluț <alexandru.balut@gmail.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1361
+
+2016-10-06 19:14:57 +0200 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-layer.c:
+ * ges/ges-timeline.c:
+ * tests/check/python/test_timeline.py:
+ timeline: Destroy transition if a neighbor is not being moved to a layer
+ And make sure that we move the transition to the right layer, not trying
+ to figure it out.
+ Differential Revision: https://phabricator.freedesktop.org/D1360
+
+2016-10-06 14:00:23 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * tests/check/python/test_clip.py:
+ * tests/check/python/test_timeline.py:
+ tests_: Check transition is gone when editing clip to another layer
+ Differential Revision: https://phabricator.freedesktop.org/D1359
+
+2016-11-04 14:41:13 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * meson.build:
+ meson: Unset the plugin paths to generate the .gir files
+ Avoiding problems when using subproject:
+ 'Failed to load plugin something.so file too short'
+
+2016-11-01 18:10:47 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: update version
+
+=== release 1.11.0 ===
+
+2016-11-01 18:53:15 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.10.0 ===
+
+2016-11-01 18:12:35 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.10.0
+
+2016-10-25 08:54:11 -0700 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * meson.build:
+ meson: Don't depend on gstreamer-check-1.0 on windows
+ https://bugzilla.gnome.org/show_bug.cgi?id=773114
+
+2016-10-25 11:48:35 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * meson.build:
+ * tests/check/meson.build:
+ Revert "meson: move gstreamer-check-1.0 dependency to tests/check"
+ This reverts commit 5665c2bfc9cae531c6dd9a75766d06a4af25ab9a.
+ Does not actually work. See:
+ https://bugzilla.gnome.org/show_bug.cgi?id=773114#c31
+
+2016-10-21 05:49:18 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * meson.build:
+ * tests/check/meson.build:
+ meson: move gstreamer-check-1.0 dependency to tests/check
+
+2016-10-17 09:34:27 -0700 Scott D Phillips <scott.d.phillips@intel.com>
+
+ * meson.build:
+ meson: mark gstreamer-check-1.0 as required: false
+
+2016-10-15 22:21:24 +0530 Nirbheek Chauhan <nirbheek@centricular.com>
+
+ * meson.build:
+ meson: Don't set c_std to gnu99
+ Use the default for each compiler on every platform instead. This
+ improves our compatibility with compilers that don't have gnu99 as
+ a c_std.
+
+2016-10-03 17:44:04 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * meson.build:
+ * tests/check/getpluginsdir:
+ * tests/check/meson.build:
+ meson: Use environment object to setup test environment variables
+ Bump meson requirement to 0.35
+
+2016-10-11 00:59:47 +0200 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-track-element.c:
+ track-element: Avoid dereferencing NULL pointer
+ We set TrackElement track type very early when creating effects
+ so it now uses that information to find TrackElement in clips
+ by track type.
+ Reviewed-by: Alex Băluț <alexandru.balut@gmail.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1370
+
+2016-09-13 12:31:54 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/meson.build:
+ meson: Add python tests
+
+2016-09-30 11:35:42 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * hooks/pre-commit.hook:
+ * meson.build:
+ * tests/check/getpluginsdir:
+ meson: Setup pre commit hook and fix getpluginsdir for standalone case
+
+2016-09-30 14:56:48 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * meson.build:
+ meson: update version
+
+=== release 1.9.90 ===
+
+2016-09-30 13:04:39 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.9.90
+
+2016-09-23 20:41:04 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * docs/libs/meson.build:
+ * docs/meson.build:
+ * ges/meson.build:
+ meson: Fix gtkdoc using new meson features
+
+2016-09-21 16:41:31 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/meson.build:
+ meson: Fix installing configured files
+
+2016-08-13 19:54:22 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-uri-clip.c:
+ uriclip: Remove some filesource leftovers
+ Differential Revision: https://phabricator.freedesktop.org/D1329
+
+2016-08-13 21:09:53 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/python/__init__.py:
+ * tests/check/python/common.py:
+ * tests/check/python/test_clip.py:
+ * tests/check/python/test_group.py:
+ * tests/check/python/test_timeline.py:
+ tests_:python: Factor out common code
+ Differential Revision: https://phabricator.freedesktop.org/D1328
+
+2016-09-17 09:46:59 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * plugins/nle/nlecomposition.c:
+ nle: Drop tags getting out of the composition
+ Those tag are meaningless in for the new stream created by the composition
+ First step toward fixing T3070
+ Differential Revision: https://phabricator.freedesktop.org/D1327
+
+2016-08-11 15:12:07 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ * tests/check/python/test_group.py:
+ timeline: Properly compute the end of groups when checking snapping
+ Computation was not taking into account the fact that the start of
+ the element being moved could be at the middle of a group and not
+ necessarily at the start!
+ Fixes T7544
+ Reviewed-by: Alex Băluț <alexandru.balut@gmail.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1282
+
+2016-08-11 13:19:44 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-auto-transition.c:
+ * tests/check/python/test_group.py:
+ ges: Handle moving groups with effects inside
+ We were only concidering that we should let the group handle moving
+ transitions when changing transitions but in fact as soon as a
+ transition is happenning between two clips that are in a same group
+ the group properly handles moving the transition, so let the
+ group do its job.
+ Fixes T7543
+ Differential Revision: https://phabricator.freedesktop.org/D1281
+
+2016-08-11 10:54:08 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ ges: Deprecate GESTimelineElement::priority writability
+ GESLayer is now responsible for setting clips priorites. Also
+ GESClip top effects priorities are now set by the
+ ges_clip_set_top_effect_index method, the user should never call
+ ges_timeline_element_set_priority as it will anyway be overriden
+ by GES itself.
+ Differential Revision: https://phabricator.freedesktop.org/D1280
+
+2016-08-11 10:36:44 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-layer.c:
+ layer: Handle operation priorities
+ All operations should have higher priorites and sources should be
+ on top of those. We now first set the operations priorities in
+ a first pass and then stack sources on top of those.
+ Differential Revision: https://phabricator.freedesktop.org/D1279
+
+2016-08-11 09:53:58 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-transition.c:
+ ges: transition: Make crossfade fade out at the same time as it fade in
+ Until now fade out was just fading in the new clip, but this is not
+ correct and crossfade should at the same time fade out while fading
+ in.
+ Fixes https://phabricator.freedesktop.org/T3451
+ Differential Revision: https://phabricator.freedesktop.org/D1278
+
+2016-08-11 09:42:32 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-layer.c:
+ * ges/ges-timeline.c:
+ layer: Make sure to resync priorities on commit
+ In case effects have been added priorites might become wrong,
+ but until the timeline is not commited, it does not matter.
+ Make sure all priorities are correct before commiting compositions
+ Differential Revision: https://phabricator.freedesktop.org/D1277
+
+2016-08-11 09:14:42 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-auto-transition.c:
+ * ges/ges-layer.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/uriclip.c:
+ * tests/check/python/test_clip.py:
+ Finally move clip priority handling to GESLayer.
+ Fix all tests as we now have 1 priority inside the layer
+ dedicated to transitions (basically no source clip will
+ ever have a priority of 0 inside a layer).
+ Differential Revision: https://phabricator.freedesktop.org/D1276
+
+2016-08-11 08:54:23 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * tests/check/ges/effects.c:
+ clip: Make top effect priority inside the clip priority range
+ And simplify the way we start computing children priority
+ making min_priority already relative to the clip itself.
+ Differential Revision: https://phabricator.freedesktop.org/D1275
+
+2016-08-11 07:54:42 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/ges/timelineedition.c:
+ tests_: timelineedition: Fix test now that we ripple from start and not from end
+ Differential Revision: https://phabricator.freedesktop.org/D1274
+
+2016-09-22 11:28:21 -0400 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Don't try to seek on an empty stack
+ We would seek on a NULL pad then, which gives ugly assertions.
+ https://bugzilla.gnome.org/show_bug.cgi?id=771843
+
+2016-09-22 11:25:18 -0400 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nleurisource.c:
+ * plugins/nle/nleurisource.h:
+ nleurisource: Always provide a srcpad
+ By putting uridecodebin into a bin with a ghostpad. Without this,
+ nlesource tries to get a srcpad too early (before uridecodebin added
+ one) and everything fails miserably.
+ This has to be fixed properly in nlesource at some point, by properly
+ handling dynamically added pads. Currently they can only work if they
+ are added in states <= READY, which is not the usual case.
+ https://bugzilla.gnome.org/show_bug.cgi?id=771843
+
+2016-09-21 18:23:56 -0400 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlesource.c:
+ nlesource: Fail prepare() if no valid source pad is found
+ https://bugzilla.gnome.org/show_bug.cgi?id=771792
+
+2016-09-14 14:32:19 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tools/utils.c:
+ ges-launch: Be a bit more agressive sanitizing arguments
+ Otherwise GstStructure might fail parsing some fields
+ containing brackets
+ https://bugzilla.gnome.org/show_bug.cgi?id=771434
+
+2016-09-14 11:31:23 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ configure: Depend on gstreamer 1.9.2.1
+
+2016-09-06 14:27:38 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-group.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-xml-formatter.c:
+ * tests/check/python/common.py:
+ * tests/check/python/test_group.py:
+ * tests/check/python/test_timeline.py:
+ timeline: Make get_groups public
+ Had to separate timeline_emit_group_added from timeline_add_group
+ to avoid emitting group-added when the project is being loaded.
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1302
+
+2016-09-06 15:49:49 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * tests/check/python/test_group.py:
+ tests_: Make sure child-removed is emitted when ungrouping
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1301
+
+2016-09-06 13:03:11 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-timeline.c:
+ timeline: Fix documentation
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1300
+
+2016-09-05 12:23:30 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * README:
+ Remove obsolete dependency
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D1299
+
+2016-09-10 20:52:38 +1000 Jan Schmidt <jan@centricular.com>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From b18d820 to f980fd9
+
+2016-09-10 09:58:37 +1000 Jan Schmidt <jan@centricular.com>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From f49c55e to b18d820
+
+2016-09-09 17:14:43 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/Makefile.am:
+ tests: Move -DGES_TEST_FILES_PATH to common_cflags
+ As it is needed to build the utils lib
+
+2016-09-09 16:42:13 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/test-utils.c:
+ * tests/check/meson.build:
+ tests: Fix the way we get tests assets
+ Using __FILE__ won't work properly with meson.
+
+2016-09-09 08:52:32 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * ges/meson.build:
+ * tests/validate/geslaunch.py:
+ test:validate: Handle new expected_failures Test argument
+
+2016-09-07 16:53:06 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * meson.build:
+ * tests/check/meson.build:
+ meson: Do not build libges against libcheck
+
+2016-09-05 17:55:42 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/check/getpluginsdir:
+ * tests/check/meson.build:
+ meson: Properly find where other GStreamer plugins are when using subprojects
+
+2016-09-05 14:54:53 -0300 Thibault Saunier <thibault.saunier@osg.samsung.com>
+
+ * tests/validate/geslaunch.py:
+ Revert "validate: Blacklist racy tests"
+ This reverts commit ce35412ff260fbd6e07b374bc3ca677053c277e0.
+ https://bugzilla.gnome.org/show_bug.cgi?id=769894 has been fixed
+
+2016-08-26 19:55:33 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/meson.build:
+ * meson.build:
+ * meson_options.txt:
+ meson: Handle building the gir file when used as subproject
+ Add support for building GIR when used as subproject
+ Add an option to disable GIR generation
+ And bump version to 1.9.2
+
+2016-09-01 12:33:22 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.9.2 ===
+
+2016-09-01 12:33:13 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.9.2
+
+2016-08-25 15:04:54 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/meson.build:
+ * meson.build:
+ meson: doc: Fix building documentation when using subprojects
+ And check the presence of gtk-doc before building the documentation
+
+2016-08-25 10:06:51 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * .gitignore:
+ * ges/Makefile.am:
+ ges: Rename parse_lex.h to ges-parse-lex.h
+ Fixes the build and makes it consistent with the meson build system.
+
+2016-08-04 17:33:55 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * .gitignore:
+ * config.h.meson:
+ * docs/libs/meson.build:
+ * docs/meson.build:
+ * examples/c/meson.build:
+ * examples/meson.build:
+ * ges/ges-command-line-formatter.c:
+ * ges/meson.build:
+ * meson.build:
+ * pkgconfig/meson.build:
+ * plugins/meson.build:
+ * plugins/nle/meson.build:
+ * tests/check/meson.build:
+ * tests/meson.build:
+ * tools/meson.build:
+ Add support for Meson as alternative/parallel build system
+ https://github.com/mesonbuild/meson
+
+2016-08-18 18:43:08 +0200 Philippe Renon <philippe_renon@yahoo.fr>
+
+ * ges/ges-track-element.c:
+ ges-track-element: fix typos in control_binding_removed signal declaration
+ https://bugzilla.gnome.org/show_bug.cgi?id=770101
+
+2016-08-14 17:45:16 +0200 Edward Hervey <edward@centricular.com>
+
+ * tests/validate/geslaunch.py:
+ validate: Blacklist racy tests
+ See https://bugzilla.gnome.org/show_bug.cgi?id=769894
+
+2016-08-14 17:44:36 +0200 Edward Hervey <edward@centricular.com>
+
+ * ges/ges-timeline.c:
+ ges-timeline: Demote some debugging statements
+ locking should be in a lower level to avoid too many messages
+
+2016-08-13 11:08:34 +0200 Edward Hervey <edward@centricular.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Reset pointer after freeing
+ dispose can be called multiple times, make sure we don't call functions
+ on free'd pointers.
+
+2016-08-03 11:40:30 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/gstframepositioner.c:
+ * tests/check/ges/timelineedition.c:
+ ges: Do not rescale videos if the track aspect ratio changes
+ Differential Revision: https://phabricator.freedesktop.org/D1242
+
+2016-08-02 16:42:20 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-source.h:
+ * ges/gstframepositioner.c:
+ * ges/gstframepositioner.h:
+ ges: Let the compositor do the scaling if mixing is enabled
+ Differential Revision: https://phabricator.freedesktop.org/D1241
+
+2016-08-01 12:55:07 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/validate/geslaunch.py:
+ tests:validate: Also test opus and theora in OGG
+
+2016-07-29 15:48:28 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ * tests/validate/geslaunch.py:
+ validate: Start also testing jpeg encoding
+
+2016-07-28 21:50:58 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Keep transitions when moving the moving context between layers
+ Differential Revision: https://phabricator.freedesktop.org/D1225
+
+2015-10-15 22:13:30 +0000 Justin Kim <justin.kim@collabora.com>
+
+ * tools/ges-launcher.c:
+ ges-launcher: don't leak project uri string
+ ges_project_get_uri returns a cloned string so it should
+ be free'd after usage.
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D381
+
+2016-07-28 19:30:28 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ * tests/check/ges/layer.c:
+ timeline: Ripple from start of clips and not the end.
+ Fixes https://phabricator.freedesktop.org/T7503
+
+2016-07-28 17:23:31 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ enums: Fix absolute text overlay alignment value
+ It needs to be in sync with GstBaseTextOverlayHAlign order.
+
+2016-07-28 16:04:42 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ title-source: Properly implement GESTimelineElement->lookup_child
+ GESTrackElement->lookup_child is deprecated and should be avoided
+ as much as possible.
+
+2016-07-28 16:02:05 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ clip: Reimplement look_child and iterate over children if needed
+ Otherwise in the case where children reimplement lookup_child to
+ handle some property renaming lookup fails.
+
+2016-07-28 14:24:07 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-command-line-formatter.h:
+ * ges/ges-internal.h:
+ * tools/ges-launch.c:
+ tools: Fix printing commands help
+
+2016-07-26 14:05:06 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges:validate: Check that no extra decoding happens
+
+2016-07-26 11:59:39 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-video-uri-source.c:
+ uri-sources: Make sure to set decodebin 'caps' property
+ Fixes a regression where we decode streams twice,
+ this was introduced when we started creating NLE
+ object at GESTrackElement construct time.
+ Fixes https://bugzilla.gnome.org/show_bug.cgi?id=769193
+
+2016-07-24 08:32:06 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Emit snap-ended on commit if needed
+ Commiting the timeline means that the current operations on the clips are over,
+ so we should concider snapping as done at that point
+ Fixes T7499
+
+2016-06-26 12:23:40 +0530 Mohan R <mohan43u@gmail.com>
+
+ * ges/Makefile.am:
+ fixed ges-version.h not found issue during out of tree build
+
+2016-07-22 07:32:51 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-image-source.c:
+ image-source: Do not concider inpoints
+ We have no restriction on inpoint for Images
+ Differential Revision: https://phabricator.freedesktop.org/D1202
+
+2016-07-15 08:57:28 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/validate/geslaunch.py:
+ tests:validate: Better choose tested rendering formats
+
+2016-07-11 21:16:11 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From f363b32 to f49c55e
+
+2016-07-06 13:51:18 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.9.1 ===
+
+2016-07-06 13:45:17 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.9.1
+
+2016-06-29 00:39:02 +0000 Justin Kim <justin.kim@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: unref GApplication properly
+ Differential Revision: https://phabricator.freedesktop.org/D380
+
+2016-06-29 00:38:36 +0000 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-project.c:
+ project_: improve get_uri doc
+ The return value of ges_project_get_uri should be freed
+ after usage.
+ Differential Revision: https://phabricator.freedesktop.org/D1142
+
+2016-06-21 11:49:14 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * common:
+ Automatic update of common submodule
+ From ac2f647 to f363b32
+
+2016-06-20 21:29:44 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/Makefile.am:
+ tests: Make sure to run python tests against the build in tree
+
+2016-06-18 16:16:00 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-timeline.c:
+ * ges/ges-uri-clip.c:
+ * tests/check/ges/basic.c:
+ * tests/check/python/test_clip.py:
+ ges: Don't remove track elements from clips when removing from layer
+ And reuse the same previously created element when adding the clip
+ back to a layer, avoiding losing all setting done on clip children
+ in that situation
+ This is a behaviour change but previous behaviour was actually totally
+ unexpected and people working around that weird behaviour will moste
+ probably not care about that change
+ Differential Revision: https://phabricator.freedesktop.org/D1094
+
+2016-06-20 14:00:07 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-clip.c:
+ * tests/check/python/test_clip.py:
+ title_: Do not forget to link up child_added/removed vmethod
+ Otherwise effect handling is broken
+ Differential Revision: https://phabricator.freedesktop.org/D1099
+
+2016-06-19 20:02:06 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-auto-transition.c:
+ * tests/check/python/test_group.py:
+ auto-transitions: Do not remove auto transitions when moving neighboor from the same group
+ Differential Revision: https://phabricator.freedesktop.org/D1097
+
+2016-05-14 19:33:05 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-container.c:
+ * ges/ges-timeline-element.c:
+ ges: fix various leaks with usage of ges_timeline_element_lookup_child
+ Some callers forgot to unref out child, pspec or both leading to leaks.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766449
+
+2016-05-14 19:02:57 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-video-track.c:
+ video-track: don't leak restriction caps in _sync_capsfilter_with_track()
+ https://bugzilla.gnome.org/show_bug.cgi?id=766450
+
+2016-06-06 17:44:15 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-uri-asset.c:
+ ges-uri-asset: GstDiscoverer can return a valid info but a non-OK result, consider this an error
+ The asynchronous case in ges_uri_clip_asset_request_async() already considered
+ it an error, do the same in ges_uri_clip_asset_request_sync().
+ https://bugzilla.gnome.org/show_bug.cgi?id=767293
+
+2016-05-31 12:09:44 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: Fix race condition in seek handling causing deadlocks
+ We might receive another seek from the application while the action task is
+ handling a previous seek (and thus setting seeking_itself to TRUE). To prevent
+ this seek to go through directly instead of being added as an action, also
+ check if the seek event was received from our action task thread or some other
+ thread.
+ https://bugzilla.gnome.org/show_bug.cgi?id=767053
+
+2016-05-31 09:29:44 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-meta-container.c:
+ ges: Emit GESMetontainer::notify-meta even if value is unset
+
+2016-05-30 10:51:51 -0400 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-meta-container.c:
+ ges: Allow passing `NULL` as a value to ges_meta_container_set_meta
+ Fixes T7430
+
+2016-05-25 10:32:46 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/Makefile.am:
+ g-i: pass compiler env to g-ir-scanner
+ It's what introspection.mak does as well. Should
+ fix spurious build failures on gnome-continuous
+ (caused by g-ir-scanner getting compiler details
+ via python which is broken in some environments
+ so passing the compiler details bypasses that).
+
+2016-05-16 12:06:37 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-clip.h:
+ uri-clip: make uri parameter of ges_uri_clip_new () const
+ To avoid compiler warning when using const string to create a new
+ GESUriClip as string is not modified and only passed to functions which
+ take a const string.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766523
+
+2016-05-16 12:53:32 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/gstframepositioner.c:
+ framepositionner: add a weak ref on track element to know when it is finalized
+ Otherwise if frame positionner is disposed after track element has been
+ finalized, it will raise a critical message because we will try to
+ disconnect a signal handler on a freed track element object.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766525
+
+2016-05-16 17:36:36 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-audio-source.c:
+ audio-source: unref private capsfilter reference on dispose
+ Otherwise a capsfilter reference will be leaked since it has been got
+ using gst_bin_get_by_name.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766524
+
+2016-05-16 17:35:29 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-audio-source.c:
+ audio-source: fix indentation
+ https://bugzilla.gnome.org/show_bug.cgi?id=766524
+
+2016-05-07 20:29:22 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: ensure elements pending to be added are not leaked
+ When nlecomposition is finalized with pending add action or io,
+ associated elements are not unreffed as they should since caller gives
+ us the reference when calling gst_bin_add causing them to be leaked.
+ So to make sure we don't leak a reference on element when adding one to
+ the bin, each stage (action and pending_io) hold a reference on element
+ and release it when stage is done.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766455
+
+2016-05-14 18:06:56 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: fix nle_composition_remove_object info message
+ We don't add internal bin, we remove it.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766455
+
+2016-05-15 01:04:17 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-asset.c:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-transition.c:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-clip.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-extractable.c:
+ * ges/ges-group.c:
+ * ges/ges-image-source.c:
+ * ges/ges-layer.c:
+ * ges/ges-meta-container.c:
+ * ges/ges-multi-file-source.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-project.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-utils.c:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-track.c:
+ * ges/ges-video-transition.c:
+ * ges/ges-video-uri-source.c:
+ ges: add some g-i annotations according to documentation
+ Mainly (transfer xxx) and (nullable). Also fix some typo.
+ https://bugzilla.gnome.org/show_bug.cgi?id=766459
+
+2016-05-15 01:03:49 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-asset.c:
+ asset: fix ges_asset_set_proxy() return value documentation
+ https://bugzilla.gnome.org/show_bug.cgi?id=766459
+
+2016-05-06 22:28:26 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ element: Also accept GParamSpec.owner_type name as a child property prefix
+ Makes it simpler for python users to be able to retrieve children
+ properties iterating over them.
+
+2016-05-06 18:21:17 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ track-element: gi: skip now deprecated children property getter/setter
+ Those are implemented with the exact same API at the GESTimelineElement
+ level now, and user of those APIs with high level languages will get the
+ exact same API.
+
+2016-05-06 15:44:28 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-xml-formatter.c:
+ formatter: Prefix all children properties in the XML formatter
+ Otherwise it will fail on properties that are mandatorily prefixed
+ like the newly added deinterlacing properties
+
+2016-05-06 15:18:50 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-group.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ ges: Remove timeline_emit_group_removed which slipped in the API by mistake
+ This is formally an API break but I am sure no one ever used that and
+ we should make sure the method is removed as soon as possible because
+ it has no reason to be exposed.
+
+2016-04-29 11:36:00 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-container.c:
+ container: Handle setting children properties that need prefixing
+
+2016-04-29 10:29:00 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-source.c:
+ video-source: Expose deinterlace-[fields, mode, tff] child properties
+ Letting some control over the deinterlacing to the users
+
+2016-04-30 18:38:33 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-effect.h:
+ * ges/ges-meta-container.c:
+ * ges/ges-timeline.c:
+ ges: fix misc g-i annotations
+
+2016-04-30 18:20:00 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/Makefile.am:
+ g-i: use only "ges/ges.h" as c-include for introspection
+ This is the only header which shall be included by user. Otherwise some
+ language using gir to generate binding, e.g Vala, will includes all
+ headers files in alphabetical order which causes compilation errors due
+ to incomplete type.
+ https://bugzilla.gnome.org/show_bug.cgi?id=765856
+
+2016-04-30 16:43:26 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * ges/ges-timeline.c:
+ timeline: rename "track-element" to "track_element" in select-tracks-for-object documentation
+ because "track-element" is not a valid identifier for a parameter and
+ will cause generated binding using GIR to be invalid. For instance in
+ Vala.
+ https://bugzilla.gnome.org/show_bug.cgi?id=765853
+
+2016-04-29 10:05:10 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-source.c:
+ video-source: Do not ever plugin avdeinterlace
+ It is not feature compatible with deinterlace and is not safe to use
+
+2016-04-28 13:39:41 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-types.h:
+ ges: #include glib.h for G_BEGIN_DECLS
+
+2016-04-28 13:39:27 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-multi-file-source.h:
+ ges-multi-file-source: Fix case of standard gobject macros
+
+2016-04-28 13:37:49 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * docs/libs/Makefile.am:
+ * docs/libs/ges-sections.txt:
+ ges: Add some more functions to the docs and don't scan internal headers
+ Someone still should look through the unused.txt for more things to add or
+ hide.
+
+2016-04-25 15:11:00 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-formatter.h:
+ * ges/ges-internal.h:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-structured-interface.h:
+ * ges/ges-types.h:
+ ges: Add G_BEGIN_DECLS around all relevant declarations in headers
+
+2016-04-22 16:06:50 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * tests/check/ges/titles.c:
+ title: Do not concider inpoints
+ It does not make sense for titles
+ Handle element with no inpoint handling in the timeline
+ Fixes https://phabricator.freedesktop.org/T7319
+
+2016-04-14 10:05:16 +0100 Julien Isorce <j.isorce@samsung.com>
+
+ * common:
+ Automatic update of common submodule
+ From 6f2d209 to ac2f647
+
+2016-04-13 12:32:53 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * tests/check/ges/timelineedition.c:
+ tests: Rename positionner to positioner in the tests too
+
+2016-04-13 12:31:05 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/Makefile.am:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-source.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-transition.c:
+ * ges/ges.c:
+ * ges/gstframepositioner.c:
+ * ges/gstframepositioner.h:
+ ges: Fix typo by renaming positionner to positioner
+ It's fortunately private API
+
+2016-04-13 12:26:13 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/gstframepositionner.c:
+ framepositionner: Initialize all fields of the meta during initialization
+ GstMetas are not allocated with all fields initialized to zeroes.
+
+2016-04-09 21:12:00 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ ges: Do not try to set read only properties
+ When copying and splitting clips
+ Fixes T7375
+
+2016-04-09 18:13:33 -0300 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-transition.c:
+ transition: Lower done some debug loggin level
+
+2016-03-27 23:40:16 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * plugins/nle/nlecomposition.c:
+ Minor fix
+
+2016-04-01 10:09:39 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-title-source.c:
+ titlesource: Add support for absolute positionning
+
+2016-04-04 10:53:13 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * tests/.gitignore:
+ * tests/check/ges/.gitignore:
+ tests: Add some things to .gitignore
+
+2016-04-03 18:06:00 +0200 Aurélien Zanelli <aurelien.zanelli@darkosphere.fr>
+
+ * examples/.gitignore:
+ examples/gitignore: ignore assets and play_timeline_with_one_clip binaries
+ https://bugzilla.gnome.org/show_bug.cgi?id=764550
+
+2016-03-30 09:26:18 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges.c:
+ GES: Properly split the GOptionGroup initialization
+ Debugging must be configuring first (before any parsing), and then
+ the types are initialized at the end.
+ Fixes issues with debugging categories not being available at the
+ start
+
+2016-03-07 08:49:14 +0900 Vineeth TM <vineeth.tm@samsung.com>
+
+ * ges/ges-smart-adder.c:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-track.c:
+ * ges/gstframepositionner.c:
+ * plugins/nle/nlecomposition.c:
+ * plugins/nle/nleoperation.c:
+ * plugins/nle/nlesource.c:
+ * plugins/nle/nleurisource.c:
+ editing-services: use new gst_element_class_add_static_pad_template()
+ https://bugzilla.gnome.org/show_bug.cgi?id=763195
+
+2016-03-24 13:33:52 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.8.0 ===
+
+2016-03-24 13:05:16 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.8.0
+
+=== release 1.7.91 ===
+
+2016-03-15 12:33:13 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.7.91
+
+2016-03-11 17:31:15 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * examples/c/overlays.c:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ Revert "titlesource: use x/yabsolute instead of x/ypos."
+ This reverts commit c4356db40c6e50f7314a75ea65d46f9f21ef0a5d.
+ This commit was not ready and was not support to be pushed
+
+2016-03-11 17:29:08 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-group.c:
+ ges: Don't emit timeline::group-removed when ungrouping outside a timeline
+
+2016-03-11 12:45:37 +0100 Lubosz Sarnecki <lubosz.sarnecki@collabora.co.uk>
+
+ * examples/c/overlays.c:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ titlesource: use x/yabsolute instead of x/ypos.
+
+2016-02-12 19:18:24 +0100 Justin Kim <justin.kim@collabora.com>
+
+ * tools/ges-launcher.c:
+ ges-launcher: don't leak sanitized_timeline string
+ Summary:
+ sanitized_timeline is created when parsing command line,
+ but it isn't free'd.
+ Reviewers: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D382
+
+=== release 1.7.90 ===
+
+2016-03-01 19:09:52 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.7.90
+
+2016-02-26 21:13:37 +0100 Sjors Gielen <sjors@sjorsgielen.nl>
+
+ * plugins/nle/nleoperation.c:
+ nle: Set the NleOperation flags to NLE_OBJECT_OPERATION
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D770
+
+2016-02-26 20:42:41 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-effect.c:
+ ges: Register scaletempo::rate as a rate changing property
+
+2015-12-20 14:03:57 +0100 Sjors Gielen <sjors@sjorsgielen.nl>
+
+ Handle changing playback rate
+ Before this patch, NLE and GES did not support NleOperations (respectively
+ GESEffects) that changed the speed/tempo/rate at which the source plays. For
+ example, the 'pitch' element can make audio play faster or slower. In GES 1.5.90
+ and before, an NleOperation containing the pitch element to change the rate (or
+ tempo) would cause a pipeline state change to PAUSED after that stack; that has
+ been fixed in 1.5.91 (see #755012 [0]). But even then, in 1.5.91 and later,
+ NleComposition would send segment events to its NleSources assuming that one
+ source second is equal to one pipeline second. The resulting early EOS event
+ (in the case of a source rate higher than 1.0) would cause it to switch stacks
+ too early, causing confusion in the timeline and spectacularly messed up
+ output.
+ This patch fixes that by searching for rate-changing elements in
+ GESTrackElements such as GESEffects. If such rate-changing elements are found,
+ their final effect on the playing rate is stored in the corresponding NleObject
+ as the 'media duration factor', named like this because the 'media duration',
+ or source duration, of an NleObject can be computed by multiplying the duration
+ with the media duration factor of that object and its parents (this is called
+ the 'recursive media duration factor'). For example, a 4-second NleSource with
+ an NleOperation with a media duration factor of 2.0 will have an 8-second media
+ duration, which means that for playing 4 seconds in the pipeline, the seek
+ event sent to it must span 8 seconds of media. (So, the 'duration' of an
+ NleObject or GES object always refers to its duration in the timeline, not the
+ media duration.)
+ To summarize:
+ * Rate-changing elements are registered in the GESEffectClass (pitch::tempo and
+ pitch::rate are registered by default);
+ * GESTimelineElement is responsible for detecting rate-changing elements and
+ computing the media_duration_factor;
+ * GESTrackElement is responsible for storing the media_duration_factor in
+ NleObject;
+ * NleComposition is responsible for the recursive_media_duration_factor;
+ * The latter property finally fixes media time computations in NleObject.
+ NLE and GES tests are included.
+ [0] https://bugzilla.gnome.org/show_bug.cgi?id=755012
+ Differential Revision: https://phabricator.freedesktop.org/D276
+
+2016-02-26 12:42:55 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From b64f03f to 6f2d209
+
+2016-02-16 12:49:57 +0000 Fabian Orccon <fabian.orccon@pucp.pe>
+
+ * ges/ges-group.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ group-added and group-removed signals added
+ Differential Revision: https://phabricator.freedesktop.org/D619
+
+2016-02-19 12:38:45 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.7.2 ===
+
+2016-02-19 12:26:27 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.7.2
+
+2016-02-18 15:26:11 +0000 Julien Isorce <j.isorce@samsung.com>
+
+ * pkgconfig/gst-editing-services-uninstalled.pc.in:
+ uninstalled.pc: add support for non libtool build systems
+ Currently the .la path is provided which requires to use libtool as
+ mentioned in the GStreamer manual section-helloworld-compilerun.html.
+ It is fine as long as the application is built using libtool.
+ So currently it is not possible to compile a GStreamer application
+ within gst-uninstalled with CMake or other build system different
+ than autotools.
+ This patch allows to do the following in gst-uninstalled env:
+ gcc test.c -o test $(pkg-config --cflags --libs gstreamer-1.0 \
+ gst-editing-services-1.0)
+ Previously it required to prepend libtool --mode=link
+ https://bugzilla.gnome.org/show_bug.cgi?id=720778
+
+2016-02-09 12:31:10 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * tests/check/ges/effects.c:
+ Fix and test priority of TrackElement after splitting
+ And make sure we properly handle transitions in that case
+
+2016-02-09 12:14:15 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ ges: Give better names to nleobjects
+
+2016-02-05 20:02:40 -0300 Thiago Santos <thiagoss@osg.samsung.com>
+
+ * tests/check/Makefile.am:
+ tests: extend the AM_TESTS_ENVIRONMENT from check.mak
+ To get the CK_DEFAULT_TIMEOUT defined for all tests
+ https://bugzilla.gnome.org/show_bug.cgi?id=761472
+
+2016-02-05 18:11:59 -0300 Thiago Santos <thiagoss@osg.samsung.com>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From 86e4663 to b64f03f
+
+2016-01-28 13:37:13 +0100 Lubosz Sarnecki <lubosz.sarnecki@collabora.co.uk>
+
+ * ges/ges-title-source.c:
+ titlesource: Add properties for text dimensions.
+
+2016-02-02 20:31:13 +0100 Lubosz Sarnecki <lubosz.sarnecki@collabora.co.uk>
+
+ * ges/ges-track-element.c:
+ trackelement: Make use of read-only children properties.
+ Read only properties will throw a GLib warning like this
+ when accessed with "set_child_property":
+ Warning: g_object_set_property: property 'text-x' of object class 'GstTextOverlay' is not writable
+
+2016-01-26 12:52:36 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * plugins/nle/nlecomposition.c:
+ nle: Turn composition structural issue into ERROR on the bus
+ Those error are really critical and we are then enable to keep
+ working. Just post an ERROR message on the bus and let the
+ application deal with it.
+ Reviewed-by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+ Differential Revision: https://phabricator.freedesktop.org/D740
+
+2016-01-25 16:11:14 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ track-element: Rely on nleobject to be created at construct time
+ Avoiding all the pending_xx dance and making the code simpler.
+ This is now possible thanks to the various recent refactoring.
+ Thanks to that the user is able to set_child_property on objects
+ that are not in GESTrack yet, as expected.
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D739
+
+2016-01-25 15:57:22 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-effect-asset.c:
+ * ges/ges-effect.c:
+ * ges/ges-internal.h:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/project.c:
+ effect: Determine the effect type as soon as possible
+ Making it possible to create the nleobject right at the creation
+ of the element.
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D738
+
+2016-01-25 15:51:26 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * tests/check/python/test_clip.py:
+ title-clip: Return default GESTitleSource value if no child set yet
+ In get_property we should return the default values if
+ we have not created any GESTitleSource yet
+ (instead of segfaulting).
+ And fix GESTitleSource default values!
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D737
+
+2016-01-25 11:56:57 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ * ges/gstframepositionner.c:
+ ges: track-element: Try to create NleObject as soon as possible
+ This way we have informations about the content of the
+ children as soon as possible.
+ Most code paths where already ready to handle that as we use it for
+ copying clips.
+ Fix framepositionner to properly handle that (it would have broke
+ with copied clips before).
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D736
+
+2016-01-19 11:22:57 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Avoid possible crash disposing the timeline
+
+2016-01-19 11:15:58 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/Makefile.am:
+ g-i: fix init section to avoid compiler warnings
+
+2016-01-06 17:20:20 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-container.c:
+ container: Update start if adding a child that as a start < current start
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D629
+
+2016-01-06 18:14:07 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Fix infinite loop on dispose
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D628
+
+2016-01-01 11:56:27 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-group.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-video-source.c:
+ * ges/ges-video-uri-source.c:
+ * ges/gstframepositionner.c:
+ * tests/check/python/test_clip.py:
+ * tests/check/python/test_group.py:
+ group: Make deep copying actually copy deep
+ Allowing pasting groups paste exactly what had been copied
+ And not the new version of the contained objects
+ This technically breaks the C API but this is a new API and I believe
+ and hope nobody is using it right now.
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D616
+
+2015-12-22 23:21:44 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ * tests/check/Makefile.am:
+ * tests/check/python/test_group.py:
+ tests_: Add a simple python copy/paste test for groups
+ Integrating python tests in the build system
+ And cleanup configure.ac
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D601
+
+2016-01-02 16:15:02 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/Makefile.am:
+ Do not install ges-smart-video-mixer.h
+ it should always have been private
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D617
+
+2016-01-06 09:50:39 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ Revert "timeline-element: Do not consider not serializable elements when getting top element"
+ This commit was causing issue where we were reporting the toplevel
+ element as an element but that element was actually in another
+ not serialized group. That is very tricky to handle for end users
+ as they are not guaranteed the toplevel clips were actually not
+ contained in another element.
+ This reverts commit ceb82ba3028332987d8d5251f98b4896120aa59b.
+ Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
+ Differential Revision: https://phabricator.freedesktop.org/D627
+
+2016-01-09 05:15:47 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: use correct type for flush_seqnum.
+
+2016-01-09 05:14:36 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * plugins/nle/nleghostpad.c:
+ nleghostpad: use GST_SEGMENT_FORMAT
+ This isn't 2005 anymore.
+
+2015-12-29 18:08:03 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-asset.c:
+ ges-asset: Don't dereference NULL proxy assets when resolving fails
+ CID 1346531
+
+2015-12-26 09:43:11 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-extractable.c:
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ ges: Fix various g-i warnings
+
+2015-12-26 09:43:19 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-track-element.c:
+ ges-track-element: Rename control-binding-reomved signal to control-binding-removed
+ Strictly speaking an API change but nobody on the Internet seemed to have used
+ the signal with the typo in the name.
+
+2015-12-24 15:30:23 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.7.1 ===
+
+2015-12-24 15:07:57 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.7.1
+
+2015-12-22 09:58:06 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-validate.c:
+ ges-validate: Fix compiler warning caused by usage of wrong enum type
+ ges-validate.c:237:22: error: implicit conversion from enumeration type
+ 'GESEdge' to different enumeration type 'GESEditMode'
+ [-Werror,-Wenum-conversion]
+ GESEditMode edge = GES_EDGE_NONE;
+ ~~~~ ^~~~~~~~~~~~~
+ ges-validate.c:277:41: error: implicit conversion from enumeration type
+ 'GESEditMode' to different enumeration type 'GESEdge'
+ [-Werror,-Wenum-conversion]
+ new_layer_priority, mode, edge, position))) {
+ ^~~~
+ https://bugzilla.gnome.org/show_bug.cgi?id=759758
+
+2015-12-18 13:32:22 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Do not consider not serializable elements when getting top element
+ Those are temporary elements that should not be considered when dealing
+ with the hierarchy of objects.
+ Fixes T3455
+
+2015-12-17 13:36:42 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-clip.c:
+ uri-clip: Copy sources child properties when resetting asset
+
+2015-03-12 13:57:28 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * Makefile.am:
+ * bindings/python/Makefile.am:
+ * bindings/python/examples/Makefile.am:
+ * configure.ac:
+ * examples/.gitignore:
+ * examples/Makefile.am:
+ * examples/c/Makefile.am:
+ * examples/c/assets.c:
+ * examples/c/concatenate.c:
+ * examples/c/ges-ui.c:
+ * examples/c/ges-ui.glade:
+ * examples/c/multifilesrc.c:
+ * examples/c/overlays.c:
+ * examples/c/play_timeline_with_one_clip.c:
+ * examples/c/simple1.c:
+ * examples/c/test1.c:
+ * examples/c/test2.c:
+ * examples/c/test3.c:
+ * examples/c/test4.c:
+ * examples/c/text_properties.c:
+ * examples/c/thumbnails.c:
+ * examples/c/transition.c:
+ * examples/python/simple.py:
+ * tests/Makefile.am:
+ examples: Move all examples to the root dir and create foldersdir per language
+ + Add some markdown files to link between languages
+ + Add a simple 'play timeline with one clip" example in C and python
+
+2015-12-21 12:34:56 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ configure: Use -Bsymbolic-functions if available
+ While this is more useful for libraries, some of our plugins with multiple
+ files and some internal API can also benefit from this.
+
+2015-12-11 15:20:53 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ Revert "project: Call asset_added in the first signal emition stage"
+ This reverts commit 08f927ca68f71530a32846b6da19eac9dc439a2c.
+ That commit was breaking the API and could break other people's code.
+
+2015-12-08 12:37:29 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-asset.h:
+ asset: Add a way to set asset as "needing reload"
+ Allowing application to force the asset system to recheck if an
+ asset has been "fixed" and can be used again
+ API:
+ + ges_asset_needs_reload
+ Differential Revision: https://phabricator.freedesktop.org/D584
+
+2015-12-02 11:04:10 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ project: Call asset_added in the first signal emition stage
+ Differential Revision: https://phabricator.freedesktop.org/D520
+
+2015-11-20 23:33:12 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-asset.h:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-xml-formatter.c:
+ * tests/check/Makefile.am:
+ * tests/check/ges/asset.c:
+ Implement asset proxying support
+ API:
+ ges_asset_set_proxy
+ ges_asset_get_proxy
+ ges_asset_list_proxies
+ ges_asset_get_proxy_target
+ Differential Revision: https://phabricator.freedesktop.org/D504
+
+2015-12-07 09:11:38 -0500 Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From b319909 to 86e4663
+
+2015-11-26 23:11:36 +0530 Sebastian Dröge <sebastian@centricular.com>
+
+ * Makefile.am:
+ bash-completion: Disable during "make distcheck" as this requires installing files outside the prefix
+ automake requires all files to be installed inside the prefix. bash-completion
+ requires the files to be in a specific directory given by a pkg-config file.
+ As such those two are having incompatible requirements and we just disable
+ bash-completion installation for the time being when running "make distcheck".
+ Nonetheless things like "make install" with e.g. a DESTDIR or a private
+ installation into a user's directory will fail as in both cases the
+ bash-completion data would be tried to be installed system-wide.
+
+2015-11-26 22:42:45 +0530 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Revert "build: fix make distcheck."
+ This reverts commit 462727d6d825b6e67119e6b8ea47d9e18cc22bdf.
+ This "fix" broke the build on Windows, where both prefix and datadir are
+ absolute paths and as such we would concatenate two absolute paths and fail.
+
+2015-11-21 00:23:02 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ * tests/check/Makefile.am:
+ tests: Properly setup GST_PLUGIN_PATH in test environement
+
+2015-11-15 00:31:21 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * Makefile.am:
+ Dist gst-editing-services.doap
+
+2015-11-08 22:49:43 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-asset.h:
+ asset: Add a method to retrieve the GError of an asset loaded with error
+ API:
+ ges_asset_get_error
+
+2015-11-07 18:21:53 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ project: Add a 'asset-loading' signal
+
+2015-11-05 11:16:31 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * .arcconfig:
+ * ges/ges-audio-source.c:
+ * ges/ges-smart-adder.c:
+ ges: Set restriction caps in the audio source caps filter
+ Otherwise we could have not negotiated errors in audiomixer when
+ the channel/channel-mask do not match
+ Differential Revision: https://phabricator.freedesktop.org/D493
+ Reviewed-by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2015-11-04 20:20:10 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-xml-formatter.c:
+ formatter: Do not serialize top effect priorities
+ We just need to make sure they are always serialized in the right
+ order (which is the case) and de serializing them will lead to the
+ right behaviour.
+ We should not serialize the priority as the priority of the source
+ itself depends on the action having been done on the parent clip,
+ and we do not serialize the source priorities (and should not, GES
+ should just do the right thing).
+ Differential Revision: https://phabricator.freedesktop.org/D491
+
+2015-11-04 18:37:34 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launcher.c:
+ launcher: Make sure to not activate validate twice when simply loading a scenario
+
+2015-10-30 10:52:12 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-clip.c:
+ uri-clip: Make sure to instantiate an asset to back GESUriClip-s
+
+2015-10-21 14:37:26 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From b99800a to b319909
+
+2015-10-20 17:29:42 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Use new GST_ENABLE_EXTRA_CHECKS #define
+ https://bugzilla.gnome.org/show_bug.cgi?id=756870
+
+2015-10-21 14:28:54 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 9aed1d7 to b99800a
+
+2015-10-02 22:27:37 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ Update GLib dependency to 2.40.0
+
+2015-10-02 16:51:56 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: free closure actions when disposing
+ Summary:
+ After invoking GClosure, the item of action list becomes
+ orphan so it lost a chance to be freed. In addition, even
+ when disposing, the list of actions has few items so we
+ have to check the list.
+ Reviewers: thiblahute
+ Projects: #gstreamer_editing_services
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D324
+
+2015-10-02 16:49:31 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: fix wrong argument order of GClosureNotify
+ Summary:
+ _free_action should follow GClosureNotify type.
+ ```
+ void
+ (*GClosureNotify) (gpointer data,
+ GClosure *closure);
+ ```
+ Reviewers: thiblahute
+ Projects: #gstreamer_editing_services
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D323
+
+2015-10-02 16:39:31 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-track.c:
+ track: mixing_operation is handled by its parent
+ Summary:
+ Normally, mixing_operation is created and added to nlecomposition
+ as a child element so it will be freed when nlecomposition is removed
+ from a track.
+ Reviewers: thiblahute
+ Projects: #gstreamer_editing_services
+ Differential Revision: https://phabricator.freedesktop.org/D319
+
+2015-10-02 16:11:33 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nleoperation.c:
+ nleoperation: don't leak iterator
+ Summary: Once an iterator is created, it should be freed after usage.
+ Reviewers: thiblahute
+ Projects: #gstreamer_editing_services
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D318
+
+2015-10-02 16:10:59 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-asset.c:
+ asset: simplify if-statement in cache_set_loaded
+ Summary:
+ Manual iteration can be replaced with foreach function.
+ In addition, this patch fixes mismatched GFunc type for
+ g_list_foreach and adds debug cateory for gst-asset for
+ convenient debugging.
+ Reviewers: thiblahute
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D312
+
+2015-10-02 16:08:03 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * .arcconfig:
+ * ges/ges-uri-asset.c:
+ uri-asset: do not reuse a passed GError pointer
+ Summary: A passed GError is re-allocated when discoverer has no information.
+ Reviewers: thiblahute
+ Projects: #gstreamer_editing_services
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D302
+
+2015-10-01 16:26:05 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: handle dispose properly
+ Summary:
+ To dispose properly, a child object should call same function
+ of parent class.
+ Reviewers: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D311
+
+2015-10-01 16:06:33 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ base-xml-formatter: properly handle GFile from wrong uri
+ Summary:
+ g_file_new_for_uri never fails so GFile always has valid pointer.
+ And fix a bug of double unref from D303.
+ Reviewers: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D310
+
+2015-10-01 11:28:38 +0200 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-audio-track.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-extractable.c:
+ * ges/ges-multi-file-source.c:
+ * ges/ges-video-track.c:
+ * ges/ges-xml-formatter.c:
+ don't leaks caps and converted strings
+ Summary:
+ Valgrind reports trivial leakages related to handling
+ objects and their converted strings.
+ Reviewers: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D303
+
+2015-09-30 14:50:46 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-track.c:
+ track: add gaps when going from READY to PAUSED.
+ Summary:
+ The backend commits itself automatically in these cases, so track
+ needs to do so too.
+ Reviewers: thiblahute
+ Reviewed By: thiblahute
+ Differential Revision: https://phabricator.freedesktop.org/D94
+
+2015-08-20 17:16:50 +0900 Vineeth TM <vineeth.tm@samsung.com>
+
+ * tests/examples/multifilesrc.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launcher.c:
+ editing-services: Fix memory leaks when context parse fails
+ When g_option_context_parse fails, context and error variables are not getting free'd
+ which results in memory leaks. Free'ing the same.
+ And replacing g_error_free with g_clear_error, which checks if the error being passed
+ is not NULL and sets the variable to NULL on free'ing.
+ https://bugzilla.gnome.org/show_bug.cgi?id=753864
+
+2015-09-30 17:11:20 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nleoperation.c:
+ nleoperation: don't leak srcpad
+ https://bugzilla.gnome.org/show_bug.cgi?id=755860
+
+2015-09-30 17:27:26 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-project.c:
+ project: fix a pointer for error message
+ https://bugzilla.gnome.org/show_bug.cgi?id=755862
+
+2015-09-30 17:26:31 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-project.c:
+ project: don't leak GFileInfo
+ https://bugzilla.gnome.org/show_bug.cgi?id=755862
+
+2015-09-22 01:06:00 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-track.c:
+ * ges/gstframepositionner.c:
+ timeline-element,track,framepositionner: don't leak internal object
+ https://bugzilla.gnome.org/show_bug.cgi?id=755247
+
+2015-09-24 01:30:09 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-structured-interface.c:
+ structured-interface: introduce TRY_GET_STRING
+ TRY_GET uses gst_structure_get. However, if boxed or
+ string pointer is retrieved by gst_structure_get,
+ it should be freed properly.
+ https://bugzilla.gnome.org/show_bug.cgi?id=755480
+
+2015-09-24 13:41:30 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: don't leak uri string
+ https://bugzilla.gnome.org/show_bug.cgi?id=755505
+
+2015-09-28 15:59:58 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ Revert "title-source: Force format with alpha channels out of videotestsrc"
+ This reverts commit 7d1e1010728a5348674bb9053de6b095cb824984.
+ This commit was never meant to be committed (at least *not* on master).
+
+2015-09-28 13:21:11 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-structure-parser.c:
+ * ges/ges-structure-parser.h:
+ structure-parser: define GES_STRUCTURE_PARSER macro
+ And fix trivial leakages of internal list structure.
+ https://bugzilla.gnome.org/show_bug.cgi?id=755716
+
+2015-09-27 15:15:10 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ title-source: Force format with alpha channels out of videotestsrc
+ Making sure the user can set a background of the title with an alpha
+ channel.
+ Working around https://bugzilla.gnome.org/show_bug.cgi?id=755482 for
+ the 1.6 branch.
+
+2015-09-25 12:30:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.6.0 ===
+
+2015-09-25 12:29:40 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.6.0
+
+2015-09-24 13:21:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-container.c:
+ * ges/ges-timeline.c:
+ ges: Avoid emitting 'child-added/removed' when signal emission stops addition
+ In the GESTimeline, TrackElement addition to a clip might get cancelled
+ (and thus the element gets removed), we need to make sure users do not
+ get wrong signals.
+ Also document the fact that user should connect to container::child-added
+ with g_signal_connect_after.
+
+2015-09-22 23:10:35 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nlecomposition.c:
+ * plugins/nle/nleobject.c:
+ nle{composition,object}: remove unused allocation & trivial leakages
+ nlecomposition allocates unused 'UpdateCompositionData' and it
+ causes leakages.
+ https://bugzilla.gnome.org/show_bug.cgi?id=755417
+
+2015-09-24 13:40:27 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: don't leak GstPad
+ https://bugzilla.gnome.org/show_bug.cgi?id=755505
+
+2015-09-24 13:42:16 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitivi-formatter: don't leak internal hash table
+ https://bugzilla.gnome.org/show_bug.cgi?id=755505
+
+2015-09-23 21:23:13 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-transition.c:
+ video-transition: Make compositor background transparent
+ Allowing further mixing downstream
+
+2015-09-23 21:12:33 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-transition.c:
+ video-transition: Add a framepositioner at the end of the transitio
+ So downstream compositor knows the zorder of the various streams
+
+=== release 1.5.91 ===
+
+2015-09-18 18:40:18 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.5.91
+
+2015-09-18 10:01:44 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-track.c:
+ * ges/ges-utils.c:
+ ges: Namespace NLE utils function into GES
+ Avoiding name clashes when built statically
+
+2015-09-15 12:17:19 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * plugins/nle/nlesource.c:
+ nle: Avoid unsetting srcpad target after the srcpad is already freed
+ That leaded to segfaults
+
+2015-09-15 11:08:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * plugins/nle/nleghostpad.c:
+ nle: Stop wrongly set operation segment base time
+ Inside the composition we actually do not need to have any notion
+ of what the timing outside the compositon as we already tweak the segment
+ base time outside the composition. This code was only there to work
+ around https://bugzilla.gnome.org/show_bug.cgi?id=753196
+ https://bugzilla.gnome.org/show_bug.cgi?id=754893
+
+2015-09-11 16:18:46 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nlecomposition.c:
+ nlecomposition: don't leak internal hashtable
+ https://bugzilla.gnome.org/show_bug.cgi?id=754867
+
+2015-09-11 16:13:19 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * plugins/nle/nleobject.c:
+ nleobject: don't leak srcpad when disposing
+ https://bugzilla.gnome.org/show_bug.cgi?id=754867
+
+2015-09-11 16:11:40 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: don't leak pad in private structure
+ https://bugzilla.gnome.org/show_bug.cgi?id=754867
+
+2015-09-11 09:58:56 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * tools/ges-launcher.c:
+ ges-launcher: don't leak GError
+ https://bugzilla.gnome.org/show_bug.cgi?id=754858
+
+2015-09-09 23:32:19 +0900 Justin Kim <justin.kim@collabora.com>
+
+ * tools/ges-launcher.c:
+ ges-launcher: fix double free when argument is invalid
+ https://bugzilla.gnome.org/show_bug.cgi?id=754783
+
+2015-09-04 12:01:16 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-source.c:
+ video-source: Use the priority being set to compute zorder
+
+2015-09-02 23:27:16 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-transition.c:
+ video:transition: Set mixer pad zorder
+
+2015-09-02 17:58:33 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-smart-video-mixer.h:
+ * ges/ges-video-source.c:
+ * ges/ges-video-transition.c:
+ video-source: Make sure to set framepositionner zorder when creating it
+ And fix a computation bug where we would be having mixing order
+ reversed between layers.
+ And make sure that the positionner does not mix up Transition handling
+ of the zorder
+
+2015-08-27 16:28:42 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-source.c:
+ * ges/gstframepositionner.c:
+ video-source: Simply set framepositionner->zorder = self->priority
+ Summary:
+ Making the code simpler and handling the transition case
+ where elements are in the same layer (which was failing
+ /setting same zorders until now).
+ Reviewers: Mathieu_Du
+ Differential Revision: https://phabricator.freedesktop.org/D237
+
+2015-08-23 01:35:18 +1000 Jan Schmidt <jan@centricular.com>
+
+ * configure.ac:
+ Use standard GST_PLUGIN_LDFLAGS for the nle plugin
+ Add the standard GST_PLUGIN_LDFLAGS to the configure.ac file.
+
+2015-08-21 21:25:27 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ Add support for static plugins builds
+
+=== release 1.5.90 ===
+
+2015-08-20 17:55:48 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.5.90
+
+2015-08-19 11:24:11 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * .arcconfig:
+ * Makefile.am:
+ * configure.ac:
+ * ges/Makefile.am:
+ * ges/ges-internal.h:
+ * ges/ges-track.c:
+ * ges/ges-utils.c:
+ * ges/ges.c:
+ * plugins/Makefile.am:
+ * plugins/nle/.gitignore:
+ * plugins/nle/Makefile.am:
+ * plugins/nle/gnlmarshal.list:
+ * plugins/nle/gstnle.c:
+ * plugins/nle/nle.h:
+ * plugins/nle/nlecomposition.c:
+ * plugins/nle/nlecomposition.h:
+ * plugins/nle/nleghostpad.c:
+ * plugins/nle/nleghostpad.h:
+ * plugins/nle/nleobject.c:
+ * plugins/nle/nleobject.h:
+ * plugins/nle/nleoperation.c:
+ * plugins/nle/nleoperation.h:
+ * plugins/nle/nlesource.c:
+ * plugins/nle/nlesource.h:
+ * plugins/nle/nletypes.h:
+ * plugins/nle/nleurisource.c:
+ * plugins/nle/nleurisource.h:
+ Move NLE to a dedicated GstPlugin
+ Summary: Allowing external user to directly use it
+ Reviewers: Mathieu_Du
+ Differential Revision: https://phabricator.freedesktop.org/D231
+
+2015-07-23 11:53:52 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nle: Enhance debug logging
+
+2015-07-23 11:42:48 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ * ges/ges-track.c:
+ ges: Do not leak and uselessly create errors
+ And avoid parenthesis in GstObject names
+
+2015-07-23 11:40:57 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges: Fix how we handle layer vs layer-priority in the structured interface
+
+2015-07-23 11:39:04 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/clip.c:
+ tests: Do not use gst-structured-interface in the tests
+ It breaks $ make distcheck
+
+2015-07-16 17:26:04 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * gst-editing-services.doap:
+ Update mailing list in doap file
+
+2015-07-16 10:54:54 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Remove transitions that can no fit into an auto transition
+ When activating auto transition mode
+
+2015-07-16 10:53:17 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ ges; Minor debug enhancement
+
+2015-07-13 13:48:40 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-asset.c:
+ assets: Avoid deadlock when done initialising asset
+ Avoid to hold the CACHE lock when setting the GTasks return values.
+ Fixes https://bugzilla.gnome.org/show_bug.cgi?id=752300
+
+2015-07-08 18:59:33 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ * ges/nle/nleobject.h:
+ nleobject: Concider objects as 'inactive' when they have a duration == 0
+
+2015-07-08 18:59:00 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ track-element: Handle the case where we have only one keyframe set when interpollating keyframes
+
+2015-07-06 10:24:33 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-asset.c:
+ * ges/ges-internal.h:
+ * tests/check/ges/uriclip.c:
+ asset: Port use of deprecated GSimpleAsyncResult to GTask
+
+2015-07-03 22:00:08 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From f74b2df to 9aed1d7
+
+2015-07-03 13:49:57 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ * tests/check/Makefile.am:
+ * tests/check/ges/clip.c:
+ track-element: Fix splitting bindings and add unit tests
+
+2015-07-01 18:33:39 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ structured-interface: Better handle CLOCK_TIME type from GstStructures
+
+2015-06-29 18:04:32 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-group.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-track-element.c:
+ element: Implement a paste method
+ Allowing user to copy paste clips very easily
+
+2015-06-23 16:11:26 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ * ges/gstframepositionner.c:
+ * tests/check/ges/layer.c:
+ timeline: Disable movement that lead to 2 transition at a position
+ We should never let 3 objects to overlap at a same position, for that
+ we introduce a "rollback" feature and whenever such an editing happens,
+ we rollback object position to whatever it was before the move.
+
+2015-06-23 19:19:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-smart-video-mixer.c:
+ smart-video-mixer: Always keep a ref on the mixer pad
+
+2015-06-23 13:27:00 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-container.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-track-element.c:
+ timeline-element: Add a method to get the TrackType it interacts with
+ API:
+ + ges_timeline_element_get_track_types
+
+2015-06-19 11:08:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-timeline.c:
+ timeline: Never create transitions between rippled objects
+ In case of groups, we can have track elements that do not belong
+ directly to the moved_trackelements but will be moved as others. Never
+ create transition to all object that have a start > moving group start.
+
+2015-06-16 17:07:40 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-smart-video-mixer.h:
+ * ges/ges-video-transition.c:
+ video-transition: Use a SmartMixer as mixer
+ So that the frame position metas are parsed and taken into account
+
+2015-06-16 15:02:18 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pipeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ track-element: Add method to remove control binding
+ API:
+ ges_track_element_remove_control_binding
+
+2015-06-16 13:25:32 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ * ges/ges-xml-formatter.c:
+ ges: Handle absolute GstDirectControlBindings
+
+2015-06-13 18:48:20 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-smart-video-mixer.c:
+ * ges/gstframepositionner.c:
+ * ges/gstframepositionner.h:
+ framepositionner: Make use of the new CompositorPad.width/height
+ So that the scaling is done in the compositor and this way we can cleanly interpolate its value
+
+2015-07-03 09:19:30 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/timelineedition.c:
+ timeline: Never change output media time when trimming start
+ + Fix testsuite
+ https://bugzilla.gnome.org/show_bug.cgi?id=638802
+
+2015-07-03 09:16:50 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ * tests/check/ges/timelineedition.c:
+ track-element: Return right value when editing
+ We used to always return TRUE which was wrong
+ + Fix testsuite and remove randomness from the tests
+
+2015-07-01 17:28:52 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-pipeline.c:
+ * ges/ges-track.c:
+ ges: Do not add a final gap at the end of track while rendering
+ It is not correct to force a black frame at the end of the rendered
+ video and it also leads to rendering issue with vpX encoders.
+ https://bugzilla.gnome.org/show_bug.cgi?id=751510
+
+2015-07-01 11:35:42 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * ges/nle/nlecomposition.c:
+ clip: Use container priority offset when setting children prios
+ Instead of trying to compute it ourself which might lead to wrong
+ behaviour when moving between layer.
+ + Make sure that when we reset clip children priority (to make space
+ for effects,) we update the container knowledge of priority offsets
+
+2015-06-30 23:13:28 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ clip: Fix track element priority computation
+ We were computing the priority offset taking the global MIN_NLE_PRIO
+ (which is a constant == 2 to make space for the mixing elements) instead
+ of the layer 'track element' relative priority, leading to very big
+ offsets on layer with a prio > 0. In the end it leaded to effects having
+ the same priority as the sources which leads to an undefined behaviour
+ in NLE.
+
+2015-06-24 09:06:30 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Bump the discoverer timeout to 1 minute
+ We should by default avoid false timeouts
+
+2015-06-22 01:52:39 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track.c:
+ track: Give usefull name to compositions
+
+2015-06-25 11:03:12 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ * ges/nle/nleobject.c:
+ * tests/check/nle/common.c:
+ nle: Port tests to the "commit" action signals
+ Now that nle_object_commit symbol is hidden, we can't use it
+ in the tests.
+
+2015-06-25 10:32:46 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * docs/random/lifecycle:
+ * docs/random/scenarios:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ ges: Unbreeak API after renaming of GNL to NLE
+
+2015-06-25 10:28:41 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-auto-transition.h:
+ * ges/ges-internal.h:
+ * ges/ges-structure-parser.h:
+ * ges/gstframepositionner.h:
+ ges: Hide more symbols of headers that are not installed
+
+2015-06-25 10:25:48 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/nle/nlecomposition.c:
+ nle: Remove unused function
+ nle/nlecomposition.c:2471:1: error: unused function '_parent_or_priority_changed' [-Werror,-Wunused-function]
+ _parent_or_priority_changed (NleObject * obj, GNode * oldnode,
+ ^
+
+2015-06-25 10:24:13 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * configure.ac:
+ * ges/nle/nlecomposition.h:
+ * ges/nle/nleghostpad.h:
+ * ges/nle/nleobject.h:
+ * ges/nle/nleoperation.h:
+ * ges/nle/nlesource.h:
+ * ges/nle/nleurisource.h:
+ nle: Hide away symbols, they're supposed to be internal
+
+2015-06-24 17:55:22 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.5.2 ===
+
+2015-06-24 17:44:04 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.5.2
+
+2015-06-23 09:41:01 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/nle/nlesource.c:
+ nlesource: remove outdated comment
+
+2015-06-16 17:50:38 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 6015d26 to f74b2df
+
+2015-06-10 17:54:20 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ pipeline: Handle rendering with disabled tracks
+ Summary:
+ The user might want to render only some media type of the timeline,
+ for example he wants to only render the audio part of the timeline.
+ It was failing as we were not connecting the track but were still trying
+ to 'render' it.
+ Depends on D153
+ Reviewers: Mathieu_Du
+ Reviewed By: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D154
+
+2015-06-09 21:00:44 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-asset.c:
+ * tools/ges-launcher.c:
+ ges: Raise an error when the discoverer returns != RESULT_OK
+ And do not try to run the pipeline when that happens
+
+2015-06-09 20:58:00 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Add a way to control discoverer timeout through envvar
+ Making it possible to run ges-launch test under valgrind for example
+
+2015-06-09 12:23:59 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * tools/ges-validate.c:
+ ges-launch: don't print random position/duration values at startup
+
+2015-06-09 11:30:59 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * common:
+ Automatic update of common submodule
+ From d9a3353 to 6015d26
+
+2015-06-08 23:08:40 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From d37af32 to d9a3353
+
+2015-06-07 23:07:40 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 21ba2e5 to d37af32
+
+2015-06-07 17:32:34 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From c408583 to 21ba2e5
+
+2015-06-07 17:16:53 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From d676993 to c408583
+
+2015-06-05 19:59:08 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Never snap end when rippling
+ http://phabricator.freedesktop.org/T74
+
+2015-06-05 19:58:16 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Never create transition between elements inside the moving context
+ http://phabricator.freedesktop.org/T74
+
+2015-06-05 18:49:51 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * ges/ges-group.c:
+ * ges/ges-group.h:
+ group: Disconnect from old layer notify::priority when a clip is moved to a NULL layer
+ This means we need to properly track the layer a clip was in. We now
+ keep track of the various signal IDs in a dedicated structure and
+ keep a ref on the layer an object is in.
+ http://phabricator.freedesktop.org/T88
+
+2015-06-03 14:56:11 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launcher.c:
+ * tools/ges-validate.c:
+ tools: Exit the app as it is a simgle instance app
+ And force exiting GstValidate when wanted
+
+2015-06-01 13:05:25 +0100 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * ges/ges-structured-interface.c:
+ ges: remove dead code
+ Summary:
+ No need to recheck if error exists since it has already been checked by the
+ conditional above.
+ Coverity CID #1302832
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D200
+
+2015-05-31 14:16:05 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-clip.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/layer.c:
+ ges: Handle trimming auto transitions
+ Meaning trimming neighbors.
+ + And add a test
+
+2015-05-29 15:15:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ timeline: Use a simple GList to track auto transitions
+
+2015-01-12 13:05:30 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Do not fail when removing/adding child without commiting
+ Summary:
+ We use to end up removing the nleobject when the following case happened:
+ * add an object
+ * remove that object
+ * re add the object
+ * commit the composition
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D193
+
+2015-05-19 18:18:30 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-layer.c:
+ timeline: Minor documentation addition
+
+2015-05-18 21:24:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-meta-container.h:
+ * ges/ges-xml-formatter.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-project.xges:
+ * tests/check/ges/test-utils.c:
+ ges: Enhance xges format versioning
+ Summary:
+ Handle the fact that some new features can be added and that means
+ generated files will not be fully understandable by older versions of
+ the formatter.
+ Make sure that we set the format version to 0.2 when we serialize the
+ GstEncodingProfile.enabled property.
+ Add some tests around that.
+ + Fix a minor bug in the test-utils
+ + Add a meta on the projects to tell in what format version a project
+ has been serialized/parsed back
+ API:
+ GES_META_FORMAT_VERSION
+ Depends on D178
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D184
+
+2015-05-14 11:12:20 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges: If last added clip is not in a layer, get the first layer
+ Summary:
+ In case we just removed it from its layer, make sure to
+ just use the first layer when none specified.
+ Depends on D177
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D178
+
+2015-05-14 11:11:44 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ * ges/ges-validate.c:
+ ges: Fix some error settings
+ Summary: Depends on D176
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D177
+
+2015-05-14 11:10:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges:structured-interface: Use GET_AND_CHECK in more places
+ Summary:
+ Giving more details about the issue to the user
+ Depends on D151
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D176
+
+2015-05-07 10:52:18 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ xml-formatter: De/serialize whether encoding profiles are enabled or not
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D151
+
+2015-04-26 18:22:40 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * Android.mk:
+ * ges/Makefile.am:
+ * tools/Makefile.am:
+ Remove obsolete Android build cruft
+ This is not needed any longer.
+
+2015-04-23 20:20:29 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * .gitignore:
+ Update .gitignore
+
+2015-04-22 15:07:58 +0200 Edward Hervey <edward@centricular.com>
+
+ * tools/utils.c:
+ tools: Fix string leak
+ Only allocate the return string when we know we are going to return
+ it.
+ Coverity CID #1292292
+
+2015-04-22 10:39:25 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * INSTALL:
+ Remove INSTALL file
+ autotools automatically generate this, and when using different versions
+ for autogen.sh there will always be changes to a file tracked by git.
+
+2015-04-21 11:24:38 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-base-xml-formatter.c:
+ ges:xml-formatter: Call g_markup_parse_context_end_parse
+ Summary:
+ Otherwise the parser context will never know that is all the XML it
+ will receive and fail out if the XML document is not valid (in that
+ case if it does not end)
+ https://bugzilla.gnome.org/show_bug.cgi?id=746354
+ Reviewers: Mathieu_Du
+ Reviewed By: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D38
+
+2015-04-20 17:42:44 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ ges: Add debug output when get_element returns NULL
+
+2015-04-15 12:18:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launcher.c:
+ tools:launch: Print out the timeline description as an INFO not an ERROR
+
+2015-04-15 12:18:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launcher.c:
+ tools:launch: clean user facing message on wrong timeline description
+ Summary:
+ Before:
+ $ ../gst-editing-services/tools/ges-launch-1.0 -p
+ 0:00:00.028629728 8155 0x17e1b60 ERROR default ges-launcher.c:214:_create_timeline: serialized timeline is -p
+ ** (lt-ges-launch-1.0:8155): ERROR **: Could not create timeline, error: Could not find a suitable formatter
+ [1] 8155 trace trap (core dumped) ../gst-editing-services/tools/ges-launch-1.0 -p
+ $
+ After:
+ $ GST_DEBUG=0 ges-launch-1.0 -p
+ ERROR: Could not create timeline, error: Could not find a suitable formatter
+ $
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D95
+
+2015-04-08 23:33:27 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/nle/nleobject.c:
+ * tests/check/nle/common.c:
+ nleobject: It is wrong to update object->stop in set_property.
+ Summary: It must only be done when the object is commited.
+ We can do that in constructed though, as the changes will
+ anyway be commited when the object is added to a composition.
+ Also update the tests, as we set properties spearately then
+ check the stop, we can commit the source at its creation without
+ removing meaning from the tests.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D84
+
+2015-04-08 21:38:48 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track.c:
+ timeline, track: Emit commited at the correct moment.
+ Summary:
+ + [API] GESTrack::commited signal.
+ + [API] ges_track_commit_sync
+ We were emitting commited when timeline_commit was called, which
+ wasn't very helpful. This commit makes it so we emit commited once
+ all the compositions have actually been commited.
+ We also add a synchronous commit method to spare the user
+ the need to connect to the signal and wait, and update the
+ documentation.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D83
+
+2015-04-07 22:48:27 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-layer.c:
+ layer: call timeline_element_set_timeline in layer_set_timeline.
+ Summary:
+ Otherwise if there was still a reference to the layer when it
+ is removed from the timeline, it fails when the last reference
+ is released, because timeline_element_set_timeline calls
+ timeline_remove_element, which tries to remove the element from
+ an already disposed hashtable.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D82
+
+2015-04-08 17:05:19 +0200 Edward Hervey <edward@centricular.com>
+
+ * common:
+ * tests/check/Makefile.am:
+ tests: Use AM_TESTS_ENVIRONMENT
+ Needed by the new automake test runner
+
+2015-04-03 17:38:53 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * data/completions/ges-launch-1.0:
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-structure-parser.c:
+ * ges/ges-structured-interface.c:
+ * ges/parse.l:
+ * tools/ges-launch.c:
+ ges-launch: Add support for +test-clip
+ Summary: With the pattern as a mandatory argument.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D68
+
+2015-04-03 16:48:03 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-project.c:
+ ges-project: Surface a meaningful error when no suitable formatter.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D67
+
+2015-04-03 15:35:54 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launcher.c:
+ ges-launch: Add a save-only option.
+ Summary: + Allows to serialize the timeline without playing it back.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D66
+
+2015-04-03 18:58:32 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From bc76a8b to c8fb372
+
+2015-03-31 14:26:19 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Disconnect child properties handlers.
+ Summary:
+ + And freeze notifies while doing so.
+ We had a race with GstController which isn't MT safe, we can
+ fix it by propertly disconnecting signals, and making sure
+ no notifies are emitted while doing so.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D64
+
+2015-03-30 18:41:11 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-track.c:
+ track: Set any caps features on tmpcaps.
+ Summary: Before checking if we have a specific constructor for a track type.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D63
+
+2015-03-31 15:29:49 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: no reason to disconnect a pad that is NULL anyway.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D62
+
+2015-03-25 15:43:16 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/validate/geslaunch.py:
+ * tools/ges-launcher.c:
+ ges-launch: Better document options.
+ + Sort them by topic
+ + remove --sample-paths and --sample-paths-recurse.
+ http://phabricator.freedesktop.org/D58
+
+2015-03-24 14:13:54 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/Makefile.am:
+ * tools/ges-launch.c:
+ * tools/ges-launcher.c:
+ * tools/ges-launcher.h:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ * tools/utils.c:
+ * tools/utils.h:
+ ges-launch: port to GApplication
+ Summary: + Extract some utility functions.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D55
+
+2015-03-25 12:25:54 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/Makefile.am:
+ build: no reason to introspect nodist sources.
+ Summary: g-ir-scanner was erroring like crazy on the generated sources.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D57
+
+2015-03-25 12:22:43 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-pitivi-formatter.c:
+ ges-pitivi-formatter: Don't flood stdout with alarming conclusions.
+ Reviewers: thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D56
+
+2015-03-23 12:27:56 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * data/completions/ges-launch-1.0:
+ completions: port to new base gst script
+
+2015-03-24 17:13:20 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * data/completions/ges-launch-1.0:
+ completions: Fix completions after the first command.
+
+2015-03-24 13:01:39 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launch.c:
+ Revert "ges-launch: no need for a tmp string pointer"
+ This reverts commit 44a0924c1f6b07f0c91ee8bd03d3ae5d97da92d5.
+ There indeed is a need for a tmp string pointer.
+
+2015-03-24 11:21:08 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * tools/ges-launch.c:
+ ges-launch: no need for a tmp string pointer
+
+2015-03-24 11:19:09 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * tools/ges-launch.c:
+ ges-launch: free string before going out of scope
+ CID #1291632
+
+2015-02-26 17:08:43 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/Makefile.am:
+ build: fix make distcheck.
+
+2015-03-17 18:25:02 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ * ges/ges-structured-interface.c:
+ ges: Do not clear potentially NULL errors
+ And avoid dereferencing NULL errors
+
+2015-03-13 12:02:30 +0000 Thibault Saunier <tsaunier@gnome.org>
+
+ * data/completions/ges-launch-1.0:
+ * ges/ges-structure-parser.c:
+ * ges/parse.l:
+ ges-launch: Prefix clip, transition and effect instruction with a +
+ Slightly changing the CLI so that we have indicators of the timeline
+ commands adding new objects.
+
+2015-03-01 13:10:55 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges: Accept path as URI in the create clip structured interface
+
+2015-02-26 13:49:23 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Remove useless options, rename some short options.
+
+2015-02-26 13:19:25 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * data/completions/ges-launch-1.0:
+ * tools/ges-launch.c:
+ bash-completion: Add support for new ges-launch commands.
+
+2015-02-25 18:01:38 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-structure-parser.c:
+ * ges/parse.l:
+ parse.l: Modify command arguments.
+ + --clip uri=file:// becomes clip file:// for example.
+
+2015-02-23 17:41:59 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-structure-parser.c:
+ * ges/ges-structure-parser.h:
+ * ges/ges-structured-interface.c:
+ * ges/ges-structured-interface.h:
+ * ges/ges-validate.c:
+ * tools/ges-launch.c:
+ ges: command-line-formatter: Properly error out on invalid arguments
+
+2015-02-23 14:48:18 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/Makefile.am:
+ * ges/ges-command-line-formatter.c:
+ * ges/ges-command-line-formatter.h:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ * ges/ges-structure-parser.c:
+ * ges/ges-structure-parser.h:
+ * ges/ges-structured-interface.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * ges/parse.l:
+ * tools/Makefile.am:
+ * tools/ges-launch.c:
+ ges: Factor out a GESCommandLineFormatter class
+ This formatter will allow any user to deserialize a timeline using
+ the new ges-launch command line interface
+
+2015-02-23 00:53:14 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-structured-interface.c:
+ * ges/ges-validate.c:
+ structured-interface: Be clever when no layer priority specified.
+ And add the new element to the same layer as the last clip that
+ was added, insted of adding to the last layer of the timeline
+ (and with the current code, actually adding a new layer each time)
+
+2015-02-21 15:30:57 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-structured-interface.c:
+ ges-structured-interface: fix build
+
+2015-02-20 12:26:54 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges: Automatically put clips at the end of layer if no start specified
+ In the 'structured' interface we should add it at the end of the layer
+ And make use of the new ges_timeline_get_layer API
+
+2015-02-19 19:29:36 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-structured-interface.c:
+ * tools/ges-launch.c:
+ structure-interface: rename layer-priority to layer.
+ + And add a short name for it in ges-launch.
+
+2015-02-20 12:12:52 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges: Add layer up to the wanted layer priority in the structure interface
+ Making the thing easier to use
+
+2015-02-19 19:16:44 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-structure-parser.c:
+ * tools/parse.l:
+ ges-launch: parse property names longer than 1 char.
+ + And finish the previous structure when encountering a setter.
+
+2015-02-19 13:15:25 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-structure-parser.c:
+ * tools/ges-structure-parser.h:
+ * tools/parse.l:
+ ges-launch: Update lexer / parser to handle set-*
+ + cleanup of the lexer
+
+2015-02-19 18:28:41 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-structure-parser.c:
+ tools: Implement a new CLI interface for the timeline creation
+
+2015-02-19 12:34:21 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-structured-interface.c:
+ ges: Handle setting child property on container directly in the structured based interface
+
+2015-02-19 11:28:48 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/parse.l:
+ launcher: Add support to --set-property in the parser
+
+2015-02-19 08:51:20 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * .gitignore:
+ * configure.ac:
+ * tools/Makefile.am:
+ * tools/ges-launch.c:
+ * tools/ges-structure-parser.c:
+ * tools/ges-structure-parser.h:
+ * tools/parse.l:
+ ges-launch: Implement a new parser for the commandline.
+ Summary: + flex-based lexing and manual simplistic parsing.
+ Test Plan: Use that stuff to make awesome things, see if it breaks.
+
+2015-02-17 23:48:12 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/Makefile.am:
+ * ges/ges-structured-interface.c:
+ * ges/ges-structured-interface.h:
+ * ges/ges-validate.c:
+ ges: Add an internal GstStructure based interface
+ To be use by GstValidate action and ges-launch
+ Reviewers: Mathieu_Du, thiblahute
+ Differential Revision: http://phabricator.freedesktop.org/D42
+
+2015-03-19 09:32:25 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ ges: Fix build for older GLib
+ The return type of g_hash_table_insert changed from void to boolean
+
+2015-02-19 18:19:44 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * .arcconfig:
+ * ges/ges-container.c:
+ container: implement children property handling
+
+2015-02-19 16:30:18 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/timelineedition.c:
+ ges: Move the notion of children properties to GESTimelineElement
+ Summary:
+ Deprecate the old GESTrackElement children property handling API.
+ New APIs:
+ * ges_timeline_element_list_children_properties
+ * ges_timeline_element_lookup_child
+ * ges_timeline_element_get_child_property_by_pspec
+ * ges_timeline_element_get_child_property_valist
+ * ges_timeline_element_get_child_properties
+ * ges_timeline_element_set_child_property_valist
+ * ges_timeline_element_set_child_property_by_pspec
+ * ges_timeline_element_set_child_properties
+ * ges_timeline_element_set_child_property
+ * ges_timeline_element_get_child_property
+ * ges_timeline_element_add_child_property
+ * ges_timeline_element_remove_child_property
+ Deprecated APIs:
+ * ges_track_element_list_children_properties
+ * ges_track_element_lookup_child
+ * ges_track_element_get_child_property_by_pspec
+ * ges_track_element_get_child_property_valist
+ * ges_track_element_get_child_properties
+ * ges_track_element_set_child_property_valist
+ * ges_track_element_set_child_property_by_pspec
+ * ges_track_element_set_child_properties
+ * ges_track_element_set_child_property
+ * ges_track_element_get_child_property
+ * ges_track_element_add_child_property
+ Reviewers: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D40
+
+2015-02-20 12:24:49 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Add API to find a layer with a specific priority in a timeline
+ Summary:
+ API:
+ * ges_timeline_get_layer
+ Test Plan: Nan
+ Reviewers: mathieu.duponchelle
+
+2015-02-20 12:24:49 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * .arcconfig:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-container.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/timelineedition.c:
+ Revert "ges: Move the notion of children properties to GESTimelineElement"
+ I got some trouble with
+ arc land
+ and I wanted to push the 3 commit coming after this revert as 3
+ different commits but they ended up being all squash into one single
+ commit, which is clearly not cool for later bisecting and blaming.
+ Reverting that commit and re pushing those 3 commits as they were
+ supposed to be.
+ This reverts commit 9fe15ef4354dc1d878dbdec80908ac8541bc6131.
+
+2015-03-18 20:23:55 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * .arcconfig:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-container.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/timelineedition.c:
+ ges: Move the notion of children properties to GESTimelineElement
+ Summary:
+ Deprecate the old GESTrackElement children property handling API.
+ New APIs:
+ * ges_timeline_element_list_children_properties
+ * ges_timeline_element_lookup_child
+ * ges_timeline_element_get_child_property_by_pspec
+ * ges_timeline_element_get_child_property_valist
+ * ges_timeline_element_get_child_properties
+ * ges_timeline_element_set_child_property_valist
+ * ges_timeline_element_set_child_property_by_pspec
+ * ges_timeline_element_set_child_properties
+ * ges_timeline_element_set_child_property
+ * ges_timeline_element_get_child_property
+ * ges_timeline_element_add_child_property
+ * ges_timeline_element_remove_child_property
+ Deprecated APIs:
+ * ges_track_element_list_children_properties
+ * ges_track_element_lookup_child
+ * ges_track_element_get_child_property_by_pspec
+ * ges_track_element_get_child_property_valist
+ * ges_track_element_get_child_properties
+ * ges_track_element_set_child_property_valist
+ * ges_track_element_set_child_property_by_pspec
+ * ges_track_element_set_child_properties
+ * ges_track_element_set_child_property
+ * ges_track_element_get_child_property
+ * ges_track_element_add_child_property
+ Reviewers: Mathieu_Du
+ Reviewed By: Mathieu_Du
+ Differential Revision: http://phabricator.freedesktop.org/D40
+
+2015-03-01 21:13:35 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-types.h:
+ ges: Remove all reference to already dead GESSimpleLayer
+
+2015-03-05 13:53:15 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * ges/ges-project.c:
+ project: remove unnecessary dereference
+ g_clear_error() already dereferences the error pointer, no need to manually
+ check and do it.
+ CID #1257630
+
+2015-03-03 14:26:40 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * tests/examples/test4.c:
+ examples: check argument is valid
+
+2015-02-27 01:26:24 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-base-xml-formatter.c:
+ ges-base-xml-formatter: fix setting of child properties
+ Make sure all child properties get set. GstStructureForeachFunc
+ takes a gboolean return value that decides whether to
+ continue or not.
+
+2015-02-27 01:22:39 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-meta-container.c:
+ ges-meta-container: fix ges_meta_container_foreach()
+ Really call function on all metadata inside the container
+ instead of stopping randomly. GstStructureForeachFunc
+ takes a gboolean return value.
+
+2015-02-26 20:14:31 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ ges-base-xml-formatter: fix property setting
+ GstStructureForeachFunc has a gboolean return value,
+ and the foreach function will stop unless we return
+ TRUE here. This meant it was potluck whether all
+ properties in the structure got set or not.
+ Fixes setting of text overlay clip text property
+ in particular.
+ https://bugzilla.gnome.org/show_bug.cgi?id=743874
+
+2015-02-24 18:00:34 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * configure.ac:
+ build: fix make distcheck.
+ And install bash-completions in the supplied prefix.
+
+2015-02-20 15:22:25 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * Makefile.am:
+ * configure.ac:
+ * data/completions/ges-launch-1.0:
+ ges-launch: enable auto-completion.
+ Summary: + And be a little smart about it.
+ Test Plan: New feature, working, not testing bash completion
+ Reviewers: tsaunier
+ Differential Revision: http://internal.opencreed.com:8888/D25
+
+2015-02-20 13:51:47 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track.c:
+ ges: Always set ANY capsfeatures on tracks caps property
+ Summary:
+ We should not restrict the CapsFeatures on the track caps.
+ If someone want to do such a restriction he should add it to
+ the restriction caps directly
+ Test Plan: Run testsuite
+ Reviewers: mathieu.duponchelle
+
+2015-02-19 17:33:12 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-project.c:
+ ges-project: no need to commit an empty timeline.
+ Summary: Can lead to deadlocks if the user commits at the same time.
+ Test Plan: Ran make check, it worked
+ Reviewers: tsaunier
+
+2015-02-10 10:29:39 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * ges/ges-track.c:
+ ges: initialize timeline_duration value
+ If priv->timeline is False the function does not set any value for
+ timeline_duration before using it in gap_new (). Initialize the value to aviod
+ unexpected behaviour.
+ CID #1268405
+
+2015-02-06 10:01:14 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ configure: Bump our Gst related dependencies to 1.5.0.1
+
+2015-02-04 15:21:55 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Properly protect the children task
+
+2015-02-03 12:02:42 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-track.c:
+ * ges/nle/nlecomposition.c:
+ * tests/check/nle/common.c:
+ * tests/check/nle/nlecomposition.c:
+ Cleanly handle removing the last object in a composition
+ The strategy here is to seek at the new end of the composition. And in
+ GES we always add a 1ns long gap at the end of the tracks so that all
+ track have the exact same duration, and we have black frames when the
+ timeline is empty
+
+2015-02-02 11:57:19 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-validate.c:
+ validate: Do not wrongly set clip duration for UriClips
+ That was making no sense at all....
+
+2015-01-12 13:04:16 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-validate.c:
+ validate: Properly expose the commit action as ASYNC
+
+2015-01-27 21:16:05 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-layer.c:
+ layer: Remove child from children list before emitting "child-removed"
+
+2015-01-26 18:25:02 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * ges/nle/nlecomposition.c:
+ ges: remove useless gpointer variable
+ gpointer useless is indeed useless since we can use GST_DEBUG_REGISTER_FUNCPTR
+ to avoid having to store the return of the GST_DEBUG_FUNCPTR registration.
+ CID #1265771
+
+2015-01-26 17:46:36 +0000 Luis de Bethencourt <luis.bg@samsung.com>
+
+ * ges/ges-timeline.c:
+ ges: merge MIN() and MAX() into CLAMP()
+ Merge the usage of MIN() and MAX() into one CLAMP() function.
+ CID #1265770
+
+2015-01-24 10:54:13 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges.c:
+ ges: Make sure the GESTextOverlayClip is register on init
+ So it can be used when de serializing projects containing it.
+ https://bugzilla.gnome.org/show_bug.cgi?id=743406
+
+2015-01-12 16:14:32 +0100 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From f2c6b95 to bc76a8b
+
+2014-12-18 10:56:54 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From ef1ffdc to f2c6b95
+
+2014-12-13 15:13:32 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Avoid trying to remove a signal handler that has already been removed
+
+2014-12-12 12:02:41 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Check that the newly computed URI exist
+ No the one we know failed!
+
+2014-12-10 10:21:16 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ * ges/ges-validate.c:
+ validate: Add an action type to load a project from its content
+
+2014-12-06 10:41:25 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-track.c:
+ Revert "track: [API]: ges_track_update_restriction_caps."
+ This reverts commit e9544ce1d67da6990f0a1cae75774063ec37be9d.
+ This commit should never have landed we decided we do not want to do
+ that.
+
+2014-12-01 00:38:07 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Add a method to easily check if a timeline is empty
+ API:
+ + ges_timeline_is_empty
+
+2014-12-01 00:34:38 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-group.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/group.c:
+ ges: Recompute Group priority when one of its clip.layer change priority
+ And add a unit test for that case where it was previously failing
+
+2014-11-29 01:12:43 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/validate/geslaunch.py:
+ validate: Start using the new testsuite based API from GstValidate
+
+2014-11-26 00:28:35 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-video-track.c:
+ track: [API]: ges_track_update_restriction_caps.
+ + And specify default restriction caps for audio and video tracks.
+ + Add ges_track_set_restriction_caps to the sections, it was missing.
+ https://bugzilla.gnome.org/show_bug.cgi?id=740726
+
+2014-11-25 23:35:55 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/integration.c:
+ tests: Remove integration tests, GstValidate is the way forward!
+
+2014-11-25 19:15:52 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/validate/geslaunch.py:
+ * tools/ges-launch.c:
+ validate: Handle long tests in the TestManager
+ + Minor bug fixes
+
+2014-11-25 19:14:59 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Minor debug enhancements
+
+2014-11-25 19:13:02 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ pipeline: Expose playsink::video-filter and playsink::audio-filter
+ That can be used to add filters at the very end of the pipeline,
+ and one can think of adding a watchdog element in there for
+ example.
+
+2014-11-25 18:46:03 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ pipeline: Dot not check for chain->tee twice
+
+2014-11-21 19:53:52 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-validate.c:
+ * tools/ges-validate.c:
+ validate: Avoid depending on not stable APIs
+ And cleanup includes
+
+2014-11-21 19:53:36 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-validate.c:
+ validate: Move to new action type registration API
+
+2014-11-16 20:07:24 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-validate.c:
+ validate: Add missing action execution printing
+
+2014-11-16 20:05:25 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Properly handle setting name to NULL
+
+2014-11-04 15:38:05 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-pipeline.c:
+ * ges/ges-track.c:
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Add a 'query-position' signal
+ In order to get the precise position of the pipeline, the only
+ way is to ask the 'application' to query the pipeline position and
+ use it.
+
+2014-11-03 12:18:35 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ * ges/ges-uri-asset.h:
+ * ges/ges.c:
+ * tools/ges-launch.c:
+ ges: Keep backward compatibility for relocated assets CLI
+ Meaning adding an API for user to add relacation URI paths
+ API:
+ ges_add_missing_uri_relocation_uri
+
+2014-11-03 12:17:42 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * .gitignore:
+ Add some ignore files to .gitignore
+
+2014-11-03 11:59:32 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges.c:
+ * ges/ges.h:
+ ges: Add a method to pass argc/argv to GES at init time
+ Allowing user to set configuration actions without using
+ the GES GOptionGroup
+ https://bugzilla.gnome.org/show_bug.cgi?id=740717
+
+2014-11-03 11:58:30 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/Makefile.am:
+ * ges/ges-validate.c:
+ * ges/ges.h:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ validate: Expose GES Validate action
+ So other can also make use of those action outside
+ ges-launch itself
+ https://bugzilla.gnome.org/show_bug.cgi?id=740718
+
+2014-11-03 11:55:29 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Ensure that UriAssets loaded with error are remembered
+
+2014-11-03 11:54:10 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ project: Add a method to create assets synchronously
+ This allows to create a add an asset to a project in a
+ synchronous way.
+ API:
+ ges_project_create_asset_sync
+ https://bugzilla.gnome.org/show_bug.cgi?id=740716
+
+2014-11-03 11:51:51 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ * ges/ges.c:
+ * tools/ges-launch.c:
+ ges: Add an init option to set media paths for moved assets
+ Allowing user to easily set a set of paths to look for moved
+ assets instead of needing the to re implement that logic
+ over and over.
+ https://bugzilla.gnome.org/show_bug.cgi?id=740714
+
+2014-11-03 11:14:45 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges.c:
+ * ges/ges.h:
+ ges: Add a method to get GES GOption group
+ This allow us to have global options to be
+ passed as arguments of the program to configure
+ GES behaviour
+ API:
+ ges_init_get_option_group
+ https://bugzilla.gnome.org/show_bug.cgi?id=740715
+
+2014-11-16 16:51:54 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ pipeline: Disable encoding format renegotiation when rendering
+
+2014-12-05 22:19:32 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/ges/track.c:
+ check/ges/track: add forgotten test file.
+
+2014-11-26 01:08:31 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-audio-source.c:
+ * ges/ges-title-source.c:
+ * ges/ges-video-source.c:
+ *source: new lines in xml break my parser.
+ + So I removed them cause I'm clever
+ https://bugzilla.gnome.org/show_bug.cgi?id=740727
+
+2014-11-26 20:34:24 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-audio-track.c:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/.gitignore:
+ track: [API]: ges_track_update_restriction_caps.
+ + And specify default restriction caps for audio tracks.
+ + Add ges_track_set_restriction_caps to the sections, it
+ was missing.
+ https://bugzilla.gnome.org/show_bug.cgi?id=740726
+
+2014-11-27 17:13:27 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * common:
+ Automatic update of common submodule
+ From 7bb2bce to ef1ffdc
+
+2014-11-10 17:24:11 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/Makefile.am:
+ tests: Fix make distcheck
+ Some xges project are not used anymore and some new appeared, clean
+ that up in the Makefile.am
+
+2014-11-10 16:20:29 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-xml-formatter.c:
+ element: Add a property allowing user to avoid serializing TimelineElements on demand
+
+2014-10-27 16:51:42 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-group.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline.c:
+ * ges/ges-xml-formatter.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ xml-formatter: Serialize groups
+ They were not serialized until now.
+ That implies several changes:
+ * Override GESTimelineElement [start, inpoint, duration] properties in
+ GESGroup to ensure that those properties are not serialized as they
+ should not be.
+ * Rename GESBaseXmlContainer->clips field to
+ GESBaseXmlContainer->containers as the hashtable now contains Groups
+ https://bugzilla.gnome.org/show_bug.cgi?id=709148
+
+2014-11-03 13:06:34 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ pipeline: Do not relink an already linked track
+
+2014-11-02 11:46:37 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/gstframepositionner.c:
+ * tests/check/ges/timelineedition.c:
+ frameposition: In case sources have the same size as track, follow track size
+ For example if the size has been serialized in a file, but the user has
+ not personalized the size, we want that whenever the restriction caps
+ change the size, the video should take the size of the track
+ restriction caps.
+ We know need to keep track of the current positionner.size even if
+ setting through caps size changes.
+ https://bugzilla.gnome.org/show_bug.cgi?id=739527
+
+2014-11-01 11:32:16 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-base-xml-formatter.c:
+ xml-formatter: Do not forget to set properties on the track elements
+ We were just setting children properties, even if the propertie to be
+ set on themselves where properly passed in
+ https://bugzilla.gnome.org/show_bug.cgi?id=729487
+
+2014-11-01 10:34:41 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ title-source: Expose the shaded-background property
+ Rework the way we override the background property to avoid trying to
+ lookup shaded-foreground-color!
+ https://bugzilla.gnome.org/show_bug.cgi?id=728635
+
+2014-11-01 09:47:39 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ titlesource: Expose the outline-color property
+ https://bugzilla.gnome.org/show_bug.cgi?id=728634
+
+2014-10-31 11:56:16 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: launch: Wait for the project to be loaded to activate gst-validate
+ Otherwize we could have a race where GstValidate actions are launched
+ even before the project has been loaded
+
+2014-10-31 11:32:37 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Never try to propose same URI when we know it is missing URI
+
+2014-10-28 18:36:55 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-pipeline.c:
+ * ges/ges-timeline.c:
+ pipeline: connect tracks when added, not only on state change.
+ + ghost track src pad before calling track added so that
+ pipeline has a pad to link.
+ + Remove silly comment.
+
+2014-10-30 12:36:57 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-audio-source.c:
+ * ges/ges-title-source.c:
+ * ges/ges-video-source.c:
+ Document known and usable child properties for GESTrackElements subclasses
+
+2014-10-30 12:38:16 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools:launch: Properly terminate when we get a SIGINT signal
+
+2014-10-29 13:40:55 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ trackelement: Add a lookup_child vmethod
+ This method can be used for subclass to override the default behaviour
+ for child lookup. This vmethod can be used for example in the case where
+ you want the name of a child property to be 'overridden'.
+ As an example in the GESTitleSource where we have a videotestsrc
+ which has a 'foreground-color' property that is used in the TitleSource
+ to set the background color of the title, this vmethod is now used to
+ tweak the name passed as parameter to rename "background" to
+ "foreground-backend" making our API understandable.
+ API:
+ GESTrackElement::lookup_child
+ https://bugzilla.gnome.org/show_bug.cgi?id=727880
+
+2014-10-29 12:44:17 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ title: Deprecate all method related to child properties
+ The standard way setting children properties is by using the
+ GESTrackElement::set_child_propery and friend methods
+ https://bugzilla.gnome.org/show_bug.cgi?id=727880
+
+2014-10-29 13:38:13 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ ges-validate: inform of clip removal.
+
+2014-10-29 13:25:06 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-timeline.c:
+ timeline: connect_after to layer.object_added.
+ We want the user provided signal handlers to be called before
+ we add track elements.
+
+2014-10-28 17:33:09 +0100 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/nle/nlecomposition.c:
+ * ges/nle/nleobject.c:
+ nle: Downgrade some INFO to DEBUG.
+ + makes it more pleasant to read logs in info.
+
+2014-10-22 13:49:27 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ * ges/nle/nleobject.c:
+ * ges/nle/nleobject.h:
+ * ges/nle/nleoperation.c:
+ * ges/nle/nlesource.c:
+ * tests/check/Makefile.am:
+ nle: Handle sending SEEK event recursively to our children
+ Instead of relying on it being implemented in core (as it is currently
+ not!)
+
+2014-10-06 12:30:17 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/nle/nlecomposition.c:
+ tests: Use audiomixer as an audio mixing element
+ Adder is the past!
+
+2014-10-01 10:04:53 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ validate: Implement validate Action type to handle KeyFrames
+ New action types:
+ * set-control-binding
+ * add-keyframe
+ * remove-keyframe
+
+2014-10-01 09:54:49 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ track-element: Add a signal about added control bindings
+ API:
+ GESTrackElement::control-binding-added
+
+2014-10-01 09:53:44 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-internal.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-xml-formatter.c:
+ track-element: Add an API to list all set ControlBinding
+ API:
+ ges_track_element_get_all_control_bindings
+
+2014-09-27 09:59:12 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-xml-formatter.c:
+ title-source: Properly expose children properties
+ + Make sure that the TitleClip properties are not serialized anymore as
+ they are serialized through children properties now.
+ + Enhance debugging for not serialized properties in GESXmlFormatter.
+
+2014-09-26 18:28:16 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: update base time before seeking current stack.
+ There could be a race where the new segments were pushed after
+ a seek on some / all pads before the operation had had its basetime
+ updated, and thus incoming segments were tweaked wrongly.
+ Reproducible with 3 clips composited and multiple seeks,
+ FIXME hard to validate.
+
+2013-09-14 01:35:55 +0200 Joris Valette <joris.valette@gmail.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ formatter: save and load source's children properties
+
+2013-09-23 18:40:34 +0200 Joris Valette <joris.valette@gmail.com>
+
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-properties.xges:
+ tests: project: Add children properties check
+ Rename test_project_add_keyframes into test_project_add_properties
+
+2014-09-26 18:39:19 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ project: Do not concider adding am already tracked asset as failure
+ It is not really a failure, just a special case.
+
+2014-09-26 17:51:14 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ ges-validate: Add actions to add/remove object from container
+ + Add an action to set an asset on a GESTimelineElement
+
+2014-09-26 17:50:03 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Fix edit_container return code
+ It used to always return FALSE.
+ + Fix minor leaks
+ + Do not seek ourself, it is users responsability to seek and
+ commit these days.
+
+2014-09-26 17:44:12 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-extractable.c:
+ * ges/ges-extractable.h:
+ * ges/ges-transition-clip.c:
+ extractable: Make extractable_set_asset return a boolean
+ WARNING: This is a minor API breakage, it should be harmless
+ and allows us to let users know whether changing setting the
+ asset worked or no.
+
+2014-09-25 17:31:49 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Add an action type to set restriction caps on track
+
+2014-09-25 17:31:05 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ tools: Handle asset relocation for assets from scenario
+ Allowing us to share scenario and media file!
+
+2014-09-25 15:30:55 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Add support for Layer.auto-transition
+ + Fix a bug where the mandatory field name for the name of the clip to
+ remove in remove-clip did not correspond to what we used in the action
+ (clip-name vs name).
+
+2014-09-25 14:59:40 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Add commit and split-clip action types
+ And stop commit at the end of other action types, this now
+ has to be done in the scenario itself.
+
+2014-09-25 14:57:35 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Add start/inpoint/duration params to the add-clip action
+
+2014-09-25 14:55:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools:ges-launch: Save the project at the end of execution
+ So that changes from scenarios are taken into account
+
+2014-09-25 14:53:36 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Fix the get_current_position method
+
+2014-09-23 15:01:56 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Wait for a buffer from the new segment to restart task
+ Avoiding races where we would launch a seek right after a FLUSH_STOP and
+ before we get a Buffer which would possibly lead to ERROR message when upstream
+ elements try to push a buffer and check_sticky fails because downstream
+ is flushing.
+
+2014-09-22 18:58:43 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Do useless thing so that the compiler doesn't warn us!
+ Otherwize we get a gcc warning about useless statements.
+
+2014-09-19 17:14:51 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools:ges-launch: Do not set pipeline state before the timeline is ready
+ When we are loading a project
+
+2014-09-19 17:13:52 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Update start stop and duration on initialization
+ So that the composition is usable right after the initialization
+
+2014-09-19 17:12:18 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ ges: Avoid to always commit when a project is loaded
+ In case we are not in a PLAYING state and the project is loaded, the
+ only thing that should be done is to fill the gaps and this way when the
+ composition get to PLAYING, their initialization will be enough to get
+ everything on track.
+
+2014-09-19 12:58:26 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/nle/nlecomposition.c:
+ * tests/check/nle/simple.c:
+ tests: Use the new gst_check_objects_destroyed_on_unref function
+
+2014-09-19 12:57:30 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ * ges/ges-track.c:
+ * tests/check/nle/common.c:
+ Start taking advantage of the fact that NLE is in the same three as GES!
+
+2014-09-19 12:55:51 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Fix several leaks
+ * In the action closure invokation we were alway leaking the composition.
+ * gst_bin_add will actually take an extra ref since we already gst_object_ref_sink so we
+ own the object, other call to that method will increase the refcount which means we do
+ not need to pass an extra ref to the bin.
+ * We want to ref_sink right when the object is added to the composition, making things
+ cleaner and simpler to follow in the tests.
+
+2014-09-19 12:52:45 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Activate the composition ghostpad to flush downstream
+ Since commit 060b16ac75ac227d4cfe1db89ccdc4f4b31545ff
+ "pad: don't accept flush-stop on inactive pads" in -core, the flush_stop event will not be
+ fowarded downstream in case the pad is not activated. In our case the element is in
+ READY state, so pads are deactivated. In that commit we simply make sure that the
+ event can be fowarded downstream
+
+2014-09-19 12:49:52 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nlecomposition: Restart the main task on FLUSH_STOP
+ It means stop using a dedicated probe to restart task so that the main probe does not
+ drop the FLUSH_STOP event before we have a chance to restart the task. (and this is
+ for sure cleaner/and simpler to read).
+
+2014-09-19 12:29:28 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlesource.c:
+ nle:source: Protect the probeid field with the GST_OBJECT_LOCK
+ Avoiding races where we try to remove a probe on an already destroyed pad.
+
+2014-09-19 12:28:05 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/gstframepositionner.c:
+ framepositionner: Add a weak pointer to the track_source
+ Avoiding assertions when the object is destroyed.
+
+2014-08-25 18:11:52 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/nle/nlesource.c:
+ nle: Seek gnlsource when prerolled only
+ Instead of implementing seek on ready all around GStreamer, just
+ seek in PAUSED, when the source gets 'prerolled'.
+
+2014-08-28 10:20:24 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track.c:
+ ges-track: Do not set removed object state
+ It is the composition to handle
+
+2014-08-20 13:15:30 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/nle/nlecomposition.c:
+ nle: Stop using a MainContext avoiding needing one iter per source dispach
+ Using GClosure to handle the source handling and handle our action
+ ordering ourselves
+ https://bugzilla.gnome.org/show_bug.cgi?id=733342
+
+2014-10-21 11:01:17 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-audio-transition.c:
+ * ges/ges-smart-adder.c:
+ ges: Use audiomixer instead of adder by default
+
+2014-10-21 10:59:43 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ Port to the new NLE API
+ Port the timeline, track and pipeline to the new NLE API where
+ all objects have static src pads.
+
+2014-08-15 15:48:14 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * docs/random/design:
+ * docs/random/lifecycle:
+ * docs/random/scenarios:
+ * ges/Makefile.am:
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-transition.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-internal.h:
+ * ges/ges-layer.c:
+ * ges/ges-layer.h:
+ * ges/ges-operation.c:
+ * ges/ges-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-track.c:
+ * ges/ges-utils.c:
+ * ges/ges-video-source.c:
+ * ges/ges.c:
+ * ges/nle/.gitignore:
+ * ges/nle/gnlmarshal.list:
+ * ges/nle/nle.h:
+ * ges/nle/nlecomposition.c:
+ * ges/nle/nlecomposition.h:
+ * ges/nle/nleghostpad.c:
+ * ges/nle/nleghostpad.h:
+ * ges/nle/nleobject.c:
+ * ges/nle/nleobject.h:
+ * ges/nle/nleoperation.c:
+ * ges/nle/nleoperation.h:
+ * ges/nle/nlesource.c:
+ * ges/nle/nlesource.h:
+ * ges/nle/nletypes.h:
+ * ges/nle/nleurisource.c:
+ * ges/nle/nleurisource.h:
+ * gnl/Makefile.am:
+ * gnl/gnl.c:
+ * gnl/gnlobject.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/check/nle/common.c:
+ * tests/check/nle/common.h:
+ * tests/check/nle/complex.c:
+ * tests/check/nle/nlecomposition.c:
+ * tests/check/nle/nleoperation.c:
+ * tests/check/nle/nlesource.c:
+ * tests/check/nle/seek.c:
+ * tests/check/nle/simple.c:
+ * tests/examples/transition.c:
+ Cleanup import of GNL and rename gnl to nle for Non Linear Engine
+ Conflicts:
+ ges/ges-track-element.c
+ gnl/Makefile.am
+ gnl/common
+ Conflicts:
+ ges/ges-internal.h
+ ges/ges-track.c
+ ges/ges-utils.c
+ ges/nle/.gitignore
+ ges/nle/gnlmarshal.list
+ ges/nle/nle.h
+ ges/nle/nlecomposition.c
+ ges/nle/nlecomposition.h
+ ges/nle/nleghostpad.c
+ ges/nle/nleghostpad.h
+ ges/nle/nleobject.c
+ ges/nle/nleoperation.c
+ ges/nle/nleoperation.h
+ ges/nle/nlesource.c
+ ges/nle/nlesource.h
+ ges/nle/nletypes.h
+ ges/nle/nleurisource.c
+ ges/nle/nleurisource.h
+ gnl/Makefile.am
+ gnl/gnl.c
+ gnl/gnl.h
+ gnl/gnl/gnl.h
+ gnl/gnl/gnlcomposition.c
+ gnl/gnl/gnlcomposition.h
+ gnl/gnl/gnlghostpad.c
+ gnl/gnl/gnlghostpad.h
+ gnl/gnl/gnlmarshal.list
+ gnl/gnl/gnlobject.c
+ gnl/gnl/gnloperation.c
+ gnl/gnl/gnloperation.h
+ gnl/gnl/gnlsource.c
+ gnl/gnl/gnlsource.h
+ gnl/gnl/gnltypes.h
+ gnl/gnl/gnlurisource.c
+ gnl/gnl/gnlurisource.h
+ gnl/gnlcomposition.c
+ gnl/gnlcomposition.h
+ gnl/gnlghostpad.c
+ gnl/gnlghostpad.h
+ gnl/gnlmarshal.list
+ gnl/gnlobject.c
+ gnl/gnlobject.h
+ gnl/gnloperation.c
+ gnl/gnloperation.h
+ gnl/gnlsource.c
+ gnl/gnlsource.h
+ gnl/gnltypes.h
+ gnl/gnlurisource.c
+ gnl/gnlurisource.h
+ gnl/tests/check/gnl/common.c
+ gnl/tests/check/gnl/common.h
+ gnl/tests/check/gnl/complex.c
+ gnl/tests/check/gnl/gnlcomposition.c
+ gnl/tests/check/gnl/gnloperation.c
+ gnl/tests/check/gnl/gnlsource.c
+ gnl/tests/check/gnl/seek.c
+ gnl/tests/check/gnl/simple.c
+ tests/check/gnl/common.c
+ tests/check/gnl/common.h
+ tests/check/gnl/complex.c
+ tests/check/gnl/gnlcomposition.c
+ tests/check/gnl/gnloperation.c
+ tests/check/gnl/gnlsource.c
+ tests/check/gnl/seek.c
+ tests/check/gnl/simple.c
+ tests/check/nle/common.c
+ tests/check/nle/common.h
+ tests/check/nle/complex.c
+ tests/check/nle/nlecomposition.c
+ tests/check/nle/nleoperation.c
+ tests/check/nle/nlesource.c
+ tests/check/nle/seek.c
+ tests/check/nle/simple.c
+
+2014-08-12 14:35:09 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Commit all values before initializing the pipeline
+
+2014-08-05 15:43:11 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Prevent update sources from being added after seek.
+
+2014-07-31 16:02:06 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: only flush stop after seek was set to READY.
+
+2014-07-29 23:41:45 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Also ignore all messages from children tearing them to READY
+ At that stage elements should not be taken into account anymore. In some
+ spacial cases they can post ERROR messages (when trying to push sticky
+ events on flushing pads) on the bus. We actually do not care about those
+ issues at that exact point.
+
+2014-07-28 20:24:50 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Do not try to paused the task that could have been stopped
+ There was a race where we ended up trying to update the pipeline and
+ stop our children task at the exact moment where we were actually
+ setting its state to PAUSED. Take the composition lock and make sure
+ that can't happen
+
+2014-07-25 10:55:52 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/gnlcomposition.c:
+ composition: Post messages on the bus when it updates itself
+ And properly set the seqnums of those messages so that the application,
+ parents have the exact information about what is going on and why.
+
+2014-07-22 18:22:09 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: push flush events on the target.
+
+2014-07-19 11:41:56 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlcomposition.h:
+ * tests/check/gnl/common.c:
+ composition: No need for action signal to add and remove objects!
+
+2014-07-21 16:59:24 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: we're initialized even if update_pipeline returned FALSE
+
+2014-07-21 16:57:14 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: reset base time to 0 when needed.
+
+2014-07-21 16:54:46 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Forward flushes on initialization
+
+2014-07-18 04:04:16 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlobject.c:
+ object: make the check for self commit work in a crappy way.
+
+2014-07-18 04:01:25 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: use g_main_context_set_dispatches_per_iteration ()
+ Let's hope this gets merged ...
+
+2014-07-15 15:17:43 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: set next_eos_seqnum when we get seeked too.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-15 15:47:59 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ * gnl/gnlobject.c:
+ * gnl/gnlobject.h:
+ gnl: Rely on the GstElement to properly handle their seqnums
+ Actually it is not exactly thread safe to tweak them ourself at the GNL
+ level.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-15 15:16:23 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Always return a value in GSourceFuncs
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-15 15:01:59 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Rename _flush_downstream to _have_to_seek_downstrean
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-15 14:59:54 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlobject.h:
+ * gnl/gnlsource.c:
+ * tests/check/gnl/common.c:
+ source: Remove cruft code to seek sources
+ We now seek on ready and thus do not need to do magic trying to seek
+ the source as soon as possible as we now do it even sooner than soon.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-15 09:46:03 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Do not remove all sources when stopping task
+ We only want to remove updates and seek, commits should be kept
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-15 02:37:25 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Don't send flushes downstream on pipeline update.
+ The code is still a bit redundant in set_current_bin_to_ready, need
+ to discuss.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 17:52:36 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove locking making sure that we manipulate children in right places
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 17:47:07 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Avoid a race in PAUSED_TO_READY
+ as we were using our children list in there without locking them.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 17:18:23 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: No need to reset the composition when going to PAUSED or NULL
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 17:10:35 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Make sure we have a peer when we send flushes downstream
+ And avoid leaks
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 17:06:05 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Minor cleanups
+
+2014-07-14 16:51:56 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Take the objects lock when reseting the composition
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 16:47:45 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove the reset children method
+ as it is all already done in _empty_bin ()
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 16:44:43 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Set children caps only when they are added to the composition
+ No need to do it again on READY_TO_PAUSED
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 16:41:25 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: No need to children state locked anymore
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 16:30:35 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: cleanup dispose / finalize
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 16:24:46 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: remove children warning drop HACK.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 16:12:00 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: remove now useless notion of GnlCompositionEntry.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 15:50:58 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: cleanup GnlCompositionEntry before its actual removal.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 15:43:04 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: remove now useless prop "deactivated_elements_state".
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 15:40:28 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove now useless flag "reset_time".
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 15:37:51 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove now useless flag "stackvalid".
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 15:35:47 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: remove now useless "flushing" flag.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 13:36:31 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Do not use 'update' seek for now
+ This is not working in our new context and the seek do not work at
+ all when we set seek start to CLOCK_TIME_NONE and type to TYPE_NONE.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-14 13:35:24 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Add the stack start/stop that has been set in dotfile name
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-14 13:34:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Push flush events *downstream* not 'somewhere' :)
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-13 16:59:15 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlsource.c:
+ source: Atomically change the probe ID
+ Avoiding races where the probe would be removed 2 times
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-13 11:51:51 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Avoid deadlock when setting current bin to ready (on commit or seek)
+ We need to make sure that between the time we send flush_start/stop and
+ the time we actually set the bin to READY, no buffer got prerolled again
+ as it would lead to a deadlock trying to set the bin to READY (while
+ deactivating the pads, it needs the streaming lock, which would be
+ taken in that case)
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-12 20:54:55 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Only sync state of current bin when activating new stack.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 21:59:43 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/complex.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/gnloperation.c:
+ * tests/check/gnl/simple.c:
+ composition: Start kindergarten task when going to READY.
+ And stop it when going back to NULL.
+ Update tests.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 21:58:41 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/gnlcomposition.c:
+ composition: add a new failing test for finalize on commit.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 19:13:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Make sure to remove all updates when updating the stack
+ The EOS we received before that became meaningless and thus the
+ associated GSources should no be triggered
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 18:29:17 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Avoid emitting COMMITED like crazy
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 18:27:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Add an enum to define why we update the stack
+ Making the code simpler to follow
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 17:48:05 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: We are now waiting for caps to restart our task
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 17:31:34 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove useless functions to add GSources and add debugging
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 17:25:44 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove DONE fixme
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 17:23:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlghostpad.c:
+ ghostpad: Do not try to be smarter than possible with seqnum
+ We can have several CAPS event comming at any time and thuse we will
+ need to rely on elements to handle their seqnum properly as we can not
+ do a safe guard at our level
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 17:22:24 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Keep our GSources in a list making their thread safe
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 16:08:20 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ composition: Concider the last action as DONE when we get a CAPS or SEGMENT
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 15:41:50 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: only forward our flush start / stops
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 14:18:58 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlghostpad.c:
+ ghostpad: remove useless debug
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 14:17:36 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: cleanup and enhance debug
+ Bye Bye STRAM START you were a brave little debug we will miss you.
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-11 14:11:21 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: do not flush downstream when updating pipeline ourselves.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-11 12:20:53 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Set update to TRUE when updating the stack because of EOS
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-10 18:01:32 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Handle seeking current stack while PAUSED
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-10 16:26:48 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Add and enhance some debug
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-10 16:21:31 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Dot the newly created stacks
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-10 16:17:20 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlghostpad.c:
+ gnlghostpad: Add some more debugging and fix mistakes in seqnum handling
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-10 15:48:50 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Factor out a function to seek the current stack
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-10 15:46:19 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Set the seqnum only when receiving the actual seek event
+ Setting it before calling seek_handling is racy!
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-10 15:45:19 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Set the real_eos_seqnum in a dedicated method
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-10 15:43:26 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Reset the real_eos_seqnum when reseting the composition
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-10 15:42:48 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Release OBJECTS_LOCK when emiting the "commited"
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-10 15:33:23 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Use the private struct directly
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-09 12:51:36 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlobject.c:
+ * gnl/gnlobject.h:
+ * gnl/gnloperation.c:
+ * gnl/gnloperation.h:
+ gnloperation: Totally clear operations when removing emptying current_bin
+ Removing linked childre leads to weird behaviour, we want to make sure
+ that all elements are totally clean when they are out the current bin.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-08 23:25:09 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlghostpad.c:
+ gnlghostpad: Do not forget to set output segment seqnum
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-08 23:00:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Fix the build
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-08 22:59:11 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/seek.c:
+ tests:composition: Fix some refcounts
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-08 22:58:02 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Relink nodes *after* setting the seqnum
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-08 22:57:22 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlghostpad.c:
+ gnlghostpad: Do not tolerate getting seeked when no target is set
+ This should not happen anymore
+ + Do not set twice events seqnums
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-08 22:55:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlghostpad.c:
+ gnlghostpad: Add missing seqnum tweaking
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-08 22:54:31 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/gnloperation.c:
+ tests: Use compositor instead of videomixer
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-08 22:53:57 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Handle the case were we get an EOS right after a segment
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-08 13:28:57 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Make basic seeking working
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 23:28:43 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/gnl/gnloperation.c:
+ tests: Start fixing operation tests
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-07 23:24:46 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/simple.c:
+ tests: Fix composition tests
+ We can not expect a seek event anymore as we are seeking in READY the elements
+ themselves
+ +remove actual sinks
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 23:07:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Implement the logic to PAUSE the task while executing actions
+ We need to wait for the pipeline update to be actually finished before we can start another
+ action. That means that we pause the task until one buffer from the new stack is
+ outputed.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-07 23:08:56 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Do not forget to set expandables state to NULL when disposing
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 23:01:24 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Factor out a function to commit only the values
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-07 22:58:27 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Get the current position before actually commiting values on commit
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 22:54:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Add a method that set the current bin to READY
+ We need to get the stream lock in some conditions, and thuse send
+ flush event in those cases.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-07 22:50:34 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove useless code
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 22:33:09 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Clear the old stack when removing children that where used
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-07 22:27:21 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove useless toplevel_seek argiment from activate_new_stack
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 22:25:51 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ * gnl/gnlobject.c:
+ * gnl/gnlobject.h:
+ composition: Rework the seqnum logic to avoid races when setting the new stack seqnum
+ When we were seeking the same stack without a logic that gurantees that we actually
+ saw the seek with the new seqnum set, we could have ended up with an EOS set with
+ the right seqnum even if it was actually not the case.
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-07 21:31:01 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Add/Remove children in the same order as they were called
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-07 21:28:28 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove useless flush_start argument from the stop_task method
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-06 15:46:22 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Force setting children state to NULL rebfore unrefing them
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-04 11:05:41 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ * gnl/gnlobject.c:
+ * gnl/gnlobject.h:
+ compositition: Check last stack in the children thread
+ Avoiding to take the OBJECT_LOCK when recieving EOS. The computation is
+ based on the GstEvent.seqnum to make sure that the EOS we receive
+ corresponds to the right sequence.
+ In that patch we tweak seqnums so that they are correctly computed
+ avoiding to depend on all elements to do it properly as it might pretty
+ much not be the case!
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-04 11:11:53 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Actiually set current_bin state even when not debugging
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-03 17:42:06 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Set *all* children state when going back to READY and then NULL
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-03 17:36:01 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ * tests/check/gnl/gnlcomposition.c:
+ composition: Fix toplevel seek event refcounting
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-03 16:46:21 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/complex.c:
+ composition: Teach the composition to seek same stack
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-03 16:44:05 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/simple.c:
+ composition: The ref the user gave us is our, and we give another to the bin when needed
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-03 16:41:42 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Rename commit_pipeline_func to commit_func
+ We do not commit any pipeline, we commit the new state of the composition internals
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-03 14:48:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Set the state of the internal bin before removing its children
+ Otherwize when we set the composition state to READY or NULL we can end
+ up with children in PAUSED state outside of everything
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-03 14:34:11 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/common.c:
+ * tests/check/gnl/simple.c:
+ tests: Minor cleanup
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-03 14:32:44 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlobject.c:
+ gnlobject: Allow commiting of object that are not inside a composition
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-03 14:32:18 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: Empty current_bin on gnl_composition_reset
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-03 14:31:35 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Remove now useless external_gst_bin_add_remove field
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-03 11:36:20 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlsource.c:
+ composition: Remove now useless pad probes
+ Co-Authored by: Thibault Saunier <tsaunier@gnome.org>
+
+2014-07-02 21:01:31 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ * gnl/gnlghostpad.h:
+ * gnl/gnlobject.c:
+ * gnl/gnlsource.c:
+ * tests/check/gnl/complex.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/gnloperation.c:
+ * tests/check/gnl/seek.c:
+ * tests/check/gnl/simple.c:
+ composition: Add an internal bin where that contain used children
+ Co-Authored by: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+2014-07-02 17:33:35 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/gnlcomposition.c:
+ composition: Add objects to the pending IO list in a GSource
+ This way we make sure we do not manipulate our children from another
+ thread than the dedicated one.
+
+2014-07-01 18:08:32 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Start implementing seeking in a GSource
+
+2014-06-30 16:21:30 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlcomposition.h:
+ * tests/check/gnl/common.c:
+ * tests/check/gnl/common.h:
+ * tests/check/gnl/complex.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/gnloperation.c:
+ * tests/check/gnl/seek.c:
+ * tests/check/gnl/simple.c:
+ composition: switch to using an action signal to add and remove objects.
+
+2014-06-30 16:29:50 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: chain up finalize before clering mcontext_lock.
+
+2014-06-30 15:12:38 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Initialize the first stack async
+
+2014-06-29 22:35:34 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/gnloperation.c:
+ Finish fixing tests
+
+2014-06-28 14:44:24 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/common.c:
+ tests/common: disconnect commited handler
+
+2014-06-27 16:12:12 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/common.c:
+ * tests/check/gnl/common.h:
+ * tests/check/gnl/complex.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/seek.c:
+ * tests/check/gnl/simple.c:
+ composition: Actually commit in on our own thread
+ Avoiding races
+
+2014-06-27 17:03:44 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: start and stop task in init and finalize
+
+2014-06-27 17:01:34 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/gnlcomposition.c:
+ tests: unref message correctly
+
+2014-06-27 16:40:19 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlcomposition.h:
+ composition: home grown task
+
+2014-06-27 16:12:28 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tests/check/gnl/gnlcomposition.c:
+ Don't be sync silly test
+
+2014-06-27 15:00:48 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ composition: simplify update_pipeline_func
+
+2014-06-27 16:26:09 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Reset the srcpad target when removing the toplevelentry
+
+2014-06-27 12:15:10 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ * tests/check/gnl/simple.c:
+ composition: Use a GstPad task to run the update pipeline thread
+
+2014-06-26 23:48:09 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ gnlcomposition: Factor out code to deactivate old stack and activate new one
+
+2014-06-26 19:00:03 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ composition: Avoid looping using gotos
+
+2014-06-26 18:41:48 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ gnlcomposition: Use the new _object_block_and_drop_data where appropriate
+ Renaming block_object_src_pad to _object_block_and_drop_data
+
+2014-06-25 19:39:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ gnlcomposition: factor out some functions
+
+2014-06-25 19:18:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ gnlcomposition: Factor out the condition of _parent_or_priority_changed
+
+2014-06-25 19:17:55 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlcomposition.c:
+ gnlcomposition: Add a function to block object source pad
+
+2014-06-24 13:44:13 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlcomposition.c:
+ * gnl/gnlghostpad.c:
+ * gnl/gnlghostpad.h:
+ * gnl/gnlobject.c:
+ * gnl/gnlobject.h:
+ * gnl/gnloperation.c:
+ * gnl/gnloperation.h:
+ * gnl/gnlsource.c:
+ * tests/check/gnl/complex.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/gnloperation.c:
+ * tests/check/gnl/gnlsource.c:
+ * tests/check/gnl/seek.c:
+ * tests/check/gnl/simple.c:
+ gnl: Add the srcpad directly to GnlObject
+ Starting from now we will not claim that we support GnlObject that have
+ several source pads as this is
+ 1- Not true at all;
+ 2- the design of priorities in the GnlComposition tree does not allow that;
+ 3- Not very useful in most of the cases and it complexifies quite a lot the code
+ in the composition.
+ Conflicts:
+ configure.ac
+ tests/check/Makefile.am
+
+2014-06-25 15:35:08 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/gnlobject.c:
+ Revert "gnlobject: Commit object in READY_TO_PAUSED"
+ This causes races when seeking, reverting for now even if we will
+ probably want to bring something like that back.
+ This reverts commit 3549e745a8f0de3977b83c60e9b447afaf55d8a0.
+
+2014-06-24 12:52:24 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * gnl/gnlsource.c:
+ * gnl/gnlsource.h:
+ gnlsource: remove useless "controls_one" field.
+
+2014-10-21 10:35:48 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * gnl/Makefile.am:
+ * gnl/gnl.c:
+ * gnl/gnl.h:
+ * gnl/gnlcomposition.c:
+ * gnl/gnlcomposition.h:
+ * gnl/gnlghostpad.c:
+ * gnl/gnlghostpad.h:
+ * gnl/gnlmarshal.list:
+ * gnl/gnlobject.c:
+ * gnl/gnlobject.h:
+ * gnl/gnloperation.c:
+ * gnl/gnloperation.h:
+ * gnl/gnlsource.c:
+ * gnl/gnlsource.h:
+ * gnl/gnltypes.h:
+ * gnl/gnlurisource.c:
+ * gnl/gnlurisource.h:
+ * tests/check/gnl/common.c:
+ * tests/check/gnl/common.h:
+ * tests/check/gnl/complex.c:
+ * tests/check/gnl/gnlcomposition.c:
+ * tests/check/gnl/gnloperation.c:
+ * tests/check/gnl/gnlsource.c:
+ * tests/check/gnl/seek.c:
+ * tests/check/gnl/simple.c:
+ Import GNL from 978332e7c4c3bba1949421d28b492540ab471450 'Release 1.4.0'
+
+2014-08-15 18:02:36 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/ges/timelineedition.c:
+ tests: timelineedition: Init GES once at the beginning.
+ Avoiding to forget to init in a particular test and failling stupidly
+
+2014-08-15 18:00:24 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tests/check/ges/backgroundsource.c:
+ test: backgroundsource: Disable tests that rely on nlecomposition internals
+ We can't rely on that, in particular now that it does not actually
+ add its children all the time but only when it is needed (and that
+ it has an internal bin where actual things happen).
+
+2014-08-13 13:15:02 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Add a method to retrieve a pad from a track
+ It allows user to easily get the proxied pad from a track.
+ API:
+ + ges_timeline_get_pad_for_track
+
+2014-07-28 15:26:18 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools:launcher: Avoid commiting when we are not done loading the timeline
+
+2014-07-26 00:31:32 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ ges: Use the new GNL element message feature to notify async operations
+
+2014-07-24 17:55:35 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-base-xml-formatter.c:
+ xml-formatter: message-forward is not something that should be parsed.
+
+2014-07-25 14:47:07 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-timeline.c:
+ timeline: handle async start
+
+2014-10-23 21:46:04 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * configure.ac:
+ * tests/Makefile.am:
+ * tests/validate/Makefile.am:
+ * tests/validate/geslaunch.py:
+ * tests/validate/scenarios/Makefile.am:
+ * tests/validate/scenarios/ges-edit-clip-while-paused.scenario:
+ tests: implement our validate TestManager.
+ And make sure it installs alongside the other validate apps.
+ https://bugzilla.gnome.org/show_bug.cgi?id=739093
+
+2014-10-30 15:54:04 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-video-mixer.c:
+ smart-video-mixer: unref pads_info hash table in dispose
+ Before gst_bin_dispose() runs and destroys elements.
+
+2014-10-30 15:20:18 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-adder.c:
+ smart-adder: fix crash in unit test
+ Unref pads_info hash table in dispose instead of
+ finalize, i.e. before gst_bin_dispose runs and
+ destroys pads_info->bin (to which the pads_info
+ does not hold a ref).
+
+2014-10-27 18:01:56 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 84d06cd to 7bb2bce
+
+2014-10-26 20:36:22 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-track.c:
+ track: fix indentation
+
+2014-10-26 20:35:30 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: don't leak name string
+
+2014-10-26 20:34:29 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-timeline.c:
+ timeline: fix mutex and all_elements hash table leaks
+
+2014-10-26 20:33:50 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-timeline.c:
+ timeline: free tracks obtained via select-tracks-for-object signal
+
+2014-10-26 20:32:41 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-timeline.c:
+ timeline: free track elements list
+
+2014-10-26 20:31:40 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-video-test-source.c:
+ video-test-source: fix caps leak
+
+2014-10-26 20:31:26 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/gstframepositionner.c:
+ framepositionner: fix caps leak
+
+2014-10-26 20:31:08 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-audio-track.c:
+ audiotrack: fix caps leak
+
+2014-10-26 20:30:53 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-video-track.c:
+ videotrack: fix caps leak
+
+2014-10-26 20:30:29 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-track.c:
+ track: don't leak restriction caps
+
+2014-10-26 20:29:06 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-adder.c:
+ smart-adder: don't leak pads_infos hash table
+
+2014-10-26 20:28:09 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-smart-video-mixer.c:
+ smart-video-mixer: don't leak pads_infos hash table
+
+2014-10-26 20:27:17 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-project.c:
+ project: fix string leak
+
+2014-10-26 20:25:46 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-meta-container.c:
+ metacontainer: don't leak GValue contents
+
+2014-10-26 20:24:09 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * tests/check/ges/basic.c:
+ tests: don't leak clips list in basic unit test
+
+2014-10-26 20:23:26 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * Makefile.am:
+ Parallelise 'make check-valgrind'
+
+2014-10-22 14:15:11 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ ges-validate: issues naming have changed.
+ Update the override.
+
+2014-10-21 13:04:26 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From a8c8939 to 84d06cd
+
+2014-10-21 13:01:04 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 36388a1 to a8c8939
+
+2014-10-20 13:37:25 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.4.0 ===
+
+2014-10-20 11:56:36 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.4.0
+
+2014-10-16 14:18:16 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ validate: Fix naming of add_action_type to register_action_type
+ That function was just renamed in Validate
+
+2014-10-12 19:46:59 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ validate: Rename --list-action-types to --inspect-action-type
+ Making clearer the meaning of the parameter and closer to
+ the usual naming in the GStreamer land.
+
+=== release 1.3.90 ===
+
+2014-09-24 11:07:40 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 1.3.90
+
+2014-09-13 16:16:15 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * configure.ac:
+ configure: bump (soft) gst-validate requirement
+ Won't build against all older git versions with
+ 0.0.1.0 as version number.
+
+2014-07-20 11:47:18 +0200 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * tools/ges-launch.c:
+ ges-launch: option to select encoding profile from xml
+ https://bugzilla.gnome.org/show_bug.cgi?id=735121
+
+2014-07-20 11:47:56 +0200 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * tools/ges-launch.c:
+ ges-launch: clean up help page
+ https://bugzilla.gnome.org/show_bug.cgi?id=735121
+
+2014-09-05 23:14:10 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ validate: gst_validate_print_action_types now takes a const gchar **
+
+2014-09-05 23:08:41 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ validate: Add the "ges" as implementer namespace for our action types
+
+2014-09-05 22:09:44 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Fix typo in --help output
+
+2014-09-05 22:08:49 +0300 Sebastian Dröge <sebastian@centricular.com>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.h:
+ ges-launch: Fix compiler warnings
+ ges_validate_print_action_types() takes a const gchar **.
+
+2014-08-22 21:02:58 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Rename edit-clip to edit-container
+ So it represent better what the action does at the GES level
+
+2014-08-22 21:01:07 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ ges-validate: Port to the new GstValidate action registration API
+
+2014-07-24 19:03:50 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ tools:validate: Concider seek in PAUSED position being not exact as WARNING
+ In some corner cases in GNL it is totally correct that a position in a
+ seek in paused is not perfectly exact
+
+2014-08-08 10:41:48 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Let GstValidate handle assert logs
+
+2014-07-27 15:42:42 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ ges:validate: Port to the new handles-states API
+
+2014-08-01 10:44:57 +0200 Edward Hervey <edward@collabora.com>
+
+ * Makefile.am:
+ * common:
+ Makefile: Add usage of build-checks step
+ Allows building checks without running them
+
+2014-07-24 13:23:36 +0300 Lazar Claudiu <lazar.claudiu.florin@gmail.com>
+
+ * ges/ges-text-overlay.c:
+ text-overlay: added text properties as child-properties
+
+2014-06-16 11:38:29 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-internal.h:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-utils.c:
+ * ges/ges-video-transition.c:
+ ges: Use registry to select the compositor element
+
+2014-07-18 18:27:20 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Set errno to 0 before g_ascii_strtoll.
+
+2014-06-12 09:51:02 +0100 Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>
+
+ * ges/ges-smart-video-mixer.c:
+ smart-video-mixer: fix memory leak on error path
+ Coverity 1212166
+
+2014-03-20 17:04:31 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track-element.c:
+ trackelement: Sort paramspec by name in list_children_properties
+ https://bugzilla.gnome.org/show_bug.cgi?id=720023
+
+2014-06-06 12:08:47 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Fix transition handling in ges-launch
+ https://bugzilla.gnome.org/show_bug.cgi?id=730806
+
+2014-06-05 04:20:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/test-utils.h:
+ clip: Fix the ges_clip_set_position function
+ And enhance the new test
+ https://bugzilla.gnome.org/show_bug.cgi?id=731248
+
+2014-06-05 04:05:06 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ clip: Rename top_effect_index to top_effect_index
+ Keeping the old method to not break the API but removing it from the
+ documentation as users should use the new method (which is the exact
+ same with a better naming)
+ https://bugzilla.gnome.org/show_bug.cgi?id=731248
+
+2014-06-05 03:48:12 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/clip.c:
+ tests: Check ges_clip_set_position behaviour
+ + Minor fix to handle properly the feature when clip is not in any layer
+ https://bugzilla.gnome.org/show_bug.cgi?id=731248
+
+2014-06-04 23:16:42 +0200 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * tests/check/ges/clip.c:
+ clip: Add test for effects priorities
+ https://bugzilla.gnome.org/show_bug.cgi?id=731248
+
+2014-06-05 02:16:01 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ * tools/ges-launch.c:
+ pipeline: Add support to rendering without container
+ + Simplify the support in ges-launch as we should not require the
+ profile desc to start with : in that case
+ https://bugzilla.gnome.org/show_bug.cgi?id=731245
+
+2014-05-24 19:16:12 +0200 Christoph Reiter <reiter.christoph@gmail.com>
+
+ * ges/ges-enums.c:
+ Fix invalid GEnumValue.value_name entries.
+ This makes the enum entries in the gir have valid c:identifiers and documentation.
+ https://bugzilla.gnome.org/show_bug.cgi?id=730691
+
+2014-06-03 17:53:23 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-track.c:
+ ges-track: fill the gaps left empty by deactivated track elements.
+
+2014-05-21 10:54:19 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 211fa5f to 1f5d3c3
+
+2014-05-19 12:21:52 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * tests/check/ges/clip.c:
+ ges:clip: Add a method to look for a list of TrackElement-s
+ + Add unit tests to check it works properly.
+ API:
+ + ges_clip_find_track_elements
+
+2014-05-18 18:34:26 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ tools: Handle request state changes from GstValidate
+
+2014-05-15 20:44:35 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ * tests/check/ges/basic.c:
+ ges: Do not forget to update the count when updating already used name
+ That could still lead to naming conflicts
+
+2014-05-15 19:37:05 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-video-source.c:
+ ges: Add a queue after the decoder in video test src
+
+2014-05-14 22:06:55 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/basic.c:
+ ges: Avoid GESTimelineElement naming conflicts
+ When users (can be formatters) set timeline element names in the
+ default 'namespace' we need to update our counter to avoid setting
+ twice the same name on TimelineElements so afterward there is no
+ problem adding them in the GESTimeline
+ + add a testcase to check that new code and fix leaks on the
+ existing testcases.
+ + Sensibly enhance debugs
+
+2014-05-13 14:30:39 +0200 Edward Hervey <edward@collabora.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Don't leak children_props
+ By going through the cleanup code-path
+ CID #1212146
+
+2014-05-10 22:52:18 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-base-xml-formatter.c:
+ * tools/ges-launch.c:
+ ges: Do not use freed pointers
+ COVERTY CID 1212182
+ COVERTY CID 1212184
+ COVERTY CID 1212185
+
+2014-05-10 22:48:12 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-smart-adder.c:
+ ges: Plug a leak in ges-smart-adder
+ COVERTY CID 1212166
+
+2014-05-10 22:45:34 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-xml-formatter.c:
+ ges-xml-formatter: fix memory leak
+ COVERTY CID 1212148
+
+2014-05-10 22:41:23 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-xml-formatter.c:
+ ges-xml-formatter: fix memory leak in error path
+ COVERITY CID 1212147
+
+2014-05-10 22:38:21 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-asset.c:
+ ges: Assert if an asset is not in the global hashtable
+ COVERITY CID 1151679
+
+2014-05-10 22:33:15 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ ges: Remove useless pointer assignement
+ COVERITY CID: 1139442
+
+2014-05-10 22:30:00 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-meta-container.c:
+ ges: Remove useless pointer assignement
+ COVERITY CID: 1139941
+
+2014-05-10 22:28:01 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-meta-container.c:
+ ges: Add license header in ges-meta-container
+
+2014-05-10 22:09:31 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-timeline.c:
+ ges: remove comparison of unsigned inferior to 0
+ COVERITY CID 1139769
+
+2014-05-09 13:00:32 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * ges/ges-xml-formatter.c:
+ ges-xml-formatter: fix memory leak in error path
+ CID 1212146
+
+2014-05-08 17:21:33 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tool: Add the option to set audiosink
+ And use gst_parse_bin_from_description to create the sinks letting more
+ control to users.
+
+2014-05-08 17:11:54 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ scenarios: update the prototype of ges_validate_activate
+ if validate is not present.
+
+2014-05-08 14:12:11 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: add an option to use a custom video sink
+
+2014-05-08 01:38:26 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ validate: make sure we release our ref when we get_timeline.
+ Conflicts:
+ tools/ges-validate.c
+
+2014-05-08 01:15:42 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ scenarios: Check priority before creating a layer.
+
+2014-05-06 15:32:18 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ ges-launch: Make it so actions are executed directly when needed.
+
+2014-05-02 17:11:24 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ validate: implement remove / add clip actions
+ And a helper to get a layer by priority
+
+2014-05-02 16:48:46 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: return TRUE in _set_name when both names match.
+
+2014-05-02 14:17:07 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Only create a layer if needed.
+ That way scenarios can start with an empty timeline
+
+2014-05-02 13:37:04 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ validate: Add add-layer and remove-layer
+
+2014-05-08 01:13:02 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ scenarios: add a remove-asset action
+
+2014-05-01 17:02:05 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-validate.c:
+ ges-validate: add an add-asset action
+ Conflicts:
+ tools/ges-validate.c
+
+2014-05-01 17:00:25 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: When a scenario is set, don't request triplets
+
+2014-05-01 16:59:15 +0200 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+
+ * ges/ges-timeline.h:
+ ges-timeline: Fix ges_timeline_get_project macro.
+ This macro was a little confused about its own meaning.
+
+2014-05-05 11:58:45 +0100 Tim-Philipp Müller <tim@centricular.com>
+
+ * tools/ges-launch.c:
+ ges-launch: remove dead code
+ duration can't be smaller than 0 because it's unsigned,
+ and it can't be 0 because 0 is transformed to CLOCK_TIME_NONE
+ earlier.
+ Coverity CID 1211822.
+
+2014-05-03 10:18:12 +0200 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From bcb1518 to 211fa5f
+
+2014-05-01 10:13:39 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Add a way to look for moved media sample recursively
+ In ges-launch let the user set a folder where the media sample that
+ move can be found recursing into that specified folder.
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-30 20:58:42 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-track.c:
+ * tools/ges-launch.c:
+ tools: Add an option to disable mixing
+ + Add a a GObject property so that the info is seralized
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-30 16:26:03 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-project.c:
+ project: Enhance debugging when updating URI with an invalid one
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-05-02 16:49:10 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ tools: Always activate gst-validate to have position printing
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-29 21:29:54 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Handle times as doubles + concider duration=0 as TIME_NONE
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-26 09:51:37 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ tools: Disable --set-scenario if not compiled against gst-validate
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-26 08:55:31 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-clip.c:
+ ges: Rename remaning tlobj to clip
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-05-02 16:43:42 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ ges-validate: Add an action to serialize the project
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+ Conflicts:
+ tools/ges-validate.c
+
+2014-04-25 18:23:06 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-validate.c:
+ tools:validate: Always seek after editing a clip
+ Otherwize the displayed frame will not be updated when paused.
+ + Add a get_timeline internal helper method in ges-validate.c
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-03-14 12:10:53 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-validate.c:
+ ges-validate: Add a GstValidate action to set children properties
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-02-18 18:52:38 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ * tests/Makefile.am:
+ * tests/scenarios/Makefile.am:
+ * tests/scenarios/ges-edit-clip-while-paused.scenario:
+ scenario: Add a scenario that edits a clip while the pipeline is paused
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-02-18 17:25:05 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-validate.c:
+ tools:validate: Add an action to allow editing clips
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-02-18 15:14:40 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-internal.h:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-xml-formatter.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/uriclip.c:
+ Add a notion of 'name' in GESTimelineElement
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-23 11:28:20 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ tools: Position printing is now done at the gst-validate level
+ https://bugzilla.gnome.org/show_bug.cgi?id=729382
+
+2014-04-17 13:04:26 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Check return value of gst_tag_list_copy_value
+ CID 1139597
+
+2014-04-10 18:03:55 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/gstframepositionner.c:
+ framepositioner: Set the proper default value
+ Most likely a copy/paste error.
+ CID #1139646
+
+2014-04-10 18:01:03 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Use proper value for string extraction
+ The pass would be filled with some bogus (pointer) numerical value
+ CID #1139652
+
+2014-04-10 17:52:20 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-base-xml-formatter.c:
+ base-xml-formatter: Don't attempt to use NULL entry
+ Instead return straight away
+ CID #1139739
+
+2014-04-07 21:02:48 +0200 Christoph Reiter <reiter.christoph@gmail.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-transition.c:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-transition-clip.c:
+ * ges/ges-clip-asset.c:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-effect-asset.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-extractable.c:
+ * ges/ges-formatter.c:
+ * ges/ges-group.c:
+ * ges/ges-image-source.c:
+ * ges/ges-layer.c:
+ * ges/ges-meta-container.c:
+ * ges/ges-multi-file-source.c:
+ * ges/ges-operation-clip.c:
+ * ges/ges-operation.c:
+ * ges/ges-overlay-clip.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-source.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element-asset.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-track.c:
+ * ges/ges-video-transition.c:
+ * ges/ges-video-uri-source.c:
+ Include class related section documentation in the gir file.
+ g-ir-scanner includes section docs as class/interface docs if the section name is equal to the lowercase type name.
+ Since all the documentation is in section blocks, rename them to match the type names.
+ https://bugzilla.gnome.org/show_bug.cgi?id=727776
+
+2014-04-06 16:39:33 +0200 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/Makefile.am:
+ build: Add reference to GstVideo in gir file
+
+2014-03-26 23:48:45 +0100 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * ges/ges-types.h:
+ multifilesrc: remove unused class declaration
+
+2014-03-26 23:47:03 +0100 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * ges/Makefile.am:
+ build: install ges-version.h
+
+2014-03-26 11:45:07 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * tools/ges-launch.c:
+ ges-launch: Not being able to load an asset is an error
+
+2014-03-21 10:22:52 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ges/ges-pipeline.c:
+ pipeline: Do not set EncodingProfile.presence when we have no track for the type
+ That leads to freeze as encodebin will be waiting for a pad and EOS
+ forever
+
+2014-01-09 16:31:01 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Format the position printing as in gst-validate and friends
+
+2013-09-13 18:15:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ * tools/Makefile.am:
+ * tools/ges-launch.c:
+ * tools/ges-validate.c:
+ * tools/ges-validate.h:
+ ges-launch: Play nicely with gst-validate if avalaible
+
+2013-09-20 01:31:10 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Add a way to mute test video and audio output
+
+2013-10-12 10:07:28 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Properly handle project loading
+ So we start the pipeline only when the project is done loading, and we save it when it is
+ loaded, taking into account possible media URI updates.
+
+2013-11-12 12:13:31 +0100 Lubosz Sarnecki <lubosz@gmail.com>
+
+ ges: multifilesrc support
+ * GESMultiFileSource class
+ * multifilesrc example
+ * Support multifile:// urls in uri asset
+ * start/stop index modification
+ * Doc
+ https://bugzilla.gnome.org/show_bug.cgi?id=719373
+
+2014-03-16 12:48:22 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.2.0 ===
+
+2014-03-16 12:46:26 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * ChangeLog:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * ges/ges.c:
+ * gst-editing-services.doap:
+ Release 1.2.0
+
+2014-03-15 10:34:17 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ * ges/ges-version.h.in:
+ * ges/ges.h:
+ Properly generate versioning #define-s during autogen
+
+2014-03-14 20:04:33 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ * ges/ges-timeline-element.c:
+ * tests/check/ges/group.c:
+ container: Properly handle the case where we could not set parent
+ In this case we had a FIXME about reverting everything that was done,
+ implement that FIXME!
+
+2014-03-14 19:59:27 +0100 Andreas Schwab <schwab@linux-m68k.org>
+
+ * ges/ges-smart-adder.c:
+ ges: remove extra semicolon
+ https://bugzilla.gnome.org/show_bug.cgi?id=726365
+
+2014-03-14 18:48:44 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Always set the encoding profile presence to 1
+ We currenty do not support multiple tracks with same type in GESPipeline
+ and we actually need to set the presence field to avoid a scenario where
+ we have only video in a video track, and no audio in the audio track. So
+ audiotestsrc is used and we end up encoding the whole audio stream but
+ no decoded video frame as reached the decodebin src pad, so the pad
+ has not been created and thus it will not be linked to the encodebin.
+ On the audio part, the EOS will be emitted so fast that the resulting stream will
+ not have any video in it as the muxer will not even have a video pad created.
+ Setting the presence will ensure that the muxer does have a video pad
+ (because of how encodebin behaves) and thus will create a pad for it
+ and wait for its EOS.
+
+2014-03-10 11:18:21 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ title-source: Rename ges_title_clip_set_backrgound_colour as appropriate
+ The method was badly called _clip_ instead of _source_ we have not release the API
+ so we still can change it.
+
+2014-03-08 11:26:13 +0000 Dan Williams <dcbw@redhat.com>
+
+ * ges/ges-project.c:
+ ges: fix finalize/dispose mixup
+ https://bugzilla.gnome.org/show_bug.cgi?id=725918
+
+2014-03-07 14:48:06 -0600 Dan Williams <dcbw@redhat.com>
+
+ * ges/ges-clip-asset.c:
+ * ges/ges-container.c:
+ * ges/ges-formatter.c:
+ * ges/ges-project.c:
+ * ges/gstframepositionner.c:
+ ges: Ensure GObject finalize and dispose methods chain up to parents
+ https://bugzilla.gnome.org/show_bug.cgi?id=725918
+
+2014-03-07 09:28:16 -0600 Dan Williams <dcbw@redhat.com>
+
+ * ges/ges-base-xml-formatter.c:
+ Fix use-after-free in _free_pending_clip()
+ https://bugzilla.gnome.org/show_bug.cgi?id=725855
+
+2014-02-28 09:37:01 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From fe1672e to bcb1518
+
+2014-02-26 04:36:11 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * docs/design/encoding-research.txt:
+ * docs/design/metadata.txt:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ Update the documentation to use Pitivi instead of PiTiVi
+
+2014-02-26 04:17:36 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-base-effect.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-transition-clip.c:
+ Remove "#" from short-description
+ It breaks the display in the index.html page.
+
+2014-02-18 22:14:00 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-extractable.c:
+ Minor documentation fixes: GESExtractable, GESAsset
+
+2014-02-26 22:16:13 +0100 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 1a07da9 to fe1672e
+
+2014-02-18 12:40:06 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ docs: Fix documentation about copying timeline elements
+
+2014-02-17 13:33:51 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Make sure not to add 2 times a TrackElement in the same track
+ Without that, if a UriClip contains several tracks of a same type (ie.
+ video or audio...), we would add all the TrackElements to each track
+ making everything failling as we end up with several GNL sources at
+ the same position with the same priority.
+
+2014-02-17 12:34:04 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-pipeline.c:
+ * tests/check/ges/integration.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename TIMELINE_MODE_XXX to GES_PIPELINE_MODE_XXX so it corresponds to reality
+ First, it was not in any namespace, second the name of the enum is
+ GESPipelineFlags.
+
+2014-02-14 13:20:31 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ ges: Give a reference to the formatter for the idle callback
+ This avoid segfault as we are not guaranteed that the formatter will
+ not be destroyed in the meantime.
+ + Minor cleanup of handling of private members
+ https://bugzilla.gnome.org/show_bug.cgi?id=724337
+
+2014-02-09 23:50:25 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-meta-container.c:
+ * ges/ges-pitivi-formatter.c:
+ ges: Remove unused functions
+
+2014-02-08 20:19:53 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-group.c:
+ ges-group: Properly check for integer underflows
+ error: comparison of unsigned expression < 0 is always false
+
+2014-02-08 20:18:11 +0100 Sebastian Dröge <sebastian@centricular.com>
+
+ * ges/ges-extractable.c:
+ ges-extractable: Return NULL instead of G_TYPE_INVALID
+ The return type of this function is gchar *, not GType
+
+2014-02-05 00:10:52 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-enums.h:
+ ges: Some documentation cleanup
+
+2014-02-04 13:58:48 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests:integration: Fix a race about get_position being called before AYNC_DONE happens
+
+2014-02-04 10:45:58 +0100 Alexandru Băluț <alexandru.balut@gmail.com>
+
+ * bindings/python/examples/simple.py:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pipeline.c:
+ * ges/ges-pipeline.h:
+ * tests/check/ges/integration.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-utils.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ ges-pipeline: Rename add_timeline to set_timeline
+ API BREAKAGE:
+ - ges_pipeline_add_timeline
+ + ges_pipeline_set_timeline
+
+2014-01-16 15:25:06 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * ges/ges-uri-asset.c:
+ uri-asset: Let a chance for user to change URI if the set one is not readable
+ It sounds like the most logical thing to do in that case.
+ https://bugzilla.gnome.org/show_bug.cgi?id=721111
+
+2014-01-15 19:12:08 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-video-source.c:
+ * ges/ges-video-uri-source.c:
+ videosource: Always add a deinterlace at the beining of videosrcbin
+ It might be needed in some cases (for example when decoding prores files) and
+ it is the way it is done with playbin now. Also deinterlace now properly supports
+ passtrough mode.
+
+2014-01-27 15:30:40 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/uriclip.c:
+ ges-asset: Do not forget to give a ref to the registry
+ + Add test in the testsuite
+ + Fix broken tests
+ https://bugzilla.gnome.org/show_bug.cgi?id=721111
+
+2014-01-30 10:46:09 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * common:
+ Automatic update of common submodule
+ From d48bed3 to 1a07da9
+
+2014-01-09 18:13:00 +0100 Mathieu Duponchelle <mduponchelle1@gmail.com>
+
+ * ges/ges-track-element.c:
+ track-element: clamp interpolated keyframe values.
+
+2014-01-10 00:05:01 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * .gitignore:
+ .gitignore: add test driver and more test binaries
+
+2013-12-27 10:08:47 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ base-xml-formatter: Emit 'loaded' right after a project with empy timeline is loaded
+ https://bugzilla.gnome.org/show_bug.cgi?id=720040
+
+2013-12-24 15:34:51 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-audio-uri-source.h:
+ * ges/ges-image-source.h:
+ * ges/ges-internal.h:
+ * ges/ges-title-source.h:
+ * ges/ges-video-test-source.h:
+ * ges/ges-video-uri-source.h:
+ track-element: Remove constructors for TrackElement from the API
+ Most of the time the user should not create GESTrackElements
+ himself, instead he should add a GESAsset to a layer, that will
+ result in a clip creation and the proper TrackElements to be
+ created and added to the tracks.
+ The case of effects and overlays is a bit different as the user should
+ create the TrackElement and add them to a clip.
+
+2013-12-24 15:08:24 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-track.c:
+ ges: Remove versionning infos now that we start on the 1.X API serie
+ They are now meaningless, all the current symbols are the basic
+ ones for the 1.X serie.
+
+2013-12-24 14:34:09 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-effect-asset.h:
+ * ges/ges-smart-adder.h:
+ * ges/ges-smart-video-mixer.h:
+ * ges/ges-xml-formatter.h:
+ * ges/gstframepositionner.h:
+ ges: Add padding for API extension where missing
+
+2013-12-22 22:36:16 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * autogen.sh:
+ * common:
+ Automatic update of common submodule
+ From dbedaa0 to d48bed3
+
+2013-11-28 15:13:06 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * ges/ges-timeline.c:
+ ges: Reimplement 'always create a project to back a timeline'
+ Keeping it simple, and making sure everything is synchronous
+
+2013-11-28 15:08:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/mixers.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ Revert "ges: Always create a project to back a timeline"
+ This reverts commit 59d83f1a93055391097e7c1fe34f5a39eb8ec625.
+ Conflicts:
+ tests/check/ges/backgroundsource.c
+ tests/check/ges/effects.c
+ tests/check/ges/overlays.c
+ tests/check/ges/simplelayer.c
+ tests/check/ges/text_properties.c
+ tests/check/ges/titles.c
+
+2013-11-25 15:17:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * ges/ges-timeline.c:
+ timeline: Add all assets of the clip added to the timeline to the project
+
+2013-11-22 17:49:49 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-simple-layer.c:
+ * ges/ges-simple-layer.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/titles.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/thumbnails.c:
+ * tools/ges-launch.c:
+ Remove GESSimplerLayer, that API should land into GESLayer in the end
+ The priority handling of clip is now handled by GESLayer itself, and
+ handling clip as a ordered list should be implemented in GESLayer itself
+ too, this way the user can decide to switch mode at any time instead of
+
+2013-11-22 17:36:12 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-layer.c:
+ layer: Set clip start to the duration of the layer if == TIME_NONE
+ In the provided start of a clip is GST_CLOCK_TIME_NONE in
+ ges_layer_add_asset, it means that we want the clip to be
+ added at the end of the layer
+
+2013-11-22 17:33:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-layer.c:
+ * ges/ges-layer.h:
+ layer: Add an API to get the total duration of the layer
+ API:
+ ges_layer_get_duration
+
+2013-11-22 17:23:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Remove the android/ toplevel directory
+ To build gstreamer for android we are now using androgenizer which
+ generates the needed Android.mk files. Androgenizer can be found here:
+ * http://cgit.collabora.com/git/android/androgenizer.git/
+
+2013-11-13 13:18:00 +0100 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * ges/Makefile.am:
+ * ges/ges-gerror.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-utils.c:
+ * ges/ges-utils.h:
+ gir: fix warnings
+
+2013-11-18 13:41:07 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-video-uri-source.c:
+ video-uri-source: Handle interlaced videos
+ https://bugzilla.gnome.org/show_bug.cgi?id=710168
+
+2013-11-14 16:17:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Let user set the track types to use
+
+2013-11-09 09:55:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/examples/material.py:
+ python: Remove old material.py example
+
+2013-11-09 09:51:55 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Dot the pipeline on state changes and warnings
+
+2013-11-09 09:49:03 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/gstframepositionner.c:
+ framepositionner: Fix the range of properties dealing with number of pixels
+ This way it is possible to interpolate those values.
+
+2013-11-09 09:47:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/mixers.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ ges: Always create a project to back a timeline
+ And fix all the tests as we need to wait for the project to be loaded
+ to check the reference count of the timeline (as we keep a ref on the
+ timeline in project to later emit "loaded" on idle).
+
+2013-11-09 09:46:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/examples/simple.py:
+ bindings: Cleanup and fix simple python example
+
+2013-11-05 11:23:08 +0000 Tim-Philipp Müller <tim@centricular.com>
+
+ * common:
+ Automatic update of common submodule
+ From 865aa20 to dbedaa0
+
+2013-10-17 15:16:00 +0200 Kishore Arepalli <kishore.arepalli@gmail.com>
+
+ * ges/ges-audio-source.c:
+ * ges/ges-image-source.c:
+ * ges/ges-pipeline.c:
+ * ges/ges-smart-adder.c:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-source.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track.c:
+ * ges/ges-video-track.c:
+ ges: Fix several memory leaks
+ https://bugzilla.gnome.org/show_bug.cgi?id=710390
+
+2013-10-30 00:27:36 +0100 Mathieu Duponchelle <mduponchelle1@gmail.com>
+
+ * ges/ges-clip.c:
+ track-element: add start to the position to which we wish we split the bindings.
+ The bindings split is relative to the beginning of the clip.
+
+2013-10-29 07:59:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Fix the find_track_element method
+ What we want is to be able to find a TrackElement by its type, and
+ possibly specify a Track where to look into.
+
+2013-10-15 10:57:31 +0200 Kishore Arepalli <kishore.arepalli@gmail.com>
+
+ * ges/ges-extractable.h:
+ * ges/ges-formatter.h:
+ * ges/ges-uri-asset.h:
+ ges-formatter: don't use 'class' as function argument name in headers
+ It's a keyword in C++ and C++ compilers won't like it.
+ https://bugzilla.gnome.org/show_bug.cgi?id=710172
+
+2013-10-11 17:00:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-uri-asset.c:
+ uri-asset: Check if file exists before trying it as a proxy
+ This avoids:
+ 1- discovering file that we know do not exist
+ 2- proposing the current proxy path (that failed) as a possible proxy
+ which lead to errors
+
+2013-10-11 17:05:03 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Remember URIS that we tried to discover
+ So we do not fail several time trying to discover the same URI
+ Conflicts:
+ tools/ges-launch.c
+
+2013-10-08 13:45:55 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Try to use best formatter first
+
+2013-10-09 20:07:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-audio-source.c:
+ audiosource: Add audioconvert and audioresample before the volume element
+ https://bugzilla.gnome.org/show_bug.cgi?id=709777
+
+2013-10-01 20:07:10 +0200 Mathieu Duponchelle <mduponchelle1@gmail.com>
+
+ * ges/ges-timeline.c:
+ timeline: pass the correct argument to disconnect_by_func.
+ fixes #709205
+
+2013-09-28 21:07:10 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ Back to development
+
+=== release 1.1.90 ===
+
+2013-09-28 20:49:13 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ChangeLog:
+ * configure.ac:
+ Release 1.1.90
+
+2013-09-28 18:09:49 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ clip: Return the newly created TrackElement when adding an asset
+ This is a minor API change
+
+2013-09-28 15:42:20 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Add a signal to know when it is commited
+
+2013-09-27 02:56:44 +0200 MathieuDuponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-clip.c:
+ clip: split_bindings at position * + inpoint*
+
+2013-09-25 23:52:46 +0200 MathieuDuponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-video-track.c:
+ video-track: update gaps framerate along with restriction caps.
+
+2013-09-25 19:48:45 +0200 MathieuDuponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-xml-formatter.c:
+ xml-formatter: add support for restriction caps.
+
+2013-09-05 01:03:51 +0200 Simon Corsin <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-video-source.c:
+ * ges/gstframepositionner.c:
+ * ges/gstframepositionner.h:
+ video-source: Add a videorate in video-source.
+ And control it in framepositionner.
+ Conflicts:
+ ges/ges-video-source.c
+
+2013-09-24 18:35:56 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 6b03ba7 to 865aa20
+
+2013-09-22 21:56:14 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/clip.c:
+ * tests/check/ges/layer.c:
+ tests: Make sure not to test freed objects type
+
+2013-09-16 13:30:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Let some queuing in encodebin
+ It is sometimes necessary
+
+2013-09-16 11:19:13 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ * tests/examples/ges-ui.c:
+ examples: Make project loading more generic
+ Pitivi formatter is deprecated, do not use it by default
+
+2013-09-16 11:16:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ Revert "ges: Disable the Pitivi formatter"
+ This reverts commit e54ceff7204e712daa9949ef41b73d96035a0446.
+ Let's just keep it... it does not cost anything.
+
+2013-09-20 16:19:06 +0200 Edward Hervey <edward@collabora.com>
+
+ * common:
+ Automatic update of common submodule
+ From b613661 to 6b03ba7
+
+2013-09-19 18:46:26 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 74a6857 to b613661
+
+2013-09-19 17:39:44 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 12af105 to 74a6857
+
+2013-09-14 04:19:57 +0200 Joris Valette <joris.valette@gmail.com>
+
+ * tests/check/ges/timelineedition.c:
+ tests: timelineedition: cast start and duration values as guint64
+
+2013-09-13 20:38:43 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Handle path for project uri
+
+2013-09-13 20:37:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-xml-formatter.c:
+ formatter: Keep timeline duration as a meta
+
+2013-09-12 18:34:55 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * tools/ges-launch.c:
+ ges-launch: Make it possible to provid pathes to look for moved asset
+ For example if a project was sent from someone else thus the pates in
+ there are meaningless on the other computer, we need to be able
+ to specify a list of pathes where the files are.
+ + Fix documentation
+
+2013-09-12 09:05:51 +0200 Kishore Arepalli <kishore.arepalli@gmail.com>
+
+ * ges/ges-pipeline.c:
+ ges-pipeline: Don't unref buffer obtained from a GstSample
+ https://bugzilla.gnome.org/show_bug.cgi?id=707914
+
+2013-09-10 18:17:57 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/integration.c:
+ tests: integration: set restriction_caps on the video encoding profile
+ We need this cause now videomixer renegotiates downstream.
+
+2013-09-09 12:47:32 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pipeline.c:
+ * ges/ges.c:
+ pipeline: Create it through a factory
+ Making it possible to use it with GstValidate LD_PRELOAD feature
+
+2013-09-09 12:47:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pipeline.c:
+ * ges/ges-pipeline.h:
+ * tests/examples/thumbnails.c:
+ pipeline: Finnish renaming from GESTimelinePipeline
+
+2013-09-08 19:27:04 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/timelineedition.c:
+ tests: timelineedition: Minor cleanups
+
+2013-09-08 19:19:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Make sure we do not move object when only trimming
+ We were missing a few checks so that we do not move objects when their
+ duration is equal to the max duration, or 0
+
+2013-09-07 12:59:17 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Avoid setting duration > max_duration when rippling
+ We should use the trimming method to set duration to make sure to avoid
+ going over the max duration.
+ Also avoid computing when setting duration to the same old value.
+
+2013-09-07 02:11:23 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-group.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/timelineedition.c:
+ ges: Handle trimming in groups
+ This was broken, clips where moving all around, make it behave properly.
+
+2013-09-07 02:10:12 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Make ripple start be trimming
+ This is a more natural behaviour as a user instead of doing nothing at
+ all.
+
+2013-09-03 20:50:54 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-audio-source.c:
+ * ges/ges-video-source.c:
+ ges: Make GESAudioSource and GESVideoSource abstract
+
+2013-08-22 23:06:38 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/gstframepositionner.c:
+ gstframepositionner: correctly tag metadata.
+ We do not use GST_VIDEO_META_TAG_STR as it would mean depending on
+ GstVideo which is not the case right now
+
+2013-09-01 12:19:32 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-video-source.c:
+ videosource: Make sure to update z-order when layer priority changes
+ Conflicts:
+ ges/ges-video-source.c
+
+2013-09-01 12:18:53 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ timeline-element: Add a set_parent vmethod
+ API:
+ GESTimelineElment->set_parent vmethod
+
+2013-08-29 11:35:30 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * tests/check/ges/timelineedition.c:
+ tests: timelineedition: Add a test_scaling.
+ It will check that the clip updates its size correctly.
+
+2013-08-25 17:08:00 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ test-utils: Adds a utility function to quickly check the timeline.
+
+2013-08-15 20:12:30 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-video-transition.c:
+ videotransition: No need to hard set width and height anymore.
+
+2013-08-21 11:32:45 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-video-source.c:
+ * ges/gstframepositionner.c:
+ * ges/gstframepositionner.h:
+ gstframepositionner: Install width and height properties.
+ + And manage them properly.
+
+2013-08-17 14:57:15 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-audio-track.c:
+ * ges/ges-internal.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ ges-track: Add the notion of resriction caps to GESTrack
+ This way we can let the user determine what he want to come out of the
+ track.
+ API:
+ - ges_track_set_caps (The track caps are now construct only)
+ + ges_track_set_restriction_caps
+ + ges_track_get_restriction_caps
+ + GESTrack.props.restriction_caps
+
+2013-07-09 15:31:15 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-audio-source.c:
+ * ges/ges-audio-source.h:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-audio-uri-source.c:
+ * ges/ges-audio-uri-source.h:
+ * ges/ges-image-source.c:
+ * ges/ges-image-source.h:
+ * ges/ges-source.c:
+ * ges/ges-source.h:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-video-source.c:
+ * ges/ges-video-source.h:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-test-source.h:
+ * ges/ges-video-uri-source.c:
+ * ges/ges-video-uri-source.h:
+ * ges/ges.h:
+ * tests/check/ges/uriclip.c:
+ GES: Add GESVideoSource and GESAudioSource base classes
+ + Update documentation.
+ + Implements subclasses audio-uri-source and video-uri-source
+
+2013-07-03 18:27:00 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-audio-test-source.c:
+ * ges/ges-video-test-source.c:
+ testsource: Handle child properties as child properties
+ Makes $make check pass.
+ Standardizes property handling.
+
+2013-07-02 11:12:00 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-source.c:
+ source: Make a ges_source_create_topbin internal helper method
+
+2013-06-27 14:20:00 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-audio-test-source.c:
+ * ges/ges-image-source.c:
+ * ges/ges-source.c:
+ * ges/ges-source.h:
+ * ges/ges-title-source.c:
+ * ges/ges-uri-source.c:
+ * ges/ges-uri-source.h:
+ * ges/ges-video-test-source.c:
+ ges-source: Move common elements handling to the base class
+ + And port all the subclasses
+
+2013-09-02 13:57:15 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: Do not forget to initialize the timeline before using it
+
+2013-09-02 00:19:30 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * ges/ges-xml-formatter.c:
+ ges-xml-formatter: use g_ascii_dtostr() instead of messing with setlocale()
+ Libraries shouldn't use setlocale().
+
+2013-09-01 00:46:45 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: set LC_NUMERIC locale before saving values.
+ Avoiding to save ',' instead of '.' for floats in certain locals
+
+2013-08-29 23:45:56 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-video-transition.c:
+ video-transition: Keep switch transition type simple
+ Also make sure there is a proper default value for transition type.
+
+2013-08-30 20:32:56 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ launch: Simplify encoding profile description
+ Use a 'simple' synthax to describe encoding profiles
+
+2013-08-30 20:03:16 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ launch: Make it easier to render project
+ Now providing an output uri is enough to tell that you want to render.
+ It will use project rendering infos when possible, missing a way to
+ specify which info if various are disponnible (we use the first one
+ right now).
+ + Make options more logical now, -l mean --load, and -r means repeat
+
+2013-08-30 18:45:31 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: No autotrans between elements in same toplevel container
+ This makes no sense, we ended up creating/removing tons of transition
+ while moving groups
+
+2013-08-29 11:10:33 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ ges: Disable the Pitivi formatter
+ It lacks to many feature and the code is too bad, desactivation until
+ someone comes and fix it... The code should be removed if it never
+ happens
+
+2013-08-28 19:56:29 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-clip.c:
+ ges-clip: when a child, is removed, disconnect from its notifies.
+
+2013-08-27 19:12:26 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: Make use of g_assert_no_error when it makes sense
+
+2013-08-27 18:40:55 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ tests: Let use 20 sec to execute tests
+
+2013-08-26 23:31:14 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-base-effect.c:
+ * ges/ges-effect.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-uri-source.c:
+ trackelement: Simplify the way we handle children properties
+ So subclass do not have to implement a new logic all the time, but
+ instead can use a simple method to add properties as needed.
+
+2013-08-26 19:26:08 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-effect-clip.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-track-element.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-uri-clip.c:
+ clip: Remove the ges_clip_fill_track method
+ Its was only use by the old custom source which is dead now.
+ API:
+ Remove ges_clip_fill_track
+ https://bugzilla.gnome.org/show_bug.cgi?id=706855
+
+2013-08-26 19:15:08 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-custom-source-clip.c:
+ * ges/ges-custom-source-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/timelineedition.c:
+ ges: Remove custom clip
+ If you want a custom clip then you have to subclass GESClip,
+ This class was pre historicall and only used for testing purposes, we
+ have GESTestClip for that.
+ https://bugzilla.gnome.org/show_bug.cgi?id=706855
+
+2013-08-26 19:25:20 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * acinclude.m4:
+ Remove acinclude.m4 as we do not use it
+ and it is anyway removed by autogen.sh
+
+2013-08-26 18:56:49 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ integration: Add titles test
+
+2013-08-26 17:41:14 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/gi/overrides/GES.py:
+ python: Do not initialize GES at import time
+
+2013-08-24 18:21:26 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * tests/check/ges/test-utils.c:
+ tests: fix NULL pointer dereference, ternary operator silliness and message type use
+
+2013-08-24 11:39:11 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/test-utils.c:
+ tests: Give more debugging info when samples could not be generated
+
+2013-08-24 02:41:07 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/project.c:
+ tests: Fix make distcheck
+ We need to make sure that we can write to the directory where we save
+ project files, so doing it in the tmp folder.
+ + Properly dist test data files
+
+2013-08-13 18:05:55 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-clip.c:
+ * ges/ges-internal.h:
+ * ges/ges-track-element.c:
+ trackelement: split bindings correctly.
+
+2013-08-13 17:57:33 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-element.c:
+ trackelement: update control bindings correctly.
+ When duration or inpoint change, we need to remove edge control points,
+ and set new control points with interpolated values.
+ Also when duration == 0, we need to remove all control points, as otherwise
+ the controller will raise !is_end assertions.
+ It's the duty of the application to set keyframes back when duration gets
+ != 0 again.
+
+2013-08-12 21:25:31 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-container.c:
+ container: resort children after prepending an element.
+
+2013-08-12 16:13:40 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-timeline.c:
+ timeline: when there are no objects anymore, set duration to 0.
+
+2013-08-12 15:01:53 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-track.h:
+ ges-audio-track: Change contructor prototype.
+ We return an AudioTrack.
+
+2013-08-11 20:06:49 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pipeline.c:
+ * ges/ges-pipeline.h:
+ pipeline: add a get_mode method.
+
+2013-08-07 19:37:49 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ basexmlformatter: Only set timeline auto transitions when done loading.
+
+2013-08-07 16:12:27 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/integration.c:
+ integration: make test_basic be two concatenated clips.
+
+2013-08-20 08:22:24 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Plug leaks in the can_save_to_uri method
+ https://bugzilla.gnome.org/show_bug.cgi?id=679941
+
+2013-08-19 15:13:48 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Remove the dynamic lock
+ We actually do not need it has everywhere where we would need it we are
+ already locked against the timeline.dyn_lock, we need to make sure it is
+ always the case in the future.
+ The hierarchy of the mutex was wrong and could possibly lead to
+ deadlocks
+
+2013-08-19 15:12:48 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ formatter: Remove the unsed can_save_uri vmethod
+ This virtual method does not make much sense right now, we might need it
+ again later, but most probably with a sensibly different API so removing
+ it for now.
+
+2012-07-20 14:19:01 +0200 Paul Lange <palango@gmx.de>
+
+ * ges/ges-formatter.c:
+ ges-formatter: Check if directory of URI is writeable
+ https://bugzilla.gnome.org/show_bug.cgi?id=679941
+
+2013-08-06 18:35:24 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Rework the way we handle seeking while fully paused
+ The idea is that we should first play until the time we reach the first
+ position, at that point we PAUSE the pipeline, then, afterward do the
+ seeks as asked.
+ If we get the position before the ASYNC DONE, just accept it.
+
+2013-08-05 01:07:36 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: PNG file was renamed to png.png
+
+2013-08-04 17:46:33 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Add a list tests only option
+ Also add an empty filed in the GOptionEntry array avoiding segfaults
+
+2013-08-02 14:23:13 +0200 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * configure.ac:
+ build: add subdir-objects to AM_INIT_AUTOMAKE
+ Fixes warnings with automake 1.14
+ https://bugzilla.gnome.org/show_bug.cgi?id=705350
+
+2013-08-04 17:35:20 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Only use 2 layers for the mixing for now
+ + Call the TSuite "integration" instead of "render"
+
+2013-08-04 16:14:42 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: check: All assets moved to assets/
+
+2013-08-03 17:01:22 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Make it possible to list all avalaible tests
+ You can not use make check-integration --list-tests, you have to use
+ ./integration --list-tests instead
+
+2013-08-02 14:16:26 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Add support for group-id in the stream-start event
+
+2013-08-01 18:14:36 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ Fix compilation
+
+2013-08-01 17:56:16 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Avoid leak
+
+2013-08-01 17:47:50 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Do not forget to set encoding profiles
+
+2013-07-18 23:09:51 +0200 Vasilis Liaskovitis <vliaskov@gmail.com>
+
+ * tests/check/ges/integration.c:
+ integration: add mixing tests
+ Add a new test that creates a given number of layers. Each layer has the same
+ assets / clips shifted by a different amount in the timeline. Alpha and volume
+ properties are different for each layer. This test is similar to the mixer
+ example in:
+ http://gist.github.com/MathieuDuponchelle/5736992#file-mixit-py
+ We should be able to add more clips to each layer, but this example test only
+ tests mixing 1 clip across 4 layers.
+ Conflicts:
+ tests/check/ges/integration.c
+
+2013-08-01 11:31:16 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/integration.c:
+ tests/integration: display test name when running it.
+
+2013-08-01 11:32:44 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/assets/png.png:
+ * tests/check/ges/integration.c:
+ tests/integration: add an asset directory.
+
+2013-07-23 01:50:28 +0200 Vasilis Liaskovitis <vliaskov@gmail.com>
+
+ * tests/check/ges/integration.c:
+ integration: add seek tests to paused pipeline (no playing)
+ This second set of seeking tests performs the seeks in a PAUSED
+ pipeline. After all seeks are successful, the pipeline is resumed so that the
+ test does not timeout.
+ Conflicts:
+ tests/check/ges/integration.c
+
+2013-07-19 00:40:00 +0200 Vasilis Liaskovitis <vliaskov@gmail.com>
+
+ * tests/check/ges/integration.c:
+ integration: add paused pipeline seek tests
+
+2013-07-16 19:42:53 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/integration.c:
+ tests/integration: adds image_filename in the test generation macro
+
+2013-07-27 10:18:30 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ project: Make sure error-loading-asset is emited when needed
+ In case ges_project_try_updating_id would be called from outside ges-project the signal
+ was not emitted, change that.
+ + Add some debugging
+
+2013-07-24 22:37:06 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Add methods to get and set the snapping distance
+
+2013-07-24 14:26:18 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-internal.h:
+ * ges/ges-track-element.c:
+ clip: Fix the spliting method
+ We should make sure that the newly created trackelement are inside
+ a container when adding them to as this is needed for GESUriClip-s.
+ Also do not try to set a child property on the TrackElement itself.
+ https://bugzilla.gnome.org/show_bug.cgi?id=703152
+
+2013-07-23 19:20:34 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Let the user mute the tests when needed
+ Add an environment variable so the user can make sur that
+ integration tests use fakesinks instead of real sinks
+
+2013-07-22 20:06:25 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/mixers.c:
+ tests: Make sure we can have the results into an XML file
+
+2013-07-21 21:41:13 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pipeline.c:
+ pipeline: Check that the profile could actually be set on the encodebin
+ Setting the profile on an encodebin can fail, and if that happens, there
+ will be no profile set at all, we should return FALSE in GESPipeline
+ when that happens
+
+2013-07-01 16:27:54 +0200 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * android/ges.mk:
+ * docs/libs/architecture.xml:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-enums.h:
+ * ges/ges-pipeline.c:
+ * ges/ges-pipeline.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/integration.c:
+ * tests/check/ges/mixers.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESTimelinePipeline to GESPipeline
+ rename ges_timeline_pipeline methods to ges_pipeline
+
+2013-07-17 22:48:17 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Add some pipeline dumps
+ When we go to PLAYING, or when we get an error on the bus
+ + Activate the hack so that we dump the pipeline on first buffer
+ pushed by the smart adder
+
+2013-07-17 22:47:31 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Add video/audio only basic tests
+
+2013-07-17 20:54:20 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-uri-asset.c:
+ * tests/check/ges/integration.c:
+ tests: integration: Give some more information to user on errors
+
+2013-07-17 18:34:22 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: intergration: Add some more encoding profiles
+
+2013-07-17 16:09:29 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Give the user more details about failure when checking transcoded file
+
+2013-07-17 16:06:09 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Be more precise with namings
+ Fully define formats in the namings
+ + Add an mp3 + h264 in mov test
+
+2013-07-17 13:06:05 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Refactor and make easier to add encoding profiles
+
+2013-07-17 12:31:02 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/integration.c:
+ tests: integration: Add audio/video only seeking tests
+
+2013-07-17 12:05:26 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: integration: Remove the effect in test_seeking
+ It complexifies the test but this is not what we actually want in
+ that test
+
+2013-07-16 21:58:16 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: Remove prints
+ And use GST_DEBUG and friends instead
+
+2013-07-14 14:03:46 +0200 Vasilis Liaskovitis <vliaskov@gmail.com>
+
+ * tests/check/ges/integration.c:
+ integration: some fixes for seek tests
+ - Use g_list_remove_link so that ordering of seeks is not mandatory
+ - use g_slice allocator for SeekInfo structs
+ - Fix leak in freeing seek list
+ - Check for NULL seeks at end of test, otherwise fail and free failed seeks
+
+2013-07-13 15:15:04 +0200 Vasilis Liaskovitis <vliaskov@gmail.com>
+
+ * tests/check/ges/integration.c:
+ integration: add SeekInfo and get_position callback for seek tests
+ A Seekinfo structure consists of 2 fields:
+ - position: the position to seek to
+ - seeking_position: the position to perform the seek from
+ Seeks can be appended to a global list e.g. from code:
+ seeks = g_list_append (seeks, new_seek_info (0.2 * GST_SECOND, 0.6 * GST_SECOND));
+ seeks = g_list_append (seeks, new_seek_info (1.0 * GST_SECOND, 1.2 * GST_SECOND));
+ seeks = g_list_append (seeks, new_seek_info (1.5 * GST_SECOND, 1.8 * GST_SECOND));
+ The get_position callback checks the current position and attempts to perform
+ the corresponding seek with gst_element_seek_simple
+
+2013-07-02 20:50:05 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-uri-clip.c:
+ ges-uri-clip: Add the possibility to specify an assets directory
+ through the GES_TESTING_ASSETS_DIRECTORY environment variable.
+
+2013-07-12 19:44:46 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/integration.c:
+ tests: Simplifie integration tests using macros all around
+
+2013-04-26 00:03:21 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * Makefile.am:
+ * tests/check/Makefile.am:
+ * tests/check/ges/integration.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ tests: Add integration tests
+ Those are test with real media files, they are run separetely from other
+ unit tests using the make check-integration command (can be done from
+ the toplevel directory)
+
+2013-04-28 00:22:42 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ test-utils: Add test file generation code.
+
+2013-06-28 15:49:03 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-element.c:
+ track-element: No need to log when prio == MIN_GNL_PRIO.
+
+2013-06-27 23:33:21 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/gstframepositionner.c:
+ framepositionner: fix messup with propname enum.
+
+2013-06-26 23:23:59 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ track: Make it possible to disable the mixing feature
+ API:
+ ges_track_set_mixing
+ ges_track_get_mixing
+
+2013-06-29 00:17:31 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-timeline.c:
+ timeline: create_transitions_on_layer *before* actually commiting
+ Everything need to be in place before commiting, otherwize it makes no
+ sense at all.
+
+2013-07-12 11:55:46 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-group.c:
+ * ges/ges-group.h:
+ group: Add an empty group constructor
+ As it is more intuitive for users.
+ API:
+ ges_group_new
+
+2013-07-10 23:33:51 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-group.c:
+ container/group/clip: Allow creating an empty group.
+ This is a legitimate use case.
+
+2013-07-10 21:24:28 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-group.c:
+ * ges/ges-timeline.c:
+ container: Add a 'recursive' argument to the get_children method
+ API:
+ - ges_container_get_children (GESContainer *container);
+ + ges_container_get_children (GESContainer *container, gboolean recurse);
+
+2013-07-11 02:16:19 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-group.c:
+ group: set priv->setting_value to TRUE when moving ourselves in _child_removed
+
+2013-07-10 23:15:17 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ * tests/check/ges/group.c:
+ timelineelement: Make sure that we will never set a negative start
+ Currently we can end up overflowing the start of others child of our
+ parent, avoid that making sure we can set our start to what was
+ requested by the user before actually doing it
+ + Add a test
+
+2013-07-09 21:30:59 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Append missing layers when moving groups between layers
+ This was a missing feature of the newly added groups
+
+2013-06-26 17:08:57 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-group.c:
+ * ges/ges-group.h:
+ * ges/ges-internal.h:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/group.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/timelineedition.c:
+ ges: Implement a GESGroup class, subclass of GESContainer
+ The GESGroup class is used to group various GESContainer
+ together, it can contain either GESClips or GESGroup or both.
+
+2013-07-07 22:40:55 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Indent formatted files
+
+2013-07-03 18:33:05 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ timeline-element: Add a method to get the topelevel parent of an element
+ API:
+ ges_timeline_element_get_toplevel_parent
+
+2013-07-03 12:48:58 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/transition.c:
+ tests: More safely check if objects where destroyed
+ Check if an object rthat has already been freed has been destroyed is not safe.
+ Add a helper function that uses weak reference to check that objects that are expected
+ to be destroyed when unrefing an object are actually destroyed.
+
+2013-07-02 19:47:48 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Emit the notify::layer signal only when actually needed
+ That means:
+ - only when we do change layer
+ - At the end of moving between two layers
+
+2013-07-02 10:56:40 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Avoid list corruption when grouping objects
+ We are currently iterating over a list that is modified in the same
+ method, we have to get a copy of the list, and iterate over the copy.
+
+2013-07-01 20:35:39 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-internal.h:
+ clip: Add an internal method to easily get the priority of the layer the clip is in
+
+2013-07-01 17:57:03 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ container: Remove the get_priority_range vmethod
+ We now let full control to subclasses so we do not need it anymore.
+
+2013-07-01 17:51:32 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ container: Let full control of children priorities to subclasses
+ For that we make the children_control_mode a protected filed, directly usable by
+ subclasses, removing the method to set it.
+ And we let the subclass set and get the priority offsets to the container class.
+
+2013-07-01 16:19:31 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Make sure that new gaps are filled before removing the old ones
+ Currently we can end up having gaps in track as the first step of the
+ gap filling method removes currently set gaps.
+
+2013-06-29 19:31:23 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * tests/check/ges/effects.c:
+ clip: Handle child priority offsets when setting priority
+
+2013-06-28 19:17:54 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: Do not allow adding an element to a container if it already has a parent
+ This should never happen, an element can have 1 and only 1 parent.
+
+2013-06-28 19:16:47 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: "Implement" the set_priority vmethod
+ This way we will just accept any value setted
+
+2013-06-28 19:15:59 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ ges: Avoid leaking the timeline when grouping containers
+
+2013-06-28 14:39:16 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ container: Let subclasses decide when height change should be computed
+ API:
+ - GESContainer.compute_height vmethod
+ + _ges_container_set_height
+
+2013-07-02 13:43:49 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ base-xml-formatter: s/ducation/duration/
+
+2013-06-28 12:56:17 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: ges_container_ungroup return a transfer full list
+
+2013-06-28 11:23:27 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * tests/benchmarks/timeline.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/timelineedition.c:
+ ges: Move ges_clip_edit to GESContainer
+ This exact same method will be needed in GESGroup, so we should have the method
+ in the common parent class.
+ API:
+ - ges_clip_edit
+ + ges_container_edit
+ + GESContainer->edit vmethod
+
+2013-06-26 19:55:37 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: Update offsets in GESTimelineElement vmethod implementations
+ So subclasses just have to link up to resync offsets
+
+2013-06-26 17:08:16 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-effect-asset.c:
+ * ges/ges-gerror.h:
+ docs: Misc documentation fixes
+
+2013-06-25 18:37:48 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Never try to set the start after the end of an element when trimming
+
+2013-06-25 18:37:17 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ internal: Add a macro to make it easier to get the end of a TimelineElement
+
+2013-06-25 18:36:24 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/layer.c:
+ track: Update gaps only when commiting
+ We were still updating them at useless moments, do it only when absolutely needed.
+
+2013-06-25 18:34:44 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: Fix the way we check priority of subclasses when grouping objects
+ The resulting list was from lower to higher, we need the contrary
+
+2013-06-26 16:54:02 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ ges: Move GESTimelineElemt vmethod implementation from container to clip
+ This is where they belong to has they are specific to that
+ implementation of the baseclass
+
+2013-06-25 18:32:49 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ container: Make initiated_move a read only protected member
+ It is a interesting information for subclasses.
+ Conflicts:
+ ges/ges-container.c
+
+2013-07-09 10:57:51 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ internal: Fix typo in the header
+ ges_base_xml_formatter_add_control_bindingi was meant to be
+ ges_base_xml_formatter_add_control_binding
+
+2013-07-01 23:33:01 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-audio-transition.c:
+ * ges/ges-track-element.h:
+ * ges/ges-video-transition.c:
+ track-element: Remove duration_changed virtual method.
+ We use notifies for the properties.
+ + Use notifies in audio-transition and video-transition
+
+2013-07-09 00:31:30 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ formatters: Save and load bindings applied to sources.
+
+2013-06-17 07:55:54 +0200 Alban Browaeys <prahal@yahoo.com>
+
+ * tools/ges-launch.c:
+ ges-launch: make it portable to all locales.
+ Call setlocale (LC_ALL, "") as per setlocale man page
+ to make ges-launch portable to all locales (instead of default
+ "C" one).
+ Fixes g_option_context_parse on:
+ $ ges-launch-1.0 --verbose -r -q /home/prahal/Vidéos/Test3.xges -o
+ file:///home/prahal/Test3.mpeg
+ Error initializing: Invalid byte sequence in conversion input
+ The accentuated character in "Vidéos" the french xdg user directory
+ for "Videos" is what is choked upon.
+ https://bugzilla.gnome.org/show_bug.cgi?id=702425
+
+2013-06-28 00:24:33 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * ges/ges-timeline-pipeline.c:
+ timelinepipeline: fix caps leak
+
+2013-06-26 12:57:17 +0000 Руслан Ижбулатов <lrn1986@gmail.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timelinepipeline: make the caps from encoding profile writable
+ https://bugzilla.gnome.org/show_bug.cgi?id=703121
+
+2013-06-23 18:27:41 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-uri-source.c:
+ urisource: Do not let user reset the URI property
+ This is not supported right now and would lead to unexpected behaviours
+
+2013-06-03 23:02:15 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/Makefile.am:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-uri-source.c:
+ * ges/ges.c:
+ * ges/gstframepositionner.c:
+ * ges/gstframepositionner.h:
+ ges: Add a framepositionner element used in ges-smart-mixer and ges-uri-source
+ It adds metadata on the buffers and the mixer parses them.
+ This is done because we want to keep positionning properties
+ and set them on the dynamic mixer pad.
+ Conflicts:
+ ges/Makefile.am
+
+2013-05-30 06:05:48 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/mixers.c:
+ tests: Add a audio/video mixing test.
+
+2013-05-30 06:04:47 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-video-track.c:
+ video-track: "implement" get_mixing_element.
+
+2013-05-29 18:48:42 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/Makefile.am:
+ * ges/ges-smart-video-mixer.c:
+ * ges/ges-smart-video-mixer.h:
+ * ges/ges-video-track.h:
+ ges-smart-mixer: first code dump, mainly copy paste from ges-smart-adder.
+
+2013-04-30 19:19:39 +0200 Simon Corsin <simoncorsin@gmail.com>
+
+ * ges/ges-layer.c:
+ ges-layer.c: notify priority changes.
+
+2013-05-16 09:40:22 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-uri-source.c:
+ ges-uri-source: Refactoring work.
+ + Categorize functions (Callbacks, vmethods)
+ + make more generic functions for the creation of the bin.
+
+2013-05-16 08:10:35 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-base-effect.c:
+ * ges/ges-uri-source.c:
+ * ges/ges-utils.c:
+ * ges/ges-utils.h:
+ uri-source: Expose the volume property.
+ + Make the pspec_hash function an internal util.
+ + Add a create_props_hashtable implementation
+ + If TRACK_TYPE_AUDIO, put the volume properties in the hashtable.
+
+2013-05-16 04:22:16 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-uri-source.c:
+ * ges/ges-uri-source.h:
+ uri-source: when creating the audio element, set the volume to the layr volume when necessary.
+
+2013-05-16 03:27:20 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-uri-source.c:
+ ges-uri-source: don't use gnlurisource but a custom bin.
+ + This bin is a uridecodebin when GES_TRACK_TYPE_VIDEO
+ + This bin contains a uridecodebin and a volume when GES_TRACK_TYPE_AUDIO
+
+2013-05-15 18:59:10 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-smart-adder.c:
+ smart-adder: remove volume from the bin, which quite simplifies the code.
+ + Don't be too smart, adder.
+
+2013-03-31 00:08:15 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/Makefile.am:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-track.h:
+ * ges/ges-smart-adder.c:
+ * ges/ges-smart-adder.h:
+ * ges/ges-track.c:
+ * tests/check/Makefile.am:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/mixers.c:
+ smart-adder: Implement a GESSmartAdder bin element to be used as mixing element
+ ..in audio tracks
+
+2013-04-22 00:21:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-layer.c:
+ * ges/ges-meta-container.h:
+ * tests/check/ges/layer.c:
+ meta-container: Add a VOLUME default meta to layers
+
+2013-03-31 12:34:58 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-custom-source-clip.c:
+ ges: Misc documentation fixes
+
+2013-03-30 19:02:52 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ track: Implement infrastructure for mixing
+
+2013-03-30 19:01:26 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-track-element.c:
+ track-element: Set a reference to the GESTrackElement on the GnlObjects using qdata
+
+2013-03-29 19:23:00 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-auto-transition.c:
+ * ges/ges-clip.c:
+ * ges/ges-internal.h:
+ * ges/ges-layer.c:
+ * ges/ges-simple-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ ges: Make space in the GESTracks to be able to add mixing elements later
+ And update the tests
+
+2013-03-29 19:04:54 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/effects.c:
+ tests:effects: Make use of normal layers, and enhance tests
+
+2013-03-29 18:56:31 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-layer.c:
+ layer: Fix some mix up in variable names
+
+2013-03-29 18:55:27 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-layer.c:
+ layer: Simplify a bit how we handle priorities
+
+2013-03-29 18:53:25 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline.c:
+ timeline-element: Make it possible to reset the timeline property to NULL
+ + Add some debug symbol
+
+2013-03-28 18:51:45 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-audio-track.c:
+ * ges/ges-audio-track.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * ges/ges-types.h:
+ * ges/ges-utils.c:
+ * ges/ges-video-track.c:
+ * ges/ges-video-track.h:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ ges: Implement GESAudioTrack and GESVideoTrack, subclasses of GESTrack
+
+2013-06-16 21:47:52 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Report position while playing back
+ Giving more feedbacks to the user
+
+2013-06-16 19:10:18 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Remove xptv formatter related code
+ It is not usefull anymore
+
+2013-06-15 22:13:20 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Properly add UriClipAssets to the project
+
+2013-06-09 12:29:05 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tools/ges-launch.c:
+ ges: Port to the new commit based API in GNL
+ The GNL API changed to go from a model where user could
+ enable/disable updates in the composition, which leaded to races
+ in many places, to a model where any positioning change in the
+ composition is not directly done but 'cached' and then the user
+ has to commit those changes so they become effective in the media
+ processing stack.
+ The new API in GES is pretty similare and is basically copy
+ pasting this new design.
+ We still need to see if in some context it would make sense to add
+ a mode where we would commit any changes ourself at the end of our
+ operation for basic use cases.
+ Removed APIs:
+ ges_timeline_enable_update
+ ges_timeline_is_updating
+ ges_track_enable_update
+ ges_track_is_updating
+ New APIs:
+ ges_track_commit
+ ges_timeline_commit
+
+2013-06-20 14:23:26 +0200 Lubosz Sarnecki <lubosz@gmail.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-project.c:
+ timeline: fix segfaults
+ don't call the timeline update, when the reference is invalid
+ https://bugzilla.gnome.org/show_bug.cgi?id=702605
+
+2013-06-18 13:32:38 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * autogen.sh:
+ * common:
+ autogen.sh: generate from common module, fixing srcdir != builddir build
+ https://bugzilla.gnome.org/show_bug.cgi?id=702424
+
+2013-06-18 13:14:48 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * gst-editing-services.doap:
+ Add .doap file
+ Needed for common/update-autogen, but generally not a bad idea.
+
+2013-04-30 19:16:10 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-uri-asset.c:
+ ges-uri-asset.c: Fix ges_uri_asset_request_sync annotations.
+
+2013-06-12 11:32:16 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-project.c:
+ project: Disable update in the project rather than the formatter
+ We need to make sure the update are disabled until the project is fully
+ loaded, let the responsability to the project instead of the formatter
+
+2013-06-12 11:09:13 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Sync track enable_update property with parent
+ When we add a track to a timeline, we want it "enable update" property
+ to be set to the timeline's
+
+2013-06-12 10:48:03 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Make use of assets for uri clips
+ It avoids races in TrackElement creations.
+ We should make use of assets everywhere in ges-launch but start using
+ them for uriclips first for now.
+
+2013-06-05 15:18:36 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 098c0d7 to 01a7a46
+
+2013-05-30 11:40:36 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/uriclip.c:
+ tests: Pass a ref of CAPS_ANY to ges_track_new
+
+2013-05-29 16:48:03 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/basic.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ tests: Add a basic test for pipeline state change
+ Add some test utils to create a pipeline
+
+2013-05-29 14:05:52 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ ges: Protect from Gst dynamic callbacks
+ The pad-added and no-more-pad signal can be emited from any thread
+ so we have to protect our code from that
+
+2013-05-23 15:52:35 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ * tests/check/ges/backgroundsource.c:
+ track: Update all gaps when timeline duration changed
+ And add a unit test to check that a gap is created in empty tracks
+
+2013-05-23 13:16:22 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-source.c:
+ * ges/ges-uri-source.h:
+ * tests/check/ges/uriclip.c:
+ Finish renaming filesource to urisource
+
+2013-05-23 11:57:42 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Update gaps when we set the timeline
+
+2013-05-28 08:51:08 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/ges-pitivi-formatter.c:
+ ges-pitivi-formatter: Remove some unneeded includes and clean up includes
+ Fixes the build on Windows, where there's no unistd.h... which wasn't
+ needed at all.
+ https://bugzilla.gnome.org/show_bug.cgi?id=701115
+
+2013-05-27 22:10:03 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/asset.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ tests: Use the gst_check_run_suite helper everywhere
+ Using GST_CHECK_MAIN where appropriate
+ This way it is possible to specify an XML file to store tests results in
+
+2013-05-15 10:55:22 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 5edcd85 to 098c0d7
+
+2013-03-05 17:09:18 -0500 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * .gitignore:
+ Update gitignore
+
+2013-03-05 17:07:29 -0500 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * ges/Makefile.am:
+ * ges/ges-asset.h:
+ * ges/ges-internal-enums.h:
+ GESAssetLoadingReturn cannot be internal
+ The enumeration is referenced in a public API.
+
+2013-05-05 11:13:24 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Avoid corruption of our list of children while ungrouping
+
+2013-04-24 15:25:20 +0300 Anton Belka <antonbelka@gmail.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-auto-transition.xges:
+ tests: add project auto-transition test
+
+2013-04-24 15:23:44 +0300 Anton Belka <antonbelka@gmail.com>
+
+ * tests/check/ges/layer.c:
+ tests: add timeline auto-transition test
+
+2013-04-24 15:18:01 +0300 Anton Belka <antonbelka@gmail.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: add auto-transition
+ API:
+ ges_timeline_get_auto_transition
+ ges_timeline_set_auto_transition
+ GESTimeline::auto-transition
+
+2013-04-30 18:26:57 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ basexmlformatter: Do not allow empty file loading
+
+2013-04-27 03:45:29 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-video-test-source.c:
+ video-test-src: Force video/x-raw
+ Avoiding to hit errors with video/x-bayer
+
+2013-04-21 21:35:22 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-audio-transition.c:
+ audiotransition: Add a resampler in the audio transition bin
+
+2013-04-21 19:21:14 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-audio-transition.c:
+ audiotransition: Fix porting error of the interpollator
+ + update debug statements s/LOG/INFO
+ (acontrolsource != bcontrolsource)
+
+2013-04-27 03:44:40 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-audio-test-source.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-video-test-source.c:
+ * tests/check/ges/backgroundsource.c:
+ test-clip: Do not set black/silent by default
+
+2013-04-24 03:50:40 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-video-transition.c:
+ replace query_caps with get_current_caps. don't check for unused gnlobject.
+
+2013-04-23 20:04:04 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * bindings/python/examples/material.py:
+ * bindings/python/examples/simple.py:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-container.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-internal.h:
+ * ges/ges-layer.c:
+ * ges/ges-layer.h:
+ * ges/ges-operation-clip.c:
+ * ges/ges-overlay-clip.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-simple-layer.c:
+ * ges/ges-simple-layer.h:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-source-clip.c:
+ * ges/ges-source-clip.h:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-clip.h:
+ * ges/ges-track-element.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-types.h:
+ * ges/ges-utils.c:
+ * ges/ges-xml-formatter.c:
+ * ges/ges.h:
+ * tests/benchmarks/timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineLayer to GESLayer
+
+2013-04-23 19:57:44 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline.c:
+ * ges/ges-xml-formatter.c:
+ * tests/benchmarks/timeline.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/test1.c:
+ layer: Remove the "rate" property of ges_timeline_layer_add_asset
+ API:
+ - ges_timeline_layer_add_asset (layer, asset, start, inpoint, duration, rate, track_types);
+ + ges_timeline_layer_add_asset (layer, asset, start, inpoint, duration, track_types);
+
+2013-04-23 22:38:23 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-element.c:
+ trackelement: asynchronously add bindings if the track-element is not in a track yet.
+ Also fix annotations.
+
+2013-04-22 17:34:09 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-pitivi-formatter.c:
+ formatter: Pass a dummy instance of formatter to virtual method
+ Instead of passing the class itself
+
+2013-04-22 23:56:03 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 3cb3d3c to 5edcd85
+
+2013-04-22 09:41:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-xml-formatter.c:
+ Fix compilation
+
+2013-04-21 21:29:29 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-xml-formatter.c:
+ xml-formatter: Use G_GUINT64_FORMAT where needed
+
+2013-04-21 21:13:00 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ uri-clip-asset: Add "_class_" to a class method
+ API CHANGE:
+ - ges_uri_clip_asset_set_timeout
+ + ges_uri_clip_asset_class_set_timeout
+
+2013-04-21 21:11:52 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ * ges/ges-xml-formatter.c:
+ ges: Fix compilation with clang
+
+2013-04-19 19:58:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ project: Accept NULL as a valid value for @id in _create_asset
+
+2013-04-18 18:41:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/clip.c:
+ tests: Check splitting a clip with several TrackElement
+
+2013-04-18 21:45:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Rework the splitting method
+ + Avoid setting clip duration of our parent ourself
+ Now each and every TrackElement inside a clip have the same
+ start/inpoint/duration
+
+2013-04-18 18:59:52 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ ges: Fix compilation
+
+2013-04-18 18:37:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ pipeline: Add API guards where needed
+
+2013-04-17 16:51:30 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * tests/examples/thumbnails.c:
+ * tools/ges-launch.c:
+ pipeline: Add a GError argument
+
+2013-04-17 16:48:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ pipeline: Return FALSE in save_thumbnail when the operation fails
+
+2013-04-15 01:30:10 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * tests/check/ges/timelineedition.c:
+ tests:timelineedition: Add a simple trimming test
+
+2013-04-14 23:19:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: The TimelineElement.inpoint property is call "in-point" not inpoint
+
+2013-03-22 19:44:28 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ ges-clip: Remove the unlocked TrackElement APIs
+ Remove APIs:
+ ges_track_element_set_locked
+ ges_track_element_is_locked
+ Those APIs where really not nice to use and were causing more issues
+ than solving them. If 2 time related properties of TimelineElement must
+ be different, then those element can *not* have the same parent.
+ Plus, with the new ges_container_group () API, we will recreate 1
+ GESClip containing the proper GESTimelineElements if it is the thing
+ to do.
+
+2013-03-22 19:34:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitivi-formatter: Remove broken code to handle unlocked track object
+ WARNING: The plan is to remove unlocked track object APIs so this is
+ the first part of that process... that code was already broken, and
+ *needs* to be fixed anyway, better do it using new APIs
+
+2013-03-22 18:43:30 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitivi-formatter: Remove saving code
+
+2013-04-14 17:58:38 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From aed87ae to 3cb3d3c
+
+2013-04-09 21:03:03 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 04c7a1e to aed87ae
+
+2013-04-09 00:02:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ timeline: call sync_state_with_parent when adding a child
+
+2013-03-31 16:07:14 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * tests/check/ges/project.c:
+ track-element: Rename set_property_controlling_parameters set_control_source
+ + Generate the documentation
+
+2013-03-30 18:54:50 +0100 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-xml-formatter.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-keyframes.xges:
+ [Keyframes] Adds API to set a control binding on a track element, and the serialization code.
+
+2013-03-30 15:40:38 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline-layer.c:
+ Changing remaining clip::track-element-added to container::child-added
+
+2013-03-30 14:35:45 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ pipeline: Implement the video overlay interface
+
+2013-03-30 13:37:43 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Do no create "normal" timeline when you load a project
+
+2013-03-30 13:34:56 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline-pipeline: Enhance API guards
+
+2013-03-30 13:34:36 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Do no unref the timeline before returning it
+
+2013-03-30 12:30:47 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ container: s/get_priorty_range/get_priority_range/
+
+2013-03-29 15:50:12 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Comment some variables goals
+
+2013-03-28 19:00:25 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/effects.c:
+ tests:effect: Do not re-add effect to the track
+
+2013-03-24 18:42:55 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * tests/check/ges/simplelayer.c:
+ container: Let subclasses handle the height
+ + Fix tests (starting using GESTestClip instead of GESCustomClip)
+ Now the height is not only growing, but can also go down, as the value
+ is just simply computed
+ API:
+ GESContainer::compute_height virtual method
+
+2013-03-23 09:46:38 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ container: Properly implement ges_container_group
+
+2013-03-23 08:48:43 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/basic.c:
+ test:basic: Do not add useless references, and minor improvements
+
+2013-03-23 08:45:00 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ clip: Emit notify signal when setting Clip.layer
+
+2013-03-23 08:14:55 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Remove TrackElement from its container on GESTimelineLayer::"clip-removed"
+ .... when the Track is NULL
+
+2013-03-23 03:27:46 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ * tests/check/ges/clip.c:
+ container: Make sure that the child exists when emiting the "child-removed" signal
+ + Add a test
+
+2013-03-23 03:26:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-internal.h:
+ container: Replace ignore_notify by a GESChildrenControlMode flag
+
+2013-03-23 01:35:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ container: Stop ignoring notifies if ->add_child fails
+
+2013-03-23 01:33:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-container.c:
+ * ges/ges-internal.h:
+ * ges/ges-utils.c:
+ internal: Add a element_end_compare
+
+2013-03-23 01:31:23 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-element.h:
+ timeline-element: Add a macro to get element 'end'
+
+2013-03-22 17:39:04 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-gerror.h:
+ * ges/ges-uri-asset.c:
+ ges: Keep ges-gerror categories simple.
+
+2013-03-21 22:17:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ * ges/ges-uri-asset.h:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/uriclip.c:
+ Misc cleaning
+
+2013-03-21 22:12:47 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-track-element.h:
+ track-element: Make ges_track_element_set_track internal
+ Removed API:
+ + ges_track_element_set_track
+
+2013-03-21 22:03:09 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.h:
+ * ges/ges-internal.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ ges: Make ges_clip_create_track_element(s) internal methods
+ + Fix tests (we still need a round of modernisation, making use of
+ assets where it makes sense)
+ There is no reason to use those method outside of GES, so remove them,
+ cleaning the API and making it easier for users.
+ Removed APIs:
+ -----------
+ * ges_clip_create_track_element
+ * ges_clip_create_track_elements
+
+2013-03-21 21:42:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/image.png:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/uriclip.c:
+ tests::uriclip: Use a real file to test still images
+ + Make use of GESAssets
+ And do proper refactoring
+
+2013-03-19 21:07:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-uri-asset.c:
+ uri-asset: Properly handle images and do not duplicate the TrackType
+
+2013-03-19 19:49:09 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * tests/check/ges/uriclip.c:
+ uri-asset: Implement a ges_uri_clip_asset_request_sync method
+ This way we let the possibility to the user to actually do it, but we avoid him to do it
+ without knowing it is absolutely not recommanded to.
+ API:
+ + ges_uri_clip_asset_request_sync
+
+2013-03-18 12:41:06 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-gerror.h:
+ * ges/ges.h:
+ Start categorizing GError types in GES
+
+2013-03-18 10:03:19 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Do not give a reference to the calles of g_object_get_property
+ ... for both the "parent" and the "timeline" properties
+ Making things simpler to handle for the copy method.
+
+2013-03-18 10:02:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * tests/check/ges/clip.c:
+ clip: Make it mandatory that a clip is in a layer to be splittable
+ Otherwize we will not be able to describe if the returned object has a floating reference or not, and this would screw the introspection.
+
+2013-03-18 09:49:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ Use gst_object_ref_sink instead of g_object_ref_sink when appropriate
+ Making refcount issue debugging simpler
+
+2013-03-16 19:05:04 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-uri-asset.c:
+ * ges/ges-utils.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/assets.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ Always prefer gst_object_(un)ref over g_object_(un)ref
+ Making the refcount issue debugging easier
+
+2013-03-15 12:01:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.h:
+ * ges/ges-internal.h:
+ clip: Make set/is_moving_from_layer internal
+
+2013-03-15 11:58:59 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.h:
+ * ges/ges-internal.h:
+ clip: Reindent header and make ges_clip_set_layer internal
+
+2013-03-15 11:32:48 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ track: Cleanup header and add a FIXME
+
+2013-03-15 00:01:47 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * tests/check/ges/basic.c:
+ Remove GESTrackElements from GESTracks when removing from a GESClip
+ ... Not the other way round.
+ + Add and enhance debugging info on the way
+ The user should not be responsible for removing the GESTrackElements from
+ GESTracks, instead, removing it from a GESClip should imply removing
+ it from any GESTrack it is in.
+ This patch changes sensibly the behaviour when we remove a
+ GESTrackElement from a GESTrack, not remoing it from the GESClip it is
+ in. *But*, users should never remove a GESTrackElement from a GESTrack
+ anyway. The testsuite has been updated to that new behaviour.
+
+2013-03-14 12:53:25 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/effects.c:
+ * tests/examples/ges-ui.c:
+ ges: Make GESTimeline responsible for adding GESTrackElement to GESTrack
+ + Fix tests as necessary (Do not use agingtv as it can be "applied" on any TrackType
+ and is not representative of what happens IRL)
+ We already had the infrastructure so the user can have the control over where to add
+ the elements (through the "select-track-for-object" signal). We now make use of that
+ signal everytime a GESClip is added to a GESTimelineLayer. This make user's life easier,
+ and object responsability clearer.
+
+2013-03-14 11:14:31 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/Makefile.am:
+ * ges/ges-effect-asset.c:
+ * ges/ges-effect-asset.h:
+ * ges/ges-effect.c:
+ Add a GESEffectAsset class, and make sure to set the GESTrackType asap on effects
+ + Make use of the asset in ges_effect_new
+
+2013-03-03 11:50:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-custom-source-clip.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-transition-clip.c:
+ ges: Use GESAsset in clip contructors when possible
+
+2013-03-03 11:16:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Set asset from the copied element to the new copy
+
+2013-03-02 18:35:34 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * tests/check/ges/clip.c:
+ container: Add a ges_container_group method
+ + Add some basic unit tests
+ API:
+ GESContainer:group vmethod
+ ges_container_group
+
+2013-03-01 22:26:01 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Do not remove a TrackElement from a NULL clip
+
+2013-03-01 20:25:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * tests/check/ges/clip.c:
+ container: Add a way to ungroup a GESContainer into several GESContainers
+ + Add simple unit test
+ API:
+ GESContainerClass::ungroup vmethod
+ ges_container_ungroup
+
+2013-03-01 22:05:45 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Make it possible to reset parent to NULL
+
+2013-03-01 19:18:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline.h:
+ Fix some documentations
+
+2013-02-28 22:27:50 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-auto-transition.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-container.c:
+ * ges/ges-container.h:
+ * ges/ges-internal.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-track.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-types.h:
+ * ges/ges-uri-clip.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/transition.c:
+ Implement GESContainer
+ + Fix unit tests
+ + Minor enhancement in unit tests
+ API changes:
+ -----------
+ * ges_track_element_get_clip -> ges_timeline_element_get_parent
+ * ges_clip_add_track_element -> ges_container_add
+ * ges_clip_release_track_element -> ges_container_remove
+ * ges_clip_get_track_elements -> ges_container_get_children
+ (or GES_CONTAINER_CHILDREN)
+
+2013-03-01 11:03:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ docs: Remove ges_clip_lock_track_elements
+ it does not exist anymore...
+
+2013-02-28 22:22:35 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-element.c:
+ timeline-element: Enhance debug statement and documentation
+ + Accept NULL as a parent
+
+2013-02-28 15:12:15 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-track.c:
+ timeline-element: Add a "timeline" property
+
+2013-02-28 18:14:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * .gitignore:
+ gitignore: Ignore *.page
+
+2013-03-14 16:09:37 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/random/rework_class_hierarchie.html:
+ docs: Add an little explanation about the class hierarchie rework
+
+2013-03-07 00:04:38 +0000 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 2de221c to 04c7a1e
+
+2013-03-06 10:27:15 +0400 Руслан Ижбулатов <lrn1986@gmail.com>
+
+ * ges/ges-timeline-element.c:
+ ges-timeline-element: Fix GST_DEBUG_OBJECT invocations
+ Fixes #695267
+
+2013-02-14 23:34:48 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-audio-transition.c:
+ * ges/ges-auto-transition.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-custom-source-clip.c:
+ * ges/ges-custom-source-clip.h:
+ * ges/ges-image-source.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-track-element.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-source.c:
+ * tests/benchmarks/timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ Rename object/tobj/trobj to clip or track_element as necessary
+ Not really complete but it is a good start!
+
+2013-02-09 21:49:16 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-effect-clip.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline.c:
+ * ges/ges-transition-clip.c:
+ Finish renaming tck_obj and derivate to track_element
+
+2013-02-08 17:25:25 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-clip.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline.c:
+ * ges/ges-xml-formatter.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineLayer.xxx_object to GESTimelineLayer.xxx_clip
+
+2013-02-08 17:23:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ track: Rename all GESTrack.xxx_object to GESTrack.xxx_element
+
+2013-02-08 17:19:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * tests/examples/ges-ui.c:
+ Properly rename object-added to clip-added
+
+2013-02-08 17:11:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-clip.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/test1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tools/ges-launch.c:
+ Finish renaming timeline object to clip
+
+2013-02-08 16:39:18 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-audio-test-source.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-custom-source-clip.h:
+ * ges/ges-image-source.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-source.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track.c:
+ * ges/ges-uri-clip.c:
+ * ges/ges.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ Finish renaming track object to track element
+
+2013-02-01 17:51:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ Add GESTimelineElement.{start, inpoint, duration, maxduration, priority} getters
+
+2013-01-28 14:36:06 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-uri-clip.c:
+ uriclip: Fix wrong acces to object instead of its duration field
+
+2013-01-27 16:21:01 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-clip.h:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ * tests/check/ges/titles.c:
+ Reword ges_title_clip_set_color to ges_title_clip_set_text_color
+
+2013-01-27 16:16:27 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-effect-clip.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-clip.h:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ Rename ges_title_.*_set_background to set_background_color
+
+2013-01-27 16:07:12 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-clip.h:
+ titleclip: Remove useless mute property
+
+2013-01-27 16:02:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitiviformatter: Fix renaming issues
+
+2013-01-27 16:02:19 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay-clip.h:
+ * tests/check/ges/overlays.c:
+ * tests/examples/overlays.c:
+ Rename overlay_text to text_overlay
+
+2013-01-27 12:51:52 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-clip-asset.c:
+ * ges/ges-clip-asset.h:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges.h:
+ Rename GESAssetClip to GESClipAsset
+
+2013-01-27 12:44:13 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-audio-transition.c:
+ * ges/ges-audio-transition.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Rename GESTrackAudioTransition to GESAudioTransition
+
+2013-01-27 12:41:51 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges-types.h:
+ * ges/ges-video-transition.c:
+ * ges/ges-video-transition.h:
+ * ges/ges.h:
+ * tests/check/ges/transition.c:
+ Rename GESTrackVideoTransition to GESVideoTransition
+
+2013-01-27 12:31:10 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-timeline.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition.c:
+ * ges/ges-transition.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Rename GESTrackTransition to GESTransition
+
+2013-01-27 12:27:19 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-effect.h:
+ * ges/ges-operation.c:
+ * ges/ges-operation.h:
+ * ges/ges-text-overlay.c:
+ * ges/ges-text-overlay.h:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Rename GESTrackOperation to GESOperation
+
+2013-01-27 12:24:44 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay.c:
+ * ges/ges-text-overlay.h:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/text_properties.c:
+ Rename GESTrackTextOverlay to GESTextOverlay
+
+2013-01-26 14:25:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-source.c:
+ * ges/ges-title-source.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/titles.c:
+ Rename GESTrackTitleSource to GESTitleSource
+
+2013-01-26 14:21:56 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-image-source.c:
+ * ges/ges-image-source.h:
+ * ges/ges-types.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges.h:
+ * tests/check/ges/uriclip.c:
+ Rename GESTrackImageSource to GESImageSource
+
+2013-01-26 14:14:57 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-asset-track-object.h:
+ * ges/ges-track-element-asset.c:
+ * ges/ges-track-element-asset.h:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-source.c:
+ * ges/ges.h:
+ Rename GESAssetTrackElement to GESTrackElementAsset
+
+2013-01-26 14:07:01 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-source.c:
+ * ges/ges-uri-source.h:
+ * ges/ges.h:
+ Rename TrackFileSource to UriSource
+
+2013-01-26 13:08:20 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * docs/working-diagrams.svg:
+ * ges/Makefile.am:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-clip.c:
+ * ges/ges-custom-source-clip.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-source.c:
+ * ges/ges-source.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-image-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-test-source.h:
+ * ges/ges.h:
+ Rename GESTrackSource to GESSource
+
+2013-01-26 13:03:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-test-clip.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-types.h:
+ * ges/ges-video-test-source.c:
+ * ges/ges-video-test-source.h:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ Rename GESTrackVideoTestSource to GESVideoTestSource
+
+2013-01-26 13:02:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-audio-test-source.c:
+ * ges/ges-audio-test-source.h:
+ * ges/ges-test-clip.c:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-types.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ Rename GESTrackAudioTestSource to GESAudioTestSource
+
+2013-01-26 12:40:51 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect.c:
+ * ges/ges-effect.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-track-parse-launch-effect.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-project.xges:
+ * tests/examples/ges-ui.c:
+ Rename GESTrackParseLaunchEffect to GESEffect
+
+2013-01-26 12:35:19 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * docs/working-diagrams.svg:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-base-effect.c:
+ * ges/ges-base-effect.h:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-parse-launch-effect.h:
+ * ges/ges-types.h:
+ * ges/ges-xml-formatter.c:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ Rename TrackEffect to BaseEffect
+
+2013-01-26 12:31:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-asset-clip.c:
+ * ges/ges-asset-track-object.c:
+ * ges/ges-asset-track-object.h:
+ * ges/ges-asset.c:
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-custom-source-clip.c:
+ * ges/ges-custom-source-clip.h:
+ * ges/ges-effect-clip.c:
+ * ges/ges-internal.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-test-clip.c:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-title-clip.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-element.c:
+ * ges/ges-track-element.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges-xml-formatter.c:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/transition.c:
+ Rename GESTrackObject to GESTrackElement
+
+2013-01-25 15:51:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ Rename GESStandardEffectClip to GESEffectClip
+
+2013-01-25 15:45:07 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-base-effect-clip.c:
+ * ges/ges-base-effect-clip.h:
+ * ges/ges-standard-effect-clip.c:
+ * ges/ges-standard-effect-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Rename GESEffectClip to GESBaseEffectClip
+
+2013-01-25 15:16:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-enums.c:
+ * ges/ges-timeline.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESStandardTransitionClip to GESTransitionClip
+
+2013-01-25 11:26:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-auto-transition.c:
+ * ges/ges-base-transition-clip.c:
+ * ges/ges-base-transition-clip.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-standard-transition-clip.c:
+ * ges/ges-standard-transition-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/examples/ges-ui.c:
+ Rename GESTransitionClip to GESBaseTransitionClip
+
+2013-01-20 12:58:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ timelineelement: Implement the notion of parenting
+
+2013-01-17 00:58:28 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-test-clip.c:
+ * ges/ges-test-clip.h:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/benchmarks/timeline.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/text_properties.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/test1.c:
+ * tests/examples/thumbnails.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineTestSource to GESTestSourceClip
+
+2013-01-17 00:55:03 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-title-clip.c:
+ * ges/ges-title-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/titles.c:
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineTileSource to GESTitleClip
+
+2013-01-17 00:53:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-text-overlay-clip.c:
+ * ges/ges-text-overlay-clip.h:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/overlays.c:
+ * tests/examples/overlays.c:
+ Rename GESTimelineTextOverlay to GESTextOverlayClip
+
+2013-01-17 00:49:43 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-overlay-clip.c:
+ * ges/ges-overlay-clip.h:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/asset.c:
+ Rename GESTimelineOverlay to GESOverlayClip
+
+2013-01-17 00:35:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-custom-source-clip.c:
+ * ges/ges-custom-source-clip.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-source-clip.c:
+ * ges/ges-source-clip.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-types.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-clip.h:
+ * ges/ges.h:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/timelineedition.c:
+ Rename GESTimelineSource to GESSourceClip
+ And GESCustomTimelineSource to GESCustomSourceClip
+
+2013-01-17 00:26:35 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect-clip.h:
+ * ges/ges-operation-clip.c:
+ * ges/ges-operation-clip.h:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Rename GESTimelineOperation to GESOperationClip
+
+2013-01-17 00:04:41 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-standard-effect-clip.c:
+ * ges/ges-standard-effect-clip.h:
+ * ges/ges-timeline-parse-launch-effect.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ Rename GESTimelineParseLaunchEffect to GESStandardEffectClip
+
+2013-01-16 23:21:01 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-enums.c:
+ * ges/ges-meta-container.c:
+ * ges/ges-standard-transition-clip.c:
+ * ges/ges-standard-transition-clip.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-standard-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineStandardTransition to GESStandardTransitionClip
+
+2013-01-16 23:16:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-meta-container.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-standard-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-transition-clip.c:
+ * ges/ges-transition-clip.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/examples/ges-ui.c:
+ Rename GESTimelineTransition to GESTransitionClip
+
+2013-01-16 23:11:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-effect-clip.c:
+ * ges/ges-effect-clip.h:
+ * ges/ges-meta-container.c:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-timeline-parse-launch-effect.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ Rename GESTimelineEffect to GESEffectClip
+
+2013-01-20 12:44:57 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * bindings/python/examples/material.py:
+ * bindings/python/examples/simple.py:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-asset-clip.c:
+ * ges/ges-asset.c:
+ * ges/ges-extractable.c:
+ * ges/ges-meta-container.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-types.h:
+ * ges/ges-uri-asset.c:
+ * ges/ges-uri-asset.h:
+ * ges/ges-uri-clip.c:
+ * ges/ges-uri-clip.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/asset.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-project.xges:
+ * tests/check/ges/uriclip.c:
+ * tests/examples/assets.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineFileSource to GESUriClip
+ Conflicts:
+ ges/ges-pitivi-formatter.c
+ ges/ges-uri-clip.c
+ tests/check/ges/project.c
+ tests/check/ges/uriclip.c
+
+2013-01-20 12:42:29 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * android/ges.mk:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-asset-clip.c:
+ * ges/ges-asset-clip.h:
+ * ges/ges-asset-file-source.c:
+ * ges/ges-asset-file-source.h:
+ * ges/ges-asset-track-object.c:
+ * ges/ges-asset.c:
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-clip.c:
+ * ges/ges-clip.h:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-internal.h:
+ * ges/ges-meta-container.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-operation.c:
+ * ges/ges-timeline-operation.h:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ * ges/ges-types.h:
+ * ges/ges-xml-formatter.c:
+ * ges/ges.h:
+ * tests/benchmarks/timeline.c:
+ * tests/check/Makefile.am:
+ * tests/check/ges/.gitignore:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/clip.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/project.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/test-project.xges:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Rename GESTimelineObject to GESClip
+
+2013-01-15 10:52:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-auto-transition.c:
+ * ges/ges-internal.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-element.c:
+ * ges/ges-timeline-element.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ * ges/ges-types.h:
+ * ges/ges-utils.c:
+ * ges/ges-xml-formatter.c:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/ges-ui.c:
+ Add a GESTimelineElement base class
+ + Port GESTrackObject and GESTimelineObject to the new baseclass
+
+2013-02-10 12:07:48 -0500 Jean-François Fortin Tam <nekohayo@gmail.com>
+
+ * docs/libs/architecture.xml:
+ docs: Clarify the distinction between Tracks and Layers
+
+2013-01-30 20:12:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-project.c:
+ project: Update loading asset when a URI is missing
+
+2013-01-30 01:27:17 -0800 Kerrick Staley <mail@kerrickstaley.com>
+
+ * configure.ac:
+ build: replace AM_CONFIG_HEADER with AC_CONFIG_HEADERS to fix build with automake 1.13
+ AM_CONFIG_HEADER is deprecated; see
+ https://lists.gnu.org/archive/html/automake/2012-12/msg00038.html
+ https://bugzilla.gnome.org/show_bug.cgi?id=692864
+
+2013-01-28 20:46:06 +0100 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From a942293 to 2de221c
+
+2013-01-22 18:44:00 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ configure: Properly check if PyGObject is present
+ And make use of the PyGObject overrides if present
+
+2013-01-22 18:08:44 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ Bump Glib dependency to 2.34
+ We use new APIs (g_list_copy_deep) that appeared in GLib 2.34
+
+2013-01-22 19:51:25 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ * tests/check/ges/simplelayer.c:
+ Fix various printf format issues in debug messages
+
+2013-01-15 15:09:39 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From a72faea to a942293
+
+2013-01-14 09:01:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ Bump GStreamer dependency version to current master (1.1.0)
+
+2013-01-12 20:49:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitiviformatter: Handle project metadatas
+
+2013-01-12 10:50:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.h:
+ * ges/ges-xml-formatter.c:
+ xmlformatter: Do no allow saving CONSTRUCTONLY properties
+
+2013-01-11 19:10:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ layer: State assets we create async as loading to the project
+
+2013-01-11 19:07:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ * tests/check/ges/project.c:
+ project: Track Asset that were loaded with error
+
+2013-01-11 11:49:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset.c:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-internal.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ * ges/ges-timeline-layer.c:
+ * tests/check/ges/project.c:
+ project: Handle assets that are being loaded
+ API:
+ ges_project_get_loading_assets
+
+2013-01-11 15:26:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline.h:
+ ges: Documentations fixes
+
+2013-01-10 18:50:54 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/Makefile.am:
+ * ges/ges-auto-transition.c:
+ * ges/ges-auto-transition.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/layer.c:
+ Reimplement the auto-transition feature
+ + Actually implement unit tests
+
+2013-01-10 18:09:23 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Make the GESTrack (parent track) a GObject property
+
+2013-01-10 18:01:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Minor refactoring
+
+2013-01-10 13:32:15 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Avoid recreating the moving_tlobjs when unecessary
+
+2013-01-10 12:41:13 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ Misc debug message enhancements
+
+2013-01-10 12:24:20 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Keep track of whether updates are enabled or not
+ Check if we want to track Track-s enable status and update our status according
+ to that
+
+2013-01-10 11:58:59 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ layer: Properly emit the notify signal when auto_transition changes
+
+2013-01-10 11:39:46 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Track TrackObject-s by layer
+
+2013-01-10 11:18:46 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/test-utils.h:
+ tests: Add a macro for type checking
+
+2013-01-10 11:15:32 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ * ges/ges-utils.c:
+ utilities: Make internal utilities instead of copy/pasting functions
+
+2013-01-10 11:01:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * .gitignore:
+ gitignore: Ignore anjuta files
+
+2013-01-05 12:02:03 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ * tests/Makefile.am:
+ * tests/benchmarks/Makefile.am:
+ * tests/benchmarks/timeline.c:
+ Benchmark rippling
+
+2013-01-04 13:11:51 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-timeline-layer.c:
+ Move LAYER_HEIGHT definition from -timeline-layer.c c to -internal.h
+
+2013-01-04 13:04:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset-track-object.c:
+ asset-track-object: Minor doc fixing
+
+2013-01-03 11:43:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Remove dead macros
+
+2013-01-03 11:41:48 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Use g_sequence_sort_changed when appropriate
+
+2013-01-03 10:34:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Keep in cache the GSequenceIter so we get a faster acces to them
+
+2012-12-30 22:37:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Plug some leaks
+
+2012-12-29 19:36:07 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/project.c:
+ test: project: Fix various leaks
+
+2012-12-29 19:34:29 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-xml-formatter.c:
+ xmlformatter: Plug various leaks
+
+2012-12-29 18:24:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ Misc nitpick fixing
+
+2012-12-29 18:04:25 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ project: Plug various leaks.
+
+2012-12-29 18:02:35 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-meta-container.c:
+ meta-container: Plug various leaks
+
+2012-12-29 17:58:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ asset: Do not allow proxying over the same currently proxied asset
+
+2012-12-29 17:54:51 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ asset: Fix some leaks
+
+2012-12-29 17:52:42 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-track-parse-launch-effect.c:
+ asset: Avoid leaking the GParameter array and content
+
+2012-12-29 14:10:11 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset-file-source.c:
+ assetfilesource: Fix some leaks
+
+2012-12-29 14:09:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/examples/concatenate.c:
+ tests: Remove useless mutex
+
+2012-12-29 14:08:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * .gitignore:
+ Add some more gitignore
+
+2012-12-28 19:10:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-xml-formatter.c:
+ Refrase formatters descriptions
+
+2012-12-28 19:06:30 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: First check the extension when checking if can load URI
+
+2012-12-28 11:40:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-project.c:
+ project: Run the vmethod in first stage for the "loaded" signal
+ This is most probably what sublcasses will need
+
+2012-12-24 09:29:48 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-screenshot.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ tests: Fix misc issues
+ Now GST_CAPS_ANY is a singleton, it is not returning a newly created caps
+ anymore
+
+2012-12-24 09:29:04 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/Makefile.am:
+ * ges/Makefile.am:
+ * tests/check/Makefile.am:
+ Allow checking code coverage
+
+2012-12-21 20:17:41 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset-file-source.c:
+ * ges/ges-asset-timeline-object.c:
+ * ges/ges-asset-track-object.c:
+ * ges/ges-asset.c:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-extractable.c:
+ * ges/ges-project.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-operation.c:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-object.c:
+ Misc documentation fixes
+ Using "#" a in short_description screws the display
+
+2012-12-21 20:48:03 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-asset-file-source.c:
+ * ges/ges-asset-file-source.h:
+ * ges/ges-asset-track-object.c:
+ * ges/ges-asset-track-object.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Implement a GESAssetTrackObject class
+ + Addapt the rest of the code to make use of it
+
+2012-12-21 18:51:26 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-asset-file-source.c:
+ * ges/ges-asset-file-source.h:
+ * ges/ges-asset-timeline-object.c:
+ * ges/ges-asset-timeline-object.h:
+ * ges/ges-meta-container.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Implement a GESAssetTimelineObject class
+ + Make GESAssetFileSource a subclass of it
+ + Remove ges_asset_filesource_get_supported_type as it is now in GESAssetTimelineObject
+ + Remove the GES_META_TIMELINE_OBJECT_SUPPORTED_FORMATS as it is useless now
+
+2012-12-21 14:28:16 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-asset-file-source.c:
+ * ges/ges-asset-file-source.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-types.h:
+ Add a GESAssetTrackFileSource class and make use of it all around
+
+2012-12-20 20:23:54 -0300 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ Allow applications to select to which track a track object should be added
+ Modifies some API:
+ ges_timeline_object_create_track_objects now take a GESTrackType instead of a
+ GESTrack as second argument, and return a GList instead of a boolean
+ ges_timeline_object_create_track_object now take a GESTrackType instead of a
+ GESTrack as second argument
+
+2012-12-20 20:21:51 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ Add a utility method to get the name of a GESTrackType
+ API:
+ ges_track_type_name
+
+2012-12-20 14:58:35 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timelinepipeline: Properly reset #GESTrack caps when switching back to playback
+
+2012-12-20 11:28:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Properly popullate the tracks field with GESTrack-s
+ + add priv_tracks private field that contained TrackPrivate structures
+ We now have 2 list containing our tracks, one with TrackPrivate structures, and one the
+ GESTrack-s themselves.
+
+2012-12-21 10:43:41 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * .gitignore:
+ Add some ignored files
+
+2012-12-20 10:17:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ Revert "Revert "ges: timeline-pipeline: Remove playsink send_event hack""
+ This reverts commit 094669391ddf8a29b3a1d1168a78cc50c20341b4.
+ Conflicts:
+ ges/ges-timeline-pipeline.c
+
+2012-12-17 22:35:28 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-enums.h:
+ * ges/ges-extractable.h:
+ * ges/ges-formatter.h:
+ * ges/ges-meta-container.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-types.h:
+ Misc documentation fixing
+
+2012-12-05 08:51:48 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Rework the _save_to_uri method to give more debug information
+
+2012-12-17 17:06:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Handle project when adding a GESTimelineObject directly
+
+2012-11-29 17:07:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Implement the Extractable type
+ We can imagine user implemts more Layer type, it could be usefull for formatters
+ to instanciate through a GESMaterial
+
+2012-11-27 13:54:54 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.h:
+ timeline-object: Reindent header file
+
+2012-11-27 13:53:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges.c:
+ docs: Minor documentation fixes
+
+2012-11-27 13:52:59 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ internale: Add the G_GNUC_INTERNAL attribute to all internal methods
+
+2012-09-09 21:25:54 -0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * tests/check/ges/asset.c:
+ tests: Add testcase for GESAsset
+
+2012-11-27 13:52:20 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-asset.c:
+ * ges/ges-enums.c:
+ * ges/ges-timeline-transition.h:
+ ges: Create assets for all GESTimelineStandardTransition on ges_init()
+ + Add some testsuite
+
+2012-11-27 12:53:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges.c:
+ ges: Make sure not to initialize twice
+
+2012-11-27 12:18:27 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ timelineobject: Add a method to add a GESAsset
+ + Avoid to assume function arguments are correct before actually testing
+ them in ges_timeline_object_add_track_object
+ API: ges_timeline_object_add_asset
+
+2012-11-26 17:27:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-standard-transition.c:
+ timeline-standard-transition: Override the GESExtractable implementation
+ Standard transition material have the vtype property as ID, it has the particularity
+ that the ID can be changed at runtime
+ + Implement tests to make sure it behaves properly
+
+2012-11-26 17:24:43 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ timelinefilesource: Remove deprectated methods
+ Removed API:
+ ges_timeline_filesource_get_supported_formats
+
+2012-11-24 00:09:28 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ timeline: Make use of the Project API for timeline saving
+ API:
+ * Add a formatter_type paramatter to ges_timeline_save_to_uri
+
+2012-09-23 02:13:38 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/project.c:
+ * tests/check/ges/test-project.xges:
+ * tests/check/ges/test.xptv:
+ tests: Add GESProject tests
+
+2012-11-19 13:24:03 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-base-xml-formatter.c:
+ * ges/ges-base-xml-formatter.h:
+ * ges/ges-internal.h:
+ * ges/ges-xml-formatter.c:
+ * ges/ges-xml-formatter.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ xml-formatter: Implement a GESXmlFormatter
+
+2012-11-18 20:20:47 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Set the gap element creator function when tracks are using raw audio/video
+
+2012-11-18 20:19:01 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ pipeline: Minor documentation fixes
+
+2012-11-18 12:46:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * tests/check/ges/layer.c:
+ Check in TimelineObject what tracks are supported before creating TrackObject-s
+ We used to do it in TimelineFileSource which does not make sense.
+ At the same time we set AUDIO | VIDEO as default supported types as it is more
+ likely to be what subclasses support. If it is not the case, they need to
+ specify it as shown in ges-timeline-custom-timeline-source.c
+ + Fix the tests accordingly
+
+2012-11-20 18:23:59 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Implement the GESMetaContainerInterface
+
+2012-09-23 02:11:46 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Implement the GESExtractable interface
+
+2012-11-11 13:51:45 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Make timeline->track and timeline->layers public fields
+
+2012-12-17 19:26:23 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ formatter: Make it work with GESProject
+ + Compile new GESProject code
+ The formatter and projects should work together, and the user will in the end not need
+ the GESFormatter API in most cases. Start making that happening
+ Update the GESPitiviFormatter to the new behaviour and remove APIs that became
+ obselete
+ API:
+ + Adds:
+ * Pass the GESFormatterClass to can_load/save_uri vmethods
+ * Add an @overwrite argumenent to ges_formatter_save_to_uri and the
+ corresponding vmethod
+ * Add name, description, extension, mimetype, version, rank metadatas
+ to GESFormatterClass
+ + Removes:
+ * ges_pitivi_formatter_set_sources:
+ * ges_pitivi_formatter_get_sources:
+
+2012-09-24 22:24:42 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ formatter: Implement the GESExtractable interface
+ Make it a GInitially unowned, GESProject will become the owner
+
+2012-09-21 15:48:56 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/audio_only.ogg:
+ * tests/check/ges/audio_video.ogg:
+ * tests/check/ges/filesource.c:
+ tests: First filesource test port to assets
+
+2012-09-20 12:16:38 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/examples/test1.c:
+ Examples: Use GESTimelineTestSource instead of GESCustomTimelineSource in test1
+
+2012-09-02 15:14:27 +0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-internal.h:
+ * ges/ges-project.c:
+ * ges/ges-project.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ project: Implement GESProject
+ Do not build yet, waiting for everythnig to be in place before doing so
+ Co-Authored-By: Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+2012-11-21 10:22:41 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Implement the GESMetaContainer interface
+
+2012-11-20 18:25:31 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Implement the GESMetaContainerInterface
+
+2012-11-20 00:29:23 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timeline-object: Implement the GESMetadataContainer interface
+
+2012-11-19 23:42:47 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-parse-launch-effect.c:
+ trackobject: Implement the GESExtractable interface
+
+2012-09-19 22:36:38 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-test-source.c:
+ docs: Update GESTimelineTestSource documentation
+
+2012-09-18 14:42:58 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ customtimelinesource: Override Extractable
+ API: ges_material_custom_timeline_source_new (helper method)
+
+2012-09-18 14:40:51 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/examples/material.py:
+ * bindings/python/examples/simple.py:
+ bindings: Add simple python examples
+
+2012-09-14 01:05:45 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * Makefile.am:
+ * bindings/Makefile.am:
+ * bindings/python/Makefile.am:
+ * bindings/python/examples/Makefile.am:
+ * bindings/python/gi/Makefile.am:
+ * bindings/python/gi/__init__.py:
+ * bindings/python/gi/overrides/GES.py:
+ * bindings/python/gi/overrides/Makefile.am:
+ * bindings/python/gi/overrides/__init__.py:
+ * configure.ac:
+ bindings: Start implementing overrides for python
+
+2012-09-09 21:26:49 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/examples/concatenate.c:
+ example: Port the concatenate example to assets
+
+2012-09-09 21:26:15 -0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/assets.c:
+ examples: Add basic examples of asset
+
+2012-12-17 17:05:56 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline.c:
+ ges: Implement the ges_timeline_layer_add_asset method
+ + Remove GstDiscoverer related code in GESTimeline as we do not need it anymore
+ + Refactor the ges_timeline_layer_add_object method to make sure it is still working as intended
+ API:
+ ges_timeline_layer_add_asset
+
+2012-09-09 21:21:21 -0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * ges/ges-timeline-file-source.c:
+ timelinefilesource: Override default GESExtractable interface implementation
+
+2012-09-09 21:20:46 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ timelineobject: Implement the GESExtractable interface
+
+2012-09-09 21:15:17 -0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-asset-file-source.c:
+ * ges/ges-asset-file-source.h:
+ * ges/ges-asset.c:
+ * ges/ges-internal.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ ges: Implement GESAssetFileSource
+ + Generate the documentation
+ + Make the new Asset infrastructure compile
+ Co-Authored-By: Thibault Saunier <thibault.saunier@collabora.com>
+
+2012-09-09 21:12:06 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-extractable.c:
+ * ges/ges-extractable.h:
+ * ges/ges-internal.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ ges: Implement the GESExtractable interface
+ + Generate the documentation
+ Note: Do not compile (add to Makefile.am) for now as we are missing pieces at that point
+ Co-Authored-By: Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+2012-08-31 19:36:37 -0700 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-asset.c:
+ * ges/ges-asset.h:
+ * ges/ges-internal-enums.h:
+ * ges/ges-internal.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ ges: Implement GESAsset
+ + Generate the documentation
+ Note: Do not compile (add to Makefile.am) for now as we are missing pieces at that point
+ Co-Authored-By: Thibault Saunier <thibault.saunier@collabora.com>
+
+2012-12-17 15:27:52 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/design/asset.txt:
+ design: Add asset design document
+ Co-Authored-By: Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+2012-08-10 12:58:13 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ filesource: Make the uri property CONSTRUCT_ONLY
+ This is the way it should always have been.
+
+2012-07-21 17:12:08 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * .gitignore:
+ gitignore: Ignore some more files
+
+2012-12-17 15:17:50 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/layer.c:
+ tests: implement GESMetaContainer tests
+ Co-Authored-By: Paul Lange <palango@gmx.de>
+
+2012-12-17 15:24:52 -0300 Paul Lange <palango@gmx.de>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: implement the GESMetaContainer interface
+
+2012-12-17 15:23:39 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-meta-container.c:
+ * ges/ges-meta-container.h:
+ * ges/ges.c:
+ metacontainer: Finnish GESMetaContainer implementation
+
+2012-11-26 13:31:17 -0300 Paul Lange <palango@gmx.de>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-meta-container.c:
+ * ges/ges-meta-container.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ implement the GESMetaContainer interface
+
+2012-05-14 22:14:37 +0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/design/metadata.txt:
+ docs: Added metadata design doc
+
+2012-11-23 11:44:08 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Fix track-added Callback parametters
+
+2012-12-01 13:56:37 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/test-utils.h:
+ test-utils: Add some more utilities
+
+2012-11-18 20:23:13 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/test-utils.c:
+ Minor fixes to the test utils
+
+2012-12-01 13:51:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timeline-object: Edit can only work work with GESTrackSource
+
+2012-11-23 23:52:32 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-pitivi-formatter.c:
+ formatter: Remove obselete APIs
+ Removed APIs:
+ ges_formatter_update_source_uri
+ GESFormatter::source-moved
+ ges_formatter_update_source_uri
+ ges_formatter_load
+ ges_formatter_save
+ ges_formatter_set_data
+ ges_formatter_clear_data
+ ges_formatter_get_data
+ GESFormatterLoadMethod
+ GESFormatterSaveMethod
+ This is now GESProject's role
+
+2012-11-23 23:51:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-keyfile-formatter.h:
+ * ges/ges-timeline.c:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/save_and_load.c:
+ * tests/examples/ges-ui.c:
+ Remove the GESKeyFileFormatter
+ It was using deprecated URI, and can not be used in real life anymore.
+ Also remove the ges_formatter_default_new method ges_formatter_new_for_uri
+ that are useless now
+
+2012-11-19 14:19:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitiviformatter: Some minor restrtucturation
+
+2012-12-01 13:53:06 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Force video/x-raw in raw gaps
+
+2012-12-17 12:27:54 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Keep track of GSequenceIter for each GESTrackObject
+ This way we do not have to look for them in the sequence itself, and
+ make things simpler
+
+2012-12-17 13:51:49 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ timelineobject: Give a direct access to the list of TrackObject
+ Avoid to have to copy the list each time we want to access it
+
+2012-12-19 10:37:02 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/design/effects.txt:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/effects.c:
+ trackobject: Properly rename get/set_child_property
+ We used to have a ges_track_object_get/set_child_property that was in
+ fact letting user set/get various properties at once, rename it to
+ get/set_properties, and implement:
+ API:
+ ges_track_object_get_child_property (GESTrackObject *object, const gchar
+ *property_name, GValue * value);
+ ges_track_object_set_child_property (GESTrackObject *object, const gchar
+ *property_name, GValue * value);
+
+2012-12-18 19:47:50 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Some GI annotation fix
+
+2012-11-25 16:11:17 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/Makefile.am:
+ docs: Add make upload
+
+2012-11-19 11:31:33 +0000 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 6bb6951 to a72faea
+
+2012-11-17 00:10:20 +0000 Tim-Philipp Müller <tim@centricular.net>
+
+ * ges/ges-track-effect.c:
+ ges-track-effect: don't use deprecated API
+
+2012-11-04 00:25:20 +0000 Tim-Philipp Müller <tim@centricular.net>
+
+ * COPYING:
+ * COPYING.LIB:
+ * docs/design/gstencodebin.h:
+ * docs/design/gstprofile.h:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-internal.h:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-keyfile-formatter.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-screenshot.c:
+ * ges/ges-screenshot.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-effect.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-operation.c:
+ * ges/ges-timeline-operation.h:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-timeline-parse-launch-effect.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-standard-transition.h:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-effect.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-image-source.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-parse-launch-effect.h:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * ges/ges-types.h:
+ * ges/ges-utils.c:
+ * ges/ges-utils.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/effects.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineedition.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Fix FSF address
+
+2012-10-31 14:49:44 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/Makefile.am:
+ ges: fix g-i search path for GstAudio GstVideo GstTag and GstBase
+
+2011-12-23 14:07:21 +0100 Xabier Rodriguez Calvar <xrcalvar@igalia.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline: Added timeline and mode as properties
+
+2011-11-11 17:29:20 +0100 Xabier Rodriguez Calvar <xrcalvar@igalia.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline: Adding GObject property API to get/set preview audio and video sinks
+
+2011-11-11 17:09:34 +0100 Xabier Rodriguez Calvar <xrcalvar@igalia.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline: Initialize as NULL the preview sinks when getting them.
+ This way, if there is a problem getting the properties from the
+ playsink, we do not return garbage.
+
+2012-10-06 15:02:54 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 6c0b52c to 6bb6951
+
+2012-09-25 15:07:17 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges.h:
+ ges: Update reported version to 1.0
+
+2012-07-20 14:11:56 +0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ timeline: Added 'const' modifier
+
+2012-09-09 21:27:08 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-title-source.c:
+ titlesource: Use GST_DEBUG_OBJECT when appropriate
+
+2012-09-22 18:51:46 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.h:
+ * ges/ges-timeline-object.h:
+ * ges/ges-track-object.h:
+ * ges/ges-types.h:
+ Reset ABI for 1.0 and ensure that extensible baseclasses are extensible enough
+
+2012-09-22 13:10:55 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/test-utils.h:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ tests: Move common code to test-utils
+
+2012-09-23 02:24:14 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/test.xptv:
+ * tests/check/ges/wrong_test.xptv:
+ tests: Implement tests for ges_formatter_can_load_uri
+
+2012-09-23 02:23:20 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/test-utils.c:
+ * tests/check/ges/test-utils.h:
+ tests: Add some utils for test writing
+
+2012-09-23 02:07:04 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitiviformatter: Implement can_load_uri vmethod
+
+2012-09-23 02:06:44 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Implement a usefull version of ges_formatter_can_load_uri
+
+2012-09-23 02:05:42 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges.c:
+ * tests/check/ges/save_and_load.c:
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ formatter: Add GError everywhere needed in the API
+ We should give as much information as possible to the user when serialization/deserialization doesn't work.
+
+2012-09-22 13:27:20 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Plug a leak in the movecontext code
+
+2012-09-22 16:12:05 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 4f962f7 to 6c0b52c
+
+2012-09-07 12:58:19 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline-pipeline: use downstream block probe to avoid deadlock on duration query
+
+2012-09-06 16:58:21 -0400 Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline-pipeline: Clean the blocked pad
+ We do need to cleanup the pad now, otherwise the probe will get remove a
+ second time in pad_removed_cb causing an assertion.
+
+2012-08-26 15:35:01 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/titles.c:
+ tests: Fix tests
+ In TimelineTitleSource We do not add a TrackAudioTestSource in the audio track
+ anymore as it was a hack to work around the fact that we used not to have gap
+ support, now we do, remove related tests
+
+2012-08-16 11:20:44 +0100 Matas Brazdeikis <matas@brazdeikis.lt>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ title-source: add background color option
+
+2012-08-13 16:00:28 +0100 Matas Brazdeikis <matas@brazdeikis.lt>
+
+ * ges/ges-timeline-title-source.c:
+ timeline-title-source: remove audio-test-source
+
+2012-08-22 13:35:27 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 668acee to 4f962f7
+
+2012-08-10 12:39:10 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track.c:
+ Misc documentation fixing
+
+2012-08-14 20:33:57 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ GI: Fix some annotations in TrackObject
+
+2012-08-09 10:14:57 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/thumbnails.c:
+ examples: Use GRegex instead of POSIX regex
+ They are not available on Windows.
+
+2012-08-05 16:44:22 +0100 Tim-Philipp Müller <tim@centricular.net>
+
+ * common:
+ Automatic update of common submodule
+ From 94ccf4c to 668acee
+
+2012-07-28 21:45:03 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-object.c:
+ * ges/ges-utils.c:
+ ges: Remove useless and error prone 'transfer full' annotations
+ transfer full is default and there is currently a bug in GES when constructor
+ are declared as transfer full
+
+2012-07-23 08:48:43 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 98e386f to 94ccf4c
+
+2012-07-01 20:54:42 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge remote-tracking branch 'origin/0.10'
+ Conflicts:
+ common
+
+2012-07-01 20:03:37 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add the new ges_formatter_emit_loaded API to the docs
+ + Fix sections
+
+2012-07-01 19:57:30 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-pitivi-formatter.c:
+ formatter: Make the emit_loaded a real method and not a virtual method
+ + Modify formatter subclasses accordingly
+ API:ges_formatter_emit_loaded
+ This API wasn't released so it could still be changed
+
+2012-07-01 19:39:57 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitivi-formatter: Make use of the Formatter:timeline protected field
+
+2012-07-01 19:34:53 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ formatter: Add a timeline protected field
+
+2012-04-08 00:30:03 +0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * ges/ges-pitivi-formatter.c:
+ ges-pitivi-formatter: Fixed loading of projects with empty timeline
+ Because 'project-loaded' signal was triggered from track object loading
+ callback in case with projects that have empty timeline this signal was
+ never emitted.
+
+2012-04-08 00:08:43 +0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * ges/ges-formatter.c:
+ ges-formatter: Removed assert to allow saving projects with empty timeline
+ Removed assert in ges-formatter.c to allow saving projects with empty timeline.
+
+2012-06-25 10:32:36 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ gestimelinepipeline: Fix for gst_element_make_from_uri() API changes
+
+2012-06-08 15:07:15 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 03a0e57 to 98e386f
+
+2012-06-08 14:27:34 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From b811047 to 3baf58a
+
+2012-06-06 18:20:59 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 1fab359 to 03a0e57
+
+2012-06-06 18:20:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From b098abb to b811047
+
+2012-06-01 10:31:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From f1b5a96 to 1fab359
+
+2012-06-01 10:23:17 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 96f075b to b098abb
+
+2012-05-31 13:12:01 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 92b7266 to f1b5a96
+
+2012-05-30 13:41:17 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ examples: Fix linking by passing -export-dynamic in the right variable
+
+2012-05-30 12:49:02 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From ec1c4a8 to 92b7266
+
+2012-05-30 12:42:18 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 1e6c5ea to 96f075b
+
+2012-05-30 12:33:40 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From ff4cad1 to 1e6c5ea
+
+2012-05-30 11:27:44 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 3429ba6 to ec1c4a8
+
+2012-05-30 11:27:43 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 11f0cd5 to ff4cad1
+
+2012-05-30 11:24:29 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * configure.ac:
+ configure: Don't check for OBJC compiler
+
+2012-05-30 11:24:29 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * configure.ac:
+ configure: Don't check for OBJC compiler
+
+2012-05-27 22:55:12 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Make sure to remove the proper TrackObject from the GSequence
+
+2012-05-27 22:55:12 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Make sure to remove the proper TrackObject from the GSequence
+
+2012-05-26 17:41:43 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/backgroundsource.c:
+ tests: Fix backgroundsource test
+
+2012-05-26 17:00:50 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge remote-tracking branch 'origin/0.10'
+ Conflicts:
+ bindings/python/ges.defs
+ ges/ges-screenshot.c
+ ges/ges-track-video-transition.c
+
+2012-05-21 19:38:10 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Do not use meaningless offset values when snapping
+
+2012-05-21 18:10:29 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Disable updates when loading a project
+
+2012-05-21 12:45:00 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.c:
+ videotransition: Some explanations about the invert property
+
+2012-05-21 13:05:53 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.c:
+ videotransition: Misc cleanup in the smpte/crossfade transition type switches
+
+2012-05-21 13:05:14 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.c:
+ videotransition: Do not wait pad to be blocked before switching transitions
+ ... from smpte to crossfad and the other way around
+ This avoid useless async operations
+
+2012-05-18 13:17:17 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Move all callbacks to the callback section of the file
+
+2012-05-18 13:16:50 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Make the update property a GObject property
+ API: timeline::update property
+
+2012-05-18 11:13:11 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Try to resnap at same snapping point before calculating new value
+
+2012-05-18 10:33:44 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Avoid to recalculate the moving context unecessarly
+
+2012-05-18 10:28:26 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Create a debug logging category for the timeline
+
+2012-05-16 15:53:07 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Properly set TimelineFileSource-s duration and max duration
+ When we get the information of duration of files after discoverying them,
+ use that information to set the values on the TimelineFileSource-s
+
+2012-05-15 14:38:38 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Rework the way we calculate in which layer a TrackObject is
+
+2012-05-09 12:12:38 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-screenshot.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-video-transition.c:
+ docs: Misc documentation fixing
+
+2012-05-09 11:51:33 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/backgroundsource.c:
+ tests: Add basic gaps tests
+
+2012-05-09 11:45:02 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ track: Properly fill gaps
+ API: GESCreateElementForGapFunc Virtual method type
+ API: ges_track_set_create_element_for_gap_func
+
+2012-05-09 11:20:24 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Restructurate file so we have private method and API properly separeted
+
+2012-05-16 12:23:52 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ track: Use a GSequence to keep the sorted list of TrackObject-s
+ Use a GSequence instead of a GList to optimise the process.
+ Conflicts:
+ ges/ges-track.c
+
+2012-05-16 12:59:33 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Make use of our own knowledge of the timeline duration
+ Do not use each Track durations as it end going in loop as we have the Tracks
+ that need to know about timeline's duration to create or not gaps in the end and
+ then the timeline references on Tracks duration for its duration. We have this
+ information locally so just make proper use of it.
+
+2012-05-17 20:49:01 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Add a method to get the timeline duration
+ + Bind it in python
+ API: ges_timeline_get_duration
+
+2012-05-13 15:59:21 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From dc70203 to 3429ba6
+
+2012-05-10 14:56:34 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge remote-tracking branch 'origin/0.10'
+
+2012-05-10 12:40:23 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track.c:
+ track-object: Keep a reference to our gnlobject
+ Avoid refering to an object that doesn't exists and segfault in some cases.
+ We do not need to increase the reference to the gnlobj when the trackobject
+ is removed from a track because the TrackObject as its own reference and will
+ handle the disposal gracefully.
+ Add some guard around related APIs
+
+2012-05-08 19:34:48 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Avoid segfault when debug logging
+
+2012-05-07 16:11:26 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ ges: fix printf arguments in debug message
+ https://bugzilla.gnome.org/show_bug.cgi?id=675547
+
+2012-05-06 18:52:25 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: Make the invert property management coherent
+
+2012-05-06 04:52:40 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: Fix set_inverted
+
+2012-05-05 13:00:49 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge remote-tracking branch 'origin/0.10'
+ Conflicts:
+ bindings/python/ges.defs
+ ges/ges-track-video-transition.c
+
+2012-05-05 12:31:28 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ track-object: Set minimum value of max-duration to 0
+ GST_CLOCK_TIME_NONE was nonsense
+ Minor documentation fixing on the way
+
+2012-05-03 15:41:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ ges: Remove invalid ' < 0' checks
+ It's an unsigned value, it will never be < 0.
+
+2012-05-02 23:56:35 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track.c:
+ Do no check if GLib >2.26 as we depend on GLib 2.28
+
+2012-05-02 23:44:31 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: Properly emit notify for the invert and border properties
+
+2012-05-02 23:43:50 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: Expose the transition type as a GObject property
+ API: GESTrackVideoTransition::transition-type property
+
+2012-05-03 03:35:16 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: expose border and inverted as GObject properties
+
+2012-05-03 02:44:00 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * bindings/python/ges.defs:
+ python : binds the getter and setter for the "inverted" property
+
+2012-05-03 02:28:41 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ track-video-transition: Expose the invert property from smpte
+ Also, add/fixup some doc
+ API: ges_track_video_transition_get_inverted
+ API: ges_track_video_transition_set_inverted
+
+2012-05-02 22:03:51 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge remote-tracking branch 'origin/0.10'
+ Conflicts:
+ bindings/python/ges.defs
+ ges/ges-track-video-transition.c
+
+2012-05-02 18:38:42 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: Reuse interpollation setting functions when possible
+
+2012-05-02 18:07:01 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-video-transition.h:
+ track-video-transition: Reindent header
+
+2012-05-02 18:04:54 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ track-video-transition: Add a way to get current border value
+ Add some documentation for the border property
+ Change the border value in set_border to a guint as the value can be negative
+ API: ges_track_video_transition_get_border
+
+2012-05-02 00:27:31 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * docs/libs/Makefile.am:
+ build: Fixes distclean
+
+2012-05-01 23:42:47 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: Return pending type in transition_get_type when needed
+
+2012-05-01 16:01:39 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * bindings/python/ges.defs:
+ python: Binds the "set_border" function
+
+2012-05-02 01:09:07 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ track-video-transition: expose the border property of smptealpha
+ API: ges_track_video_transition_set_border
+
+2012-05-02 01:08:08 +0200 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+
+ * ges/ges-track-video-transition.c:
+ * tests/check/ges/transition.c:
+ ges-track-video-transition: Enables switching from crossfade to smpte and vice versa
+ Fix the tests properly
+
+2012-05-02 08:44:25 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: replace home-grown version of gst_filename_to_uri()
+ and remove superfluous check if file is readable with
+ fopen. Code appears to also want to accept URIs, so this
+ doesn't work so well, and should probably be done differently
+ anyway if required.
+ https://bugzilla.gnome.org/show_bug.cgi?id=674296
+
+2012-04-17 19:18:44 +0400 Руслан Ижбулатов <lrn1986@gmail.com>
+
+ * tools/ges-launch.c:
+ ges-launch: use GRegex instead of POSIX regex
+ http://bugzilla-attachments.gnome.org/attachment.cgi?id=212249
+
+2012-04-17 19:18:21 +0400 Руслан Ижбулатов <lrn1986@gmail.com>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-title-source.c:
+ ges: fix some format strings in debug messages
+ https://bugzilla.gnome.org/show_bug.cgi?id=674265
+
+2012-05-01 19:16:42 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-title-source.c:
+ ges: fix some not entirely correct casts for vararg function arguments
+
+2012-05-01 19:06:20 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ track-video-transition: update for videomixer pad template name change
+
+2012-05-01 19:05:51 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ * tests/examples/test4.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/transition.c:
+ examples: create URIs properly from filenames
+
+2012-05-01 18:50:34 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * .gitignore:
+ * tests/check/ges/.gitignore:
+ * tools/.gitignore:
+ .gitignore: ignore more
+
+2012-05-01 18:48:57 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges.c:
+ ges: fix gnonlin version check
+
+2012-05-01 18:43:02 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ track-audio-transition: fix adder sink pad template name
+
+2012-04-18 18:34:01 +0400 Руслан Ижбулатов <lrn1986@gmail.com>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline-pipeline: fix src pad request template for tee
+ https://bugzilla.gnome.org/show_bug.cgi?id=674339
+
+2012-04-25 17:53:38 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge remote-tracking branch 'origin/0.10'
+ Conflicts:
+ bindings/python/ges-types.defs
+ bindings/python/ges.defs
+ bindings/python/ges.override
+ configure.ac
+ ges/ges-timeline.c
+
+2012-04-25 17:09:19 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ docs: Add some more docs about editing mode
+ Also add the documentation "Section" into ges-enum.c so the file documentation ar
+ actualy taken into account in the final generated documentation.
+
+2012-04-25 14:55:46 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Rework snapping signaling so it is easier to use
+ API: GESTimeline::snap-started signal
+ API: GESTimeline::snap-ended signal
+ (This code has not been released so we can still change the API)
+
+2012-04-23 20:17:42 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-track-object.c:
+ trackobject: Take into account the max duration when trying to set a new duration
+ Change its default value to GST_CLOCK_TIME_NONE instead of 0.
+ (unreleased code so it still can be changed)
+
+2012-04-23 19:20:18 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Add API guards all around
+
+2012-04-23 19:17:51 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-internal.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ trackobject: Add the copy method to the API
+ Add documentation and plug a leak at the same time.
+ API: ges_track_object_copy
+
+2012-04-23 19:10:16 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timelineobject: Ignore notifies when needed
+
+2012-04-22 15:24:25 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ timelineobject: Fix wrong naming when connection to in-point notify
+ inpoint -> in-point
+
+2012-02-10 16:58:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges-types.defs:
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ python: Bind the new Timeline editing mode API
+
+2012-04-22 13:09:11 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-source.c:
+ timelineobject: Make changing start/duration sensible to snapping
+ Adapt the documentation so users are aware of the behaviour
+ Conflicts:
+ ges/ges-timeline-object.c
+
+2012-04-23 20:55:27 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Minor documentations fixes
+
+2012-04-23 20:54:15 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/timelineedition.c:
+ tests: Add a testsuite for the new timeline edition API
+
+2012-04-23 20:52:45 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * configure.ac:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-internal.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/basic.c:
+ ges: Add a timeline edition mode API
+ + timeline: Add a snapping-distance property
+ + Bump the GLib dependency to 2.28 in the mean time as we need some functions from GSequence that only landed
+ + Update the testsuite accordingly
+ API: GESTimeline:snapping-distance property
+ API: ges_timeline_object_edit
+ API: ges_timeline_object_ripple
+ API: ges_timeline_object_ripple_end
+ API: ges_timeline_object_roll_start
+ API: ges_timeline_object_roll_end
+ API: ges_timeline_object_trim_start
+ API: ges_track_object_edit
+ API: GESEdge enum
+ API: GESEditMode enum
+
+2012-04-20 20:05:46 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/timelineobject.c:
+ tests: Add a basic test for the timeline_object_split method
+
+2012-04-20 19:22:56 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ timelineobject: Make the 'position' argument of the split method a guint64 not gin64
+ This makes more sense to be a guint64 as it actually is a GstClockTime,
+ and this way we keep the API concistent.
+ (This code has not been release so we can still change the API.)
+
+2012-04-20 19:19:49 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timelineobject: Reimplement properly the splitting method
+
+2012-04-20 19:02:19 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timelineobject: Misc API guard fixes
+
+2012-04-19 00:34:59 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-internal.h:
+ * ges/ges-track-object.c:
+ track-object: Add method to copy a TrackObject
+ API: ges_track_object_copy
+
+2012-04-17 18:42:41 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track.c:
+ ges-track-object: Make possible to add a track already containing a gnlobject to a track
+
+2012-04-23 14:40:26 -0300 Thiago Santos <thiago.sousa.santos@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ ges-timeline-pipeline: add todo to remember to remove hack
+ Remove playsink hack once we depend on gst-plugins-base 0.10.37
+ (next gst-plugins-base release)
+
+2012-04-23 14:38:31 -0300 Thiago Santos <thiago.sousa.santos@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ Revert "ges: timeline-pipeline: Remove playsink send_event hack"
+ This reverts commit 54aac450dab9ac052f2c0a913bfba5f77c1670ba.
+ We need this hack until we depend on gst-p-base 0.10.36
+
+2012-04-20 14:18:18 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ Port to the new GMutex API
+
+2012-04-16 09:12:06 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 6db25be to dc70203
+
+2012-04-13 13:59:17 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * autogen.sh:
+ * configure.ac:
+ * ges/Makefile.am:
+ configure: Modernize autotools setup a bit
+ Also we now only create tar.bz2 and tar.xz tarballs.
+
+2012-04-13 13:39:50 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 464fe15 to 6db25be
+
+2012-04-07 22:31:23 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge branch '0.10'
+ Conflicts:
+ bindings/python/ges.defs
+
+2012-03-29 18:57:47 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timeline-object: Add TrackObject to the Track after the TimelineObject
+ This way, the Track::track-object-added is emited after the TrackObject is ready to be used, and it make the API easier to use.
+
+2012-04-07 21:40:07 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Factor out a method to start observing timeline for auto-transitions
+
+2012-04-07 21:24:18 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Reorganize file
+
+2012-04-07 21:04:21 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Rework auto transition callbacks management
+ We now have a GESTrack::track-object-added signal so we now depend on it rather than on each GESTimelineObject::track-object-added signal.
+
+2012-03-30 03:40:50 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timeline-object: Properly reflect contained TrackObject duration and inpoint properties changes
+
+2012-01-30 22:55:59 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Some documentation fixing
+
+2012-01-27 16:04:00 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add ges_timeline_object_release_track_object
+ We need it especially in the case of effects
+
+2012-04-04 20:47:04 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Add API documentation
+
+2012-02-02 15:29:30 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline.c:
+ ges: Calm logging when not created TrackObject on purpose
+
+2012-03-31 13:57:04 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Restructurate the file separting methods/callbacks/API
+
+2012-01-25 15:12:05 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timelineobject: Emit effect-added when adding any kind of TrackEffect
+ We were only emitting it when working with TrackParseLaunch effects
+
+2012-02-01 20:25:35 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ docs: Tell users that adding an object to a timeline layer creates media related objects
+
+2012-01-20 17:03:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ ges: Misc debug loggin cleanup
+
+2012-01-20 16:37:28 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ timeline: Plug a leak when calling enable_update
+
+2012-01-22 23:03:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ timeline: Rework the append_layer method
+ ges_timeline_append_layer now creates a new layer, adds it to the timeline
+ and returns it
+ This code has not been released yet so we can break this API.
+
+2012-01-20 14:36:36 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ layer: Add a method to check if a layer is empty or not
+ API: ges_timeline_layer_is_empty
+
+2012-01-16 09:37:22 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/save_and_load.c:
+ ges: Move the max-duration property from TrackFileSource to TrackObject
+ This property was firstly added to TrackFileSource, but in the end, it makes
+ more sense for it to be directly in TrackOject as it can be usefull in other cases.
+
+2012-04-05 18:45:53 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 7fda524 to 464fe15
+
+2012-03-30 03:36:27 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ timeline-layer: Do not call track_get_by_layer when track == NULL
+
+2012-04-04 14:02:43 -0300 Thiago Santos <thiago.sousa.santos@collabora.com>
+
+ Merge branch '0.10'
+
+2012-03-30 19:10:33 -0300 Thiago Santos <thiago.sousa.santos@collabora.com>
+
+ * ges/ges-timeline-pipeline.c:
+ ges: timeline-pipeline: Remove playsink send_event hack
+ This is fixed now in upstream playsink, remove the hack
+ https://bugzilla.gnome.org/show_bug.cgi?id=673211
+
+2012-04-04 14:50:23 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * configure.ac:
+ * docs/libs/Makefile.am:
+ * docs/libs/ges-docs.sgml:
+ * docs/version.entities.in:
+ * ges/Makefile.am:
+ * gst-editing-services.spec.in:
+ * pkgconfig/Makefile.am:
+ * pkgconfig/gst-editing-services-uninstalled.pc.in:
+ * pkgconfig/gst-editing-services.pc.in:
+ * tests/check/Makefile.am:
+ * tests/examples/Makefile.am:
+ * tools/Makefile.am:
+ ges: Update versioning
+
+2012-04-04 12:08:06 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ Merge remote-tracking branch 'origin/0.10'
+ Conflicts:
+ bindings/python/Makefile.am
+ ges/Makefile.am
+
+2012-04-03 19:25:18 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/Makefile.am:
+ * configure.ac:
+ * docs/libs/Makefile.am:
+ * ges/Makefile.am:
+ * ges/ges-formatter.c:
+ * tools/Makefile.am:
+ formatter: Try to figure out new paths when media files have moved
+ Introduces a dependency to GIO
+
+2012-03-29 12:55:44 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ Merge branch '0.10'
+ Conflicts:
+ bindings/python/ges.defs
+
+2012-03-29 15:10:09 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ Merge remote-tracking branch 'origin/0.10'
+
+2012-01-30 17:47:42 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timelineobject: Set TrackObject's TimelineObject only when calling the add function
+ ges_timeline_object_add_track_object actually calls
+ ges_track_object_set_timeline_object so do not do it once more ourself.
+ Especially since it results in having a TrackObject.timeline_object refering
+ to a TimelineObject it is not actually in yet.
+
+2012-03-28 02:53:50 +0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * ges/ges-track.c:
+ ges-track: Set gnlobject state to NULL before disposing it
+ Work around a deadlock if setting state to NULL right before removing the
+ gnlobject from the composition.
+ https://bugzilla.gnome.org/show_bug.cgi?id=672751
+
+2012-01-27 17:09:08 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ timelineobject: Update the nb_effect when releasing a TrackEffect
+
+2012-01-22 22:50:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ ges: Add a way to know whether a timeline is updating on each changes
+ + Bind the new API in python
+ API: ges_timeline_is_updating
+ API: ges_track_is_updating
+
+2012-01-26 11:53:54 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ trackobject: Enable adding a TrackObject in a Track before a TimelineObject
+ We were requiring it only for GESCustomTimelineSource, but it is not actually
+ necessary so, we can just check if the TrackObject is in a TimelineObject or
+ not, and react accordingly.
+
+2012-01-25 12:47:24 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ timeline-layer: Add a method to get the timeline it is currently in
+ API: ges_timeline_layer_get_timeline
+ Bind it in python
+
+2012-03-28 02:53:50 +0300 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+
+ * ges/ges-track.c:
+ ges-track: Set gnlobject state to NULL before disposing it
+ Work around a deadlock if setting state to NULL right before removing the
+ gnlobject from the composition.
+ https://bugzilla.gnome.org/show_bug.cgi?id=672751
+
+2012-03-26 12:43:30 +0200 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ Replace master with 0.11
+
+2012-03-19 10:56:53 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ ges-pitivi-formatter: add copyright/license headers
+ https://bugzilla.gnome.org/show_bug.cgi?id=644943
+
+2012-03-13 13:52:32 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ Merge remote-tracking branch 'origin/master' into 0.11
+
+2012-03-13 11:36:15 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: fix weird windowsy code in save_and_load unit test
+ Fixes compiler error about FILENAME_MAX in 0.11
+
+2012-03-12 16:22:22 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ ges-timeline-pipeline: port to 0.11
+
+2012-03-12 15:46:42 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track.c:
+ * ges/ges-utils.c:
+ Remove ges-marshal.[ch] and use the generic marshaller
+
+2012-03-12 15:37:33 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * configure.ac:
+ configure: bump GLib requirement in line with core and other libs
+
+2012-03-12 15:25:49 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ Merge remote-tracking branch 'origin/master' into 0.11
+ Conflicts:
+ bindings/python/Makefile.am
+ bindings/python/ges-types.defs
+ bindings/python/ges.defs
+ bindings/python/ges.override
+ bindings/python/gesmodule.c
+ bindings/python/testsuite/test_textoverlay.py
+
+2012-03-12 15:15:22 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ configure: check for libxml2 explicitly
+ GStreamer may be built without the libxml2 dependency.
+
+2012-03-12 15:09:39 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ ges-pitivi-formatter: move libxml includes into .c file
+ There's no need to have them in the header file.
+
+2012-03-06 15:37:18 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ ges: Fix 'implicit conversion from enumeration type 'GESTextHAlign' to different enumeration type 'GESTextVAlign'' and similar compiler warnings
+
+2012-03-06 15:35:51 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ ges: Fix 'comparison of unsigned expression < 0 is always false'
+
+2012-02-17 13:46:36 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * bindings/python/Makefile.am:
+ bindings: don't link to libges four times
+ And even less different versions of it.
+
+2012-02-10 19:44:49 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ tests: ges_track_new() takes ownership of caps, so can't use GST_CAPS_ANY
+
+2012-02-10 19:42:16 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: port tests to new raw caps
+
+2012-02-10 19:36:49 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-track.c:
+ track: add g-i annotation that ges_track_new() takes ownership of caps passed
+
+2012-02-10 19:35:28 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track.c:
+ ges: port to new raw audio/video caps
+ Completely untested, but more likely to work than the
+ existing code.
+
+2012-02-10 19:17:38 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/overlays.c:
+ * tests/examples/simple1.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/thumbnails.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ examples, ges-launch: remove deprecated g_thread_init()
+ Not needed any more with recent glib versions
+
+2012-02-10 19:13:44 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/Makefile.am:
+ g-i: need to call gst_init() before ges_init() so GST_TYPE_CAPS is set
+ Fixes "g_param_spec_boxed: assertion `G_TYPE_IS_BOXED (boxed_type)' failed"
+ warnings when running g-ir-scanner.
+
+2012-02-10 19:01:03 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-video-transition.c:
+ track-{audio,video}-transition: update for controller API changes
+
+2012-02-10 18:43:51 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline-pipeline: use standard GLib API to save thumbnail data to file
+
+2012-02-10 18:35:07 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ timeline-pipeline: update for new gst_buffer_map() API
+
+2012-01-30 11:34:09 +0100 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ ges: support and handle no-more-pads in GESTimeline(Pipeline)
+ ... to arrange for a clean READY to PAUSED state change transition.
+ Not doing so might have playsink reaching PAUSED prematurely
+ as one track prerolls, only to lose this state again (temporarily)
+ when the other track needs to preroll.
+ This is generally not nice or convenient, and particularly nasty
+ when trying to perform seek in PAUSED.
+
+2012-01-25 14:13:02 +0100 Thomas Vander Stichele <thomas (at) apestaart (dot) org>
+
+ * common:
+ Automatic update of common submodule
+ From c463bc0 to 7fda524
+
+2012-01-25 11:41:15 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 2a59016 to c463bc0
+
+2012-01-18 16:48:52 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 0807187 to 2a59016
+
+2012-01-12 16:34:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/save_and_load.c:
+ ges: Various doc fixups and cleanups
+
+2012-01-12 15:12:14 +0100 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-timeline-layer.c:
+ ges: only use glib constructs as required in configure.ac
+
+2012-01-12 15:11:10 +0100 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * pkgconfig/gst-editing-services-uninstalled.pc.in:
+ pkgconfig: fix uninstalled pkgconfig to handle out-of-source build case
+
+2012-01-07 13:36:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ pitivi-formatter: Use the new Formatter->project_loaded vmethod
+
+2012-01-07 13:28:15 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ formatter: Add a "loaded" signal
+ API: GESFormatter::loaded signal
+ API: GESFormatter->project_loaded VMethod
+
+2011-12-22 17:11:34 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ docs: Add a pitivi-formatter documentation
+ Move the API to the API section of the pitivi-formatter file
+
+2012-01-05 13:21:40 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-formatter.c:
+ formatter: Enhance some debug logging
+
+2012-01-04 19:04:53 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ ges: Rework the ges_pitivi_get_sources method
+ + Remove the URI parameter of ges_pitivi_get_sources
+ + Rework how we handle the PitiviFormatterPrivate.source_table HashTable
+ rename it to sources_table to make a difference between it and the
+ source_table(s) it containes
+
+2012-01-04 18:06:37 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ ges: Let user set a source list on the PitiviFormatter
+ API: ges_pitivi_formatter_set_sources
+ Bind it in python
+
+2012-01-04 15:06:11 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ ges: Handle the new SourceMoved API in PitiviFormatter
+
+2012-01-04 15:05:15 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ bindings: Bind the new formatter API
+
+2012-01-04 14:59:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ ges: Add an API to Formatter to be able to handle moved sources
+ API: GESFormatter::source-moved signal
+ API: GESFormatter::update_source_uri virtual method
+ Conflicts:
+ ges/ges-formatter.h
+
+2012-01-04 14:46:54 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ ges: Add a "discovery-error" signal to GESTimeline
+ API: GESTimeline::discovery-error signal
+
+2012-01-04 14:24:05 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ ges: Let user set TimelineObject URI while not containing any TrackObject
+ In the case of not properly set uri, we can keep using the same
+ TimelineFileSource changing its URI until its TrackObject could be created.
+ This is particularly usefull in the case of formatter trying to load filesource
+ when the file has been moved
+
+2012-01-03 11:59:29 +0100 mathieu duponchelle <duponc_m@Meh.(none)>
+
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ Add API to get all sources from xptv project
+
+2011-12-23 17:16:20 +0100 mathieu duponchelle <duponc_m@Meh.(none)>
+
+ * ges/ges-timeline-layer.c:
+ Disconnect handlers when object is removed from layer
+
+2011-12-22 21:21:37 +0100 mathieu duponchelle <duponc_m@Meh.(none)>
+
+ * ges/ges-timeline-layer.c:
+ Edit : typos
+
+2011-08-24 12:04:32 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline.c:
+ GES : remove transitions when needed
+ Conflicts:
+ ges/ges-timeline.c
+
+2011-12-21 19:48:22 +0100 mathieu duponchelle <duponc_m@Meh.(none)>
+
+ * ges/ges-timeline-layer.c:
+ Fixes auto transitions on layers + n.
+
+2011-12-29 13:56:08 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-filesource.c:
+ ges: Add a maxduration property to TrackFileSource
+ API: GESTrackFileSource::maxduration property
+
+2011-12-23 19:23:31 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ ges: Disconnect the TrackObject when removed from a Track
+
+2011-12-26 02:54:29 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ * docs/libs/ges-sections.txt:
+ * ges/ges.c:
+ * ges/ges.h:
+ ges: Add a runtime version checking function
+ Bind it in python
+ API: ges_version
+
+2011-12-22 15:59:51 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.h:
+ ges: Reindent ges-track-object.h
+
+2011-12-22 14:41:39 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/gesmodule.c:
+ bindings: Register the various enums/flags in python
+
+2011-12-19 11:21:18 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ ges: Make TrackObject:locked a GObject property
+
+2011-12-18 01:49:24 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ ges: Fix the TimelineObject::effect-added signal emission timing
+ We were emitting it before it gets added to the track_object list,
+ so the list we were getting with get_top_effects was containing
+ a TrackObject that wasen't a TrackEffect
+ + A bit of refactoring
+
+2011-12-16 09:56:08 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.h:
+ ges: Reindent ges-track.h
+
+2011-12-16 09:54:58 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ ges: Some debug logging enhancements
+
+2011-12-16 09:52:35 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ ges: Expand track background duration equal to timeline duration
+
+2011-12-16 09:35:31 +0100 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ ges: Add a duration property to GESTimeline
+ API: GESTimeline:duration property
+
+2011-12-16 04:23:58 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ ges: Fix TimelineObject movement that contains unlocked and relocked objects
+ Record the TrackObject that initiated a TimelineObject movement so we don't
+ get inifite loops.
+ Also fix the new TrackObject calculation:
+ child.start = time - offset (not time + offset)
+
+2011-06-17 14:29:52 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ * tests/check/ges/layer.c:
+ ges: Bump layer height from 10 to 1000
+
+2011-12-07 20:17:55 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * ges/ges-timeline-object.c:
+ ges: Little fixes to timeline_object_g(s)et_supprted_formats
+
+2011-12-07 20:50:13 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ ges: Add guards to all API calls in GESTimelineObject
+ And reindent the .h file
+
+2011-12-07 20:36:24 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ ges: Add a method to TimelineObject to set contained TrackObject-s locked state
+ API: ges_timeline_object_objects_set_locked
+
+2011-12-06 23:11:25 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track.c:
+ docs: Update documentation
+
+2011-12-06 14:11:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ ges: Remove transitions properly
+
+2011-12-06 14:10:14 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ ges: Add a TimelineObject::track-object-removed signal
+ API: TimelineObject::track-object
+
+2011-11-30 21:47:54 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-track.c:
+ ges: Enhance some debug logging
+ Also make sure not to warn when it shouldn't
+
+2011-11-30 21:46:21 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ ges: Misc fixes in ges-timeline-layer
+ Put the comment where they are meant to be
+ Make static functions static
+ Use LAYER_HEIGHT where needed
+
+2011-11-02 13:51:36 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ ges: Auto transition cleanup
+
+2011-11-30 20:13:09 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-file-source.c:
+ * tests/check/ges/filesource.c:
+ ges: Do not add any audio source when still image
+ We now have a backgroud so no problem with that.
+ Fix the testsuite accordingly
+ Fix #657514
+
+2011-11-16 15:22:33 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track-object.c:
+ ges: Fix debugging symbol to avoid segfaults
+
+2011-11-02 13:52:16 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-pitivi-formatter.c:
+ ges: Big PiTiVi formatter cleanup
+ Also set the version to 0.2
+
+2011-10-09 12:28:39 -0400 Stéphane Maniaci <stephane.maniaci@gmail.com>
+
+ * ges/ges-pitivi-formatter.c:
+ ges: Don't release unexisting sources when destroying the formatter
+ This happens in case of an empty project.
+
+2011-10-20 16:16:30 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-track.c:
+ ges: Add a gnl background object to tracks
+ This is in order to support gaps in the timeline.
+ This is not the proper solution, we should make sure to fill gaps properly,
+ but for the time being, it makes the trick
+
+2011-12-06 18:04:11 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-simple-timeline-layer.c:
+ ges: Make sure not to set transition start to negative
+ gnlobject.start is a guint64, we can not set it to a negative value
+
+2011-09-14 14:58:01 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-timeline-layer.c:
+ ges: makes "pass over" accurate and reset priority when transition is removed
+
+2011-08-28 01:13:20 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-timeline.c:
+ ges: don't reset the supported formats in the timeline when they're already set
+ My mom never told me goto was evil
+
+2011-08-26 18:39:39 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-timeline-object.c:
+ ges: make the offset positive as it should have been
+ Took me two days to figure that out :/ I'm pretty sure it's the way things are supposed to be
+
+2011-08-24 12:06:22 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track.c:
+ ges: modifies emission of the track/object-removed signal
+ Make it be emitted right before the track objects is removed so we don't end up
+ with a TrackObject that has already been freed
+
+2011-08-24 11:48:14 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.defs:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ ges: adds a ges_timeline_object_split method
+ Slightly improves the copy function to do so.
+ API: ges_timeline_object_split
+
+2011-06-08 20:36:58 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-object.c:
+ ges: add a timeline_object copy function
+
+2011-08-09 15:56:56 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.defs:
+ python : bind the new timeline_object functions
+ + move_to_layer
+ + is_moving_from_layer
+ + set_moving_from_layer
+
+2011-08-03 02:33:10 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.defs:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ ges: adds an enable_update function to the GESTimeline
+ Binds it in python
+ API: ges_timeline_enable_update
+
+2011-07-28 18:49:04 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track.c:
+ ges : add a track-object(removed signal to the track
+ API: GESTrack::track-object-removed signal
+
+2011-12-01 00:33:38 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-standard-transition.c:
+ ges: Handle supported formats in TimelineStandardTransition
+
+2011-07-24 02:49:36 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * tests/check/ges/layer.c:
+ ges: add an auto-transition to the layer
+ API: GESTimelineLayer.auto_transition property
+
+2011-07-27 02:04:48 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track.c:
+ ges: Keep the track object list sorted in track
+
+2011-07-27 02:02:20 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/testsuite/test_textoverlay.py:
+ * ges/ges-timeline-object.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ ges: add track objects to the track before the track-object-added is emitted
+ NOTE: The caller of ges_timeline_object_create_track_object now needs to add it to
+ the timeline_object after calling the function.
+ Fix the testsuite to support that accordingly
+
+2011-07-13 18:30:06 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ python: bind and override the ges_track_get_objects method
+
+2011-07-08 03:37:28 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ ges: Add API to get the TrackObject-s contained in a Track
+ Sort the track_objects list
+ API: ges_track_get_objects
+
+2011-08-28 03:59:19 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges-types.defs:
+ * bindings/python/ges.defs:
+ python: binds the PiTiVi formatter
+
+2011-08-28 03:58:21 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ examples: Add a load project option to ges-ui
+
+2011-08-28 03:57:13 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * tools/ges-launch.c:
+ tools: Add a -y option to ges-launch to launch pitivi projects
+
+2011-08-28 03:56:26 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * tests/check/ges/save_and_load.c:
+ test: Add a pitivi formatter test
+ For now we requiere a project files and media files to be on the host system,
+ this is not optimal and we should rework that in the future.
+
+2011-08-28 03:55:46 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/Makefile.am:
+ * ges/ges-pitivi-formatter.c:
+ * ges/ges-pitivi-formatter.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ ges: Implement a Pitivi Formatter
+ API: ges_pitivi_formatter_new
+
+2011-08-28 03:48:36 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track.c:
+ ges: add a track-object-added signal to GESTrack
+ API: GESTrack::track-object-added signal
+
+2011-08-28 06:25:37 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-timeline-object.c:
+ ges: Add a track-object-added signal to GESTimelineObject
+ API: GESTimelineObject::track-object-added signal
+
+2011-12-01 00:18:30 -0300 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.defs:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/save_and_load.c:
+ ges: Move supported formats from filesource to timelineobject
+ This is usefull by any subclass of GESTimelineObject
+ + Bind it in python
+ + Fix the keyfile formatter tests
+ API: ges_timeline_object_set_supported_formats
+ API: ges_timeline_object_get_supported_formats
+
+2011-06-07 12:54:06 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ ges: Add a ges_timeline_append_layer convenience method
+ API: ges_timeline_append_layer
+
+2011-06-06 15:56:23 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * tests/check/ges/layer.c:
+ test: Better layer priority handling testing
+ We use the ges_timeline_object_move_to_layer new function to make sure it works,
+ and that everything goes well on priority handling with this new method
+
+2011-06-06 15:55:47 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline.c:
+ ges: add a function to move TimelineObject from a layer to another
+ API: ges_timeline_object_move_to_layer
+ API: ges_timeline_object_is_moving_from_layer
+ API: ges_timeline_object_set_moving_from_layer
+
+2011-06-02 22:03:19 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline.c:
+ ges: Keep layers sorted by priorities in the timeline
+
+2011-06-02 22:01:43 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ ges: Add some debugging symbols
+
+2011-04-15 19:34:28 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/save_and_load.c:
+ ges: Handle TimelineLayer and its contained TimelineObject priorities properly
+ GESTimelineObject.priority is now actually relative to its containing layer
+ priority.
+ Test it in the layer test-suite.
+
+2011-06-02 21:35:59 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * ges/ges-timeline-layer.c:
+ ges: Define a LAYER_HEIGHT constant in the normal layer
+
+2012-01-11 15:31:41 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add pipeline {get|set}_{audio|video}_sink() docs
+
+2012-01-06 09:16:09 -0300 Robert Swain <robert.swain@gmail.com>
+
+ * ges/ges-timeline.c:
+ timeline: simplify code to remove an object from the pendingobjects list
+ g_list_remove_all () can be used as a simplification as the private data to ges
+ timeline object are 1:1.
+
+2012-01-04 17:24:16 +0100 Robert Swain <robert.swain@gmail.com>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Lock object discovery list
+ TimelineFileSource objects are asynchronously discovered with discoverer
+ with such objects being added to a pendingobjects list. If one were to
+ remove a layer before an object in said layer had been discovered, a
+ segfault could occur.
+ As such, management of the list has been made more robust with the
+ addition of a mutex and removal of the object from the pendingobjects
+ list upon layer removal.
+
+2011-11-08 17:29:38 -0500 Mateu Batle <mateu.batle@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: fix trigger notify changing props
+ Notify signal was not triggered when changing properties through
+ ges_timeline_object_set_* functions, only when done through g_object_set
+
+2012-01-04 19:56:19 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 710d453 to 0807187
+
+2012-01-02 15:58:17 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges.c:
+ ges: Update for registry API change
+
+2011-12-30 17:24:37 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ Merge remote-tracking branch 'origin/master' into 0.11
+ Conflicts:
+ bindings/python/Makefile.am
+ bindings/python/ges.override
+ bindings/python/gesmodule.c
+ configure.ac
+
+2011-12-30 17:18:18 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ tools: Add proper include for g_printf
+
+2011-12-30 17:18:40 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/thumbnails.c:
+ tests/thumbnails: Updates
+
+2011-12-30 17:18:18 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tools/ges-launch.c:
+ tools: Add proper include for g_printf
+
+2011-12-30 17:17:11 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests/save_and_load: Cleanups and leak fixing
+
+2011-12-30 17:16:29 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/effects.c:
+ tests/effects: Update for new 'parent' property in objects
+
+2011-12-30 17:15:07 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-screenshot.c:
+ * ges/ges-screenshot.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-transition.c:
+ * ges/ges.c:
+ ges: Update for 0.11 changes
+ * Changes with controller API
+ * Use new GstSample for screenshot API
+
+2011-11-30 16:15:35 +0100 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: allow for optional audio or video track
+
+2011-11-30 15:44:45 +0100 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: port over gst-launch verbose setting
+ ... as it is useful for a quick peek as to what is going on.
+
+2011-11-07 15:08:34 +0100 Robert Swain <robert.swain@gmail.com>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: Add API to get/set audio sink
+
+2011-10-12 12:49:32 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ docs: link against libgstreamer for gst_init() and fix order in GTKDOC_CFLAGS
+ Add missing backslash so we link against libgstreamer.
+
+2011-10-12 12:37:54 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * bindings/python/ges.override:
+ * bindings/python/gesmodule.c:
+ bindings: fix up pygst includes for new install directory
+ Changes from pygst/pygst.h to gst/pygst.h to match the source
+ code layout, which makes things easier in an uninstalled setup.
+ https://bugzilla.gnome.org/show_bug.cgi?id=657435
+ https://bugzilla.gnome.org/show_bug.cgi?id=657436
+
+2011-10-12 12:32:16 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * configure.ac:
+ configure: require pygst from git for the headers
+
+2011-08-26 15:21:25 +0200 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * bindings/python/Makefile.am:
+ python: Add $(PYGST_CFLAGS) to CFLAGS to fix the build
+ https://bugzilla.gnome.org/show_bug.cgi?id=657436
+
+2011-10-11 10:12:05 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add new API
+
+2011-10-11 10:08:47 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * Makefile.am:
+ * bindings/Makefile.am:
+ * bindings/python/Makefile.am:
+ * bindings/python/arg-types.py:
+ * bindings/python/codegen/Makefile.am:
+ * bindings/python/codegen/__init__.py:
+ * bindings/python/codegen/argtypes.py:
+ * bindings/python/codegen/code-coverage.py:
+ * bindings/python/codegen/codegen.py:
+ * bindings/python/codegen/definitions.py:
+ * bindings/python/codegen/defsparser.py:
+ * bindings/python/codegen/docextract.py:
+ * bindings/python/codegen/docgen.py:
+ * bindings/python/codegen/h2def.py:
+ * bindings/python/codegen/mergedefs.py:
+ * bindings/python/codegen/mkskel.py:
+ * bindings/python/codegen/override.py:
+ * bindings/python/codegen/reversewrapper.py:
+ * bindings/python/codegen/scmexpr.py:
+ * bindings/python/examples/Makefile.am:
+ * bindings/python/examples/effect.py:
+ * bindings/python/examples/simple.py:
+ * bindings/python/ges-types.defs:
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ * bindings/python/gesmodule.c:
+ * bindings/python/testsuite/Makefile.am:
+ * bindings/python/testsuite/common.py:
+ * bindings/python/testsuite/runtests.py:
+ * bindings/python/testsuite/test_global_functions.py:
+ * bindings/python/testsuite/test_layer.py:
+ * bindings/python/testsuite/test_simple_layer.py:
+ * bindings/python/testsuite/test_textoverlay.py:
+ * bindings/python/testsuite/test_timeline.py:
+ * bindings/python/testsuite/test_timeline_file_source.py:
+ * bindings/python/testsuite/test_timeline_parse_launch_effect.py:
+ * bindings/python/testsuite/test_timeline_pipeline.py:
+ * bindings/python/testsuite/test_timeline_test_source.py:
+ * bindings/python/testsuite/test_timeline_title_source.py:
+ * bindings/python/testsuite/test_track.py:
+ * bindings/python/testsuite/test_transition.py:
+ * configure.ac:
+ bindings: We no longer use static bindings in 0.11
+
+2011-10-11 10:02:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ TrackVideoTransition: Fix after merge
+
+2011-10-11 09:58:46 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ Merge remote-tracking branch 'origin/master' into 0.11
+
+2011-10-11 09:54:56 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ common: Update to tip of 0.11 branch
+
+2011-10-11 09:51:43 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-video-transition.c:
+ ges: ffmpegcolorspace is dead, long live videoconvert
+
+2011-10-11 09:51:35 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-effect.c:
+ ges: Port to 0.11 API
+
+2011-10-11 09:50:30 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-screenshot.c:
+ screenshot: Use new 0.11 API
+ FIXME : Need to figure out how to get the buffer caps.
+
+2011-10-05 12:24:36 +0200 Robert Swain <robert.swain@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: API documentation improvements
+ Added notes for refcounts and transference to API documentation for the
+ video sink getter/setter.
+
+2011-10-04 16:25:22 +0200 Robert Swain <robert.swain@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: Add video sink get/set API
+ This new API allows getting/setting of the preview mode's video sink
+ element through playsink's video-sink property.
+
+2011-09-07 15:51:36 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * docs/libs/Makefile.am:
+ docs: cleanup makefiles
+ Remove commented out parts that we don't need. Remove "the wingo addition" - no
+ so useful after all. Narrow down file-globs for plugin docs.
+
+2011-09-06 21:53:57 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From a39eb83 to 11f0cd5
+
+2011-09-06 16:07:30 +0200 Stefan Sauer <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 605cd9a to a39eb83
+
+2011-09-02 19:26:43 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ docs: clarify sentence a bit
+
+2011-09-02 18:20:00 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ design: More specifications of compositing and material handling
+ Doing it this way will enable us to handle:
+ * Output conforming (proper scaling/conversion at the right place)
+ * Compositing in an easy way at the layer level
+ * Avoid having too many transformation elements
+
+2011-09-02 17:45:52 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ design: Re-order items by importance
+ Plugins, templates and so-forth are not top priorities.
+ Also update the index
+
+2011-09-02 16:57:37 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Remove duplicate symbol
+
+2011-09-02 16:43:13 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ docs: Add note about merging GNonLin and GES
+
+2011-08-30 16:03:22 +0200 Andoni Morales Alastruey <amorales@flumotion.com>
+
+ * tests/Makefile.am:
+ Don't build the examples if it's disabled in configure
+ Fixes: #657707.
+
+2011-08-30 16:40:03 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Return before the error labels
+ Avoids a warning for no reason
+
+2011-08-29 12:00:06 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Handle some trivial error cases
+ Avoids ending up calling potentially NULL variables
+
+2011-08-29 11:47:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ docs/design: Add section on compositing and mixing
+
+2011-08-29 09:51:10 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ docs/design: updates on effects and conforming materials
+
+2011-08-17 12:24:48 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * bindings/python/examples/simple.py:
+ pyges: updating copyright of simple example
+ Signed-off-by: Thibault Saunier <thibault.saunier@collabora.com>
+
+2011-08-14 20:27:08 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-track-video-transition.c:
+ ges/ges-track-video-transition.c: fix transition of different video sizes
+
+2011-08-14 00:52:23 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: missing Since tag and typo fixes
+
+2011-08-13 19:34:55 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * docs/libs/ges-sections.txt:
+ docs: adding GESPipelineFlags to docs
+
+2011-08-13 18:38:31 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-track-text-overlay.c:
+ docs: add Since tag to new TextOverlay functions
+ And add them to the GES API doc
+
+2011-08-13 17:51:48 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-track-title-source.c:
+ docs: add Since tag to new TitleSource functions
+ And add them to the GES API doc
+
+2011-08-12 23:32:52 +0200 Luis de Bethencourt <luisbg@collabora.com>
+
+ * bindings/python/examples/Makefile.am:
+ * bindings/python/examples/effect.py:
+ pyges: Add an effect example
+
+2011-08-11 18:26:08 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * bindings/python/examples/simple.py:
+ pyges: fix and clean examples/simple.py
+
+2011-08-11 16:35:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitignore:
+ bindings: Ignore more files
+
+2011-08-11 16:32:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * bindings/Makefile.am:
+ * configure.ac:
+ bindings: makefile => Makefile
+ More in sync with all other Makefiles
+
+2011-08-11 16:28:14 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * bindings/python/testsuite/common.py:
+ * bindings/python/testsuite/runtests.py:
+ testsuite: Remove print statements
+
+2011-08-11 14:31:47 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/testsuite/test_timeline_file_source.py:
+ pyges: Fix the timeline_file_source test suite
+ Can't create a GESTimelineFileSource if you don't have the protocol in the uri
+
+2011-08-11 14:27:31 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/Makefile.am:
+ * bindings/python/testsuite/test_global_functions.py:
+ * bindings/python/testsuite/test_layer.py:
+ * bindings/python/testsuite/test_simple_layer.py:
+ * bindings/python/testsuite/test_textoverlay.py:
+ * bindings/python/testsuite/test_timeline.py:
+ * bindings/python/testsuite/test_timeline_file_source.py:
+ * bindings/python/testsuite/test_timeline_parse_launch_effect.py:
+ * bindings/python/testsuite/test_timeline_pipeline.py:
+ * bindings/python/testsuite/test_timeline_test_source.py:
+ * bindings/python/testsuite/test_timeline_title_source.py:
+ * bindings/python/testsuite/test_track.py:
+ * bindings/python/testsuite/test_transition.py:
+ pyges: Install it so we now use import ges
+ Using from gst import ges did not make much sense
+
+2011-08-11 14:22:50 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/makefile.am:
+ pyges: Do not try to build the bindings if no python found
+
+2011-08-11 14:21:18 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/Makefile.am:
+ * bindings/python/examples/Makefile.am:
+ * bindings/python/testsuite/Makefile.am:
+ * configure.ac:
+ pyges: Install files from the examples and testsuite
+
+2011-05-06 19:39:56 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * bindings/python/examples/simple.py:
+ pyges: Add a PyGes example
+
+2011-08-09 22:11:03 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/Makefile.am:
+ * bindings/python/arg-types.py:
+ pyges: Add the GstArgtypes, get all the functions binded
+
+2011-08-09 17:16:44 +0200 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.override:
+ * bindings/python/gesmodule.c:
+ pyges: link against pygst and use GstMiniObject
+
+2011-06-10 16:58:55 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/testsuite/test_global_functions.py:
+ * bindings/python/testsuite/test_layer.py:
+ * bindings/python/testsuite/test_simple_layer.py:
+ * bindings/python/testsuite/test_textoverlay.py:
+ * bindings/python/testsuite/test_timeline.py:
+ * bindings/python/testsuite/test_timeline_file_source.py:
+ * bindings/python/testsuite/test_timeline_parse_launch_effect.py:
+ * bindings/python/testsuite/test_timeline_pipeline.py:
+ * bindings/python/testsuite/test_timeline_test_source.py:
+ * bindings/python/testsuite/test_timeline_title_source.py:
+ * bindings/python/testsuite/test_track.py:
+ pyges : Improve the test suite
+
+2011-06-08 03:23:17 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.defs:
+ pyges : correct the defs for ges_track_audio_raw_new and ges_track_video_raw_new
+
+2011-06-07 01:43:42 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/codegen/argtypes.py:
+ * bindings/python/ges.override:
+ * bindings/python/testsuite/test_timeline.py:
+ pyges : Add overrides
+
+2011-06-06 01:02:17 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.override:
+ pyges : Add *_valist and *_by_pspec to the ignore-glob
+
+2011-06-06 00:59:41 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges.h:
+ Add ges-screenshot.h to ges.h
+ This is to make ges_play_sink_convert_frame available to the bindings.
+
+2011-06-08 03:50:51 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.override:
+ pyges : override unhandled methods
+
+2011-06-08 03:50:25 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/codegen/argtypes.py:
+ pyges : add argtypes
+
+2011-06-07 19:59:16 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.defs:
+ * bindings/python/ges.override:
+ pyges : Remove ges_formatter_set_data and get_data from the .defs
+
+2011-05-14 04:32:45 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.override:
+ pyges : Override ges_timeline_parse_launch_effect_new to make it accept None
+
+2011-06-07 18:38:37 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges-types.defs:
+ * bindings/python/ges.defs:
+ pyges: Update ges.defs and ges-types.defs with the new effect API
+
+2011-06-07 19:44:40 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/testsuite/test_textoverlay.py:
+ pyges : Add a text overlay test
+
+2011-06-07 18:09:35 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/testsuite/test_timeline.py:
+ * bindings/python/testsuite/test_transition.py:
+ pyges : Add actual testing to the testcases
+
+2011-06-07 18:05:43 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.override:
+ pyges: add get_type method to the ignore_blob list
+
+2011-06-07 19:35:00 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.override:
+ pyges: Override methods using GList
+
+2011-05-12 02:27:12 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/Makefile.am:
+ * bindings/python/testsuite/common.py:
+ * bindings/python/testsuite/runtests.py:
+ * bindings/python/testsuite/test_timeline.py:
+ * bindings/python/testsuite/test_transition.py:
+ pyges : Add a test suite with three test cases for the bindings
+
+2011-05-06 23:56:16 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * bindings/python/ges-types.defs:
+ * bindings/python/ges.defs:
+ pyges: Regenerate ges.def and ges-types.def so the contructors are detected properly
+
+2011-05-06 18:11:11 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * bindings/python/codegen/h2def.py:
+ pyges: Make use of the namespace for the constructor classnames in codegen
+
+2011-06-07 15:20:46 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/codegen/__init__.py:
+ * bindings/python/codegen/argtypes.py:
+ * bindings/python/codegen/code-coverage.py:
+ * bindings/python/codegen/codegen.py:
+ * bindings/python/codegen/definitions.py:
+ * bindings/python/codegen/defsparser.py:
+ * bindings/python/codegen/docextract.py:
+ * bindings/python/codegen/docgen.py:
+ * bindings/python/codegen/h2def.py:
+ * bindings/python/codegen/override.py:
+ * bindings/python/codegen/reversewrapper.py:
+ * bindings/python/codegen/scmexpr.py:
+ pyges: Sync codegen with upstream
+
+2011-04-27 08:56:29 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * bindings/python/ges.override:
+ pyges: override ges_track_get_timeline
+ This make the bindings compiling without warning
+
+2011-06-07 19:18:27 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * Makefile.am:
+ * acinclude.m4:
+ * bindings/makefile.am:
+ * bindings/python/Makefile.am:
+ * bindings/python/codegen/Makefile.am:
+ * configure.ac:
+ building: add python bindings
+
+2011-06-07 19:17:10 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges-types.defs:
+ * bindings/python/ges.defs:
+ * bindings/python/gesmodule.c:
+ pyges: Add the necessary file to compile the bindings
+ defs files have been generated with the h2defs.py script
+
+2011-06-07 16:55:41 -0400 Thibault Saunier <thibault.saunier@collabora.com>
+
+ * bindings/python/ges.override:
+ pyges: add registering functions prototypes to ges.override
+
+2011-04-25 19:13:38 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * bindings/python/ges.override:
+ pyges: Define missing types
+
+2011-04-25 19:12:38 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-timeline-pipeline.h:
+ GESTimeleinePipeline: Create a flag type instead of a simple enum
+ Make it binding friendly
+
+2011-06-07 03:37:50 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/ges.override:
+ pyges: Add ges.override
+
+2011-06-07 02:26:20 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * bindings/python/codegen/__init__.py:
+ * bindings/python/codegen/argtypes.py:
+ * bindings/python/codegen/code-coverage.py:
+ * bindings/python/codegen/codegen.py:
+ * bindings/python/codegen/definitions.py:
+ * bindings/python/codegen/defsparser.py:
+ * bindings/python/codegen/docextract.py:
+ * bindings/python/codegen/docgen.py:
+ * bindings/python/codegen/h2def.py:
+ * bindings/python/codegen/mergedefs.py:
+ * bindings/python/codegen/mkskel.py:
+ * bindings/python/codegen/override.py:
+ * bindings/python/codegen/reversewrapper.py:
+ * bindings/python/codegen/scmexpr.py:
+ Add codegen to the tracked files
+
+2011-08-09 19:15:18 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/titles.c:
+ GESTimelineTitleSource/TrackTitleSource: add xpos/ypos setting
+ Vertical and horizontal position properties of the title source
+ can be set and get.
+
+2011-08-09 19:13:37 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/titles.c:
+ GESTimelineTitleSource/TrackTitleSource: add color setting
+ Color property of the text overlay can be set and get.
+
+2011-08-09 17:39:02 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * tests/examples/transition.c:
+ examples: add file inpoints and summary to overlay example
+
+2011-08-08 18:57:37 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * tests/examples/overlays.c:
+ examples: add xpos and ypos options to overlay example
+
+2011-08-08 18:44:57 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * tests/check/ges/overlays.c:
+ GESTimelineTextOverlay/TrackTextOverlay: add xpos/ypos setting
+ Vertical and horizontal position properties of the text overlay
+ can be set and get.
+
+2011-08-08 18:30:42 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ ges-enums: completed support for all options in TextAlign
+ Added the center and position options to the vertical, and horizontal
+ properties of text alignment.
+
+2011-08-05 13:24:17 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ * configure.ac:
+ * ges/Makefile.am:
+ * gst-editing-services.spec.in:
+ * tools/.gitignore:
+ Opening the 0.11 branch
+
+2011-08-03 12:37:14 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * tests/examples/overlays.c:
+ examples: add color option to overlay example
+
+2011-08-03 12:27:04 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-track-text-overlay.h:
+ ges: include indentation fixes
+ run gst-indent through ges-timeline-text-overlay.h and
+ ges-track-text-overlay.h
+
+2011-08-03 12:20:27 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * tests/check/ges/overlays.c:
+ GESTimelineTextOverlay/TrackTextOverlay: add color setting
+ Color property of the text overlay can be set and get.
+
+2011-08-01 13:42:17 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * tools/ges-launch.c:
+ ges: easier to understand ges-launch summary
+
+2011-08-01 13:40:48 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * tools/ges-launch.c:
+ ges: audio and video preset options in ges-launch
+
+2011-06-21 20:35:47 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track-parse-launch-effect.c:
+ effects: implement the TrackParseLaunch get_property method properly
+
+2011-06-07 00:49:58 +0200 Mathieu Duponchelle <seeed@laposte.net>
+
+ * ges/ges-track-object.c:
+ GES : make sure to set n_properties to 0 when needed
+
+2011-07-01 19:30:01 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * tests/examples/test4.c:
+ tests: selectable audio format/container in test4
+ now users can select the desired rendering audio format and
+ container through --aformat and --format, like in ges-launch.
+
+2011-06-30 18:13:15 +0200 Luis de Bethencourt <luis.debethencourt@collabora.com>
+
+ * tests/check/ges/filesource.c:
+ tests: fix TEST_URI for filesource tests
+
+2011-06-27 21:22:48 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * tests/examples/test4.c:
+ examples: fix output_uri in test4
+ clean the make_ogg_vorbis_profile () code.
+ remove extra second of execution time.
+ clean some comment typos.
+
+2011-06-27 20:39:42 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * tests/examples/test2.c:
+ * tests/examples/test3.c:
+ examples: remove extra second in test2 and test3
+ remove unnecessary sources GList.
+ and fix comment typos as well.
+
+2011-06-25 20:12:46 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-timeline-file-source.c:
+ GESTimelineFileSource: Check uri at _new()
+ Check if uri is valid before creating a new object in
+ ges_timeline_filesource_new()
+
+2011-06-25 19:42:29 +0200 Luis de Bethencourt <luis@debethencourt.com>
+
+ * ges/ges-timeline-file-source.c:
+ GESTimelineFileSource: Fix documentation
+
+2011-06-23 11:30:24 -0700 David Schleef <ds@schleef.org>
+
+ * common:
+ Automatic update of common submodule
+ From 69b981f to 605cd9a
+
+2011-05-26 09:15:29 -0700 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ GESFormatter: Plug a leak
+ And make the two save methods have the same code/look
+
+2011-05-20 16:45:25 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-formatter.h:
+ GESFormatter: Move comments out of the way
+ Avoids having them appear in gtk-doc
+
+2011-05-20 16:03:30 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.h:
+ * ges/ges-track-object.h:
+ GESTimelineObject/TrackObject: Don't break ABI for the Class
+ New addition go at the end, and the _reserved pointer gets reduced
+ accordingly
+
+2011-05-20 16:02:58 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-effect.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-parse-launch-effect.c:
+ ges: More "Since: 0.10.2" doc markers
+
+2011-05-20 15:51:33 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: Require core/base 0.10.34
+
+2011-05-18 10:46:34 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges.c:
+ doc: Update the ges_init documentation
+
+2011-05-09 15:15:27 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/random/design:
+ design: Update effect statuts
+
+2011-05-09 13:33:53 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ ges: Fix introspection annotations
+
+2011-05-05 15:02:28 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Add a proper error message on errors
+
+2011-04-27 10:11:44 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-standard-transition.c:
+ GESTimelineStandardTransition: keep track of TrackVideoTransition
+ Use the new track_object_added/release vfunc to get cleaner code
+
+2011-04-27 08:47:02 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ GESTimelineTitleSource: Keep track of contained TrackTitleSource objects
+ We use the new track_object_added and track_object_released vfunc to keep track
+ of the TrackObject we might be interested in. Makes cleaner code
+
+2011-04-26 19:39:56 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ GESTimelineObject: add track_object_added and track_object_removed virtual methods
+ Those methods are meant to be used in sublassed when needed. They are not doing
+ anything at this time, but will be used to clean some code in GESTimelineObject
+ sublcasses.
+
+2011-04-25 17:01:48 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track.c:
+ GESTrack: add a duration property
+ User can connect to the notify::duration signal if needed
+
+2011-04-25 17:00:10 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track.c:
+ GESTrack: keep track of the properties GParamSpecs
+
+2011-05-19 23:01:16 +0300 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 9e5bbd5 to 69b981f
+
+2011-05-18 16:14:35 +0300 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From fd35073 to 9e5bbd5
+
+2011-05-18 12:27:56 +0300 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 46dfcea to fd35073
+
+2011-05-09 14:26:53 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ ges: Initialize GES when building the gir/typelib
+
+2011-05-09 14:25:50 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Move TimelineTestSource symbols to the proper section
+
+2011-05-09 14:25:32 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges.types:
+ docs: Comment enum types in ges.types
+ Not supported yet
+
+2011-05-09 14:24:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ ges: Use %NULL instead of #NULL
+ Reported by Stefan Kost:
+ "% is for constants, # is for objects/structs/types, @ is for parameters."
+
+2011-05-07 16:59:06 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-parse-launch-effect.h:
+ ges: Fix docs for alignment and introspection annotations
+ gtk-doc comments need to have a space before the '*' of each line else
+ they won't be picked up by gtk-doc.
+
+2011-05-07 13:42:24 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ GESTrack: Make debug statement more useful
+
+2011-05-07 13:41:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-parse-launch-effect.c:
+ GESTrackParseLaunchEffect: Name the convert elements better
+ And bump a DEBUG to an ERROR
+
+2011-05-07 13:40:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ GESTrackEffect: Cleanup of the property lookup code
+
+2011-05-07 13:26:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ GESTrackObject: Fix leak when iterating elements
+ We *always* need to unref elements and not just when they're effects
+
+2011-05-07 13:25:06 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ GESTrackObject: use gst_object_ref
+
+2011-05-07 13:22:50 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ GESTrackEffect: Use gst_object_unref for the hash value destroyfunc
+ The values are GstObjects and it makes it easier to track in debug logs
+ when they are being unreffed.
+
+2011-05-06 17:21:22 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ * tests/check/ges/effects.c:
+ GESTrackkEffect: Fix Leaks
+
+2011-05-06 17:18:58 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-parse-launch-effect.c:
+ GESTimelineParseLaunchEffect: finalize to avoid leaks
+
+2011-05-06 14:55:31 -0300 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-parse-launch-effect.c:
+ ges: Add 'Since 0.10.2' to the new effects related API
+
+2011-05-06 19:41:38 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: Check for valid argument
+
+2011-05-06 19:41:16 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: Make a local function static
+
+2011-05-06 19:40:22 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: Check for valid arguments
+ and minor indentation fix
+
+2011-05-06 19:39:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-parse-launch-effect.c:
+ ges: Debug statement fixups
+
+2011-05-06 19:38:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-parse-launch-effect.c:
+ ges: Doc fixes
+
+2011-05-06 19:36:35 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-parse-launch-effect.h:
+ * ges/ges-track-object.h:
+ ges: Include indentation fixes
+
+2011-05-06 19:35:13 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-effect.h:
+ * ges/ges-timeline-parse-launch-effect.h:
+ * ges/ges-timeline-standard-transition.h:
+ * ges/ges-timeline-transition.h:
+ * ges/ges.h:
+ ges: Fix include orders
+
+2011-05-06 11:58:02 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/.gitignore:
+ tests: Add effects to ignored files
+
+2011-05-06 11:56:30 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-object.c:
+ ges: Don't break debug lines
+
+2011-05-06 11:54:41 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add missing symbol to ges-sections
+
+2011-05-06 11:54:19 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-parse-launch-effect.h:
+ TrackLaunchEffect: Fix macros
+
+2011-03-17 11:38:38 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ ges-ui: add effects
+
+2011-03-16 17:06:08 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-parse-launch-effect.c:
+ GESTrackParseLaunchEffect: better create_element implementation
+
+2011-03-16 16:23:53 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ TrackObject: fixe a crash in connect_signal
+
+2011-02-25 17:10:00 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/effects.c:
+ GESTrackObject: add a ges_track_object_list_children_properties method
+ test: Test the new method, and also set/get_child_property_by_spec
+
+2011-02-25 12:13:03 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/effects.c:
+ GESTrackObject: Implement a get/set_child_property_by_spec and get/set_child_property_valist methods
+ Reimplement the get/set_property accordingly
+
+2011-02-25 11:32:44 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: add a ges_track_object_lookup_child method
+
+2011-02-25 10:54:55 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: fixe the connect_properties_signals
+
+2011-02-23 20:30:04 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ * ges/ges-track-object.c:
+ GESTrackObject: Change properties_hashtable format to GParamSpec->GstElement
+ It used to be 'ClassName-property-name' -> GstElement
+
+2011-02-16 18:35:02 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ * ges/ges-track-parse-launch-effect.c:
+ GESTrackEffect: move get_props_hastable implementation from GESTackParseLaunchEffect
+
+2011-02-16 17:51:21 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: set_top_effect_priority refactoring
+
+2011-02-16 17:45:05 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/effects.c:
+ GESTimelineObject: Change the get_effects method to get_top_effects.
+
+2011-02-16 15:51:20 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: fixe a few issues with the get_props_hastable vmethod
+
+2011-02-16 14:30:22 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineEffect: keep the list of TrackObjects always sorted
+ Make sort_track_effects function static
+
+2011-02-16 14:05:14 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/effects.c:
+ GESTimelineObject: use GESTrackEffect as base classe for effects and not GESTrackOperation.
+
+2011-02-11 09:17:58 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-parse-launch-effect.c:
+ GESTimelineParseLaunchEffect: documentation fixing
+
+2011-02-11 09:14:33 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-parse-launch-effect.h:
+ * tests/check/ges/effects.c:
+ TrackParseLaunchEffect: rename ges_track_parse_launch_effect_new_from_bin_desc method to ges_track_parse_launch_effect_new
+
+2011-02-10 16:33:16 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: Update effect implementation doc
+
+2011-02-10 16:15:50 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-effect.h:
+ * ges/ges-timeline-parse-launch-effect.c:
+ * ges/ges-timeline-parse-launch-effect.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ Make GESTimelineEffect abstract and move its implementation to GESTimelineParseLaunchEffect
+ test: Adapte the test suite to suite the new API
+
+2011-02-10 12:17:50 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-timeline-effect.c:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-effect.h:
+ * ges/ges-track-parse-launch-effect.c:
+ * ges/ges-track-parse-launch-effect.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ Make GESTrackEffect abstract and move its implementation to GESTrackParseLaunchEffect
+ test: update the effect test suite
+
+2011-02-08 16:08:28 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges.types:
+ docs: Add missing symbols in docs/libs/ges.types so the class hierarchy is well generated
+
+2011-02-08 11:21:41 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: update effect implementation progress
+
+2011-02-08 15:29:21 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * tests/check/ges/effects.c:
+ GESTrackObject: add the deep-notify signal
+ tests: test the new signal
+
+2011-02-08 14:04:39 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * tests/check/ges/effects.c:
+ GESTimelineObject: Emit signal when adding and removing effects
+ tests: test that those signals are actually well sent
+
+2011-02-08 11:10:31 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ docs: fixe the GESTimelineObject documentation
+
+2011-02-08 11:06:57 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/effects.c:
+ GesTrackObject: add the ges_track_object_get_child_property method
+ test: Test this new method
+ design: change the design file to fit the implementation
+
+2011-02-08 10:25:41 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/effects.c:
+ GESTrackObject: add a ges_track_object_set_child_property_method
+ test: Test the new method
+
+2011-02-08 09:02:56 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * tests/check/ges/effects.c:
+ tests: use the AgingTv as testing effects instead of identity
+ Lets us try the new property handling implementation in TrackObject
+
+2011-02-08 08:57:11 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ GESTrackEffect: add the get_props_hastable virtual method
+ Implements this virtual method for bin described effects.
+
+2011-02-07 17:06:01 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: add an hashtable linking childs properityName -> GstElement
+ We also add a Virtual method that should be implementented in subclasses to generate the new GHasTable
+
+2011-02-04 11:44:19 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/effects.c:
+ GESTimelineObject: add a ges_timeline_object_set_top_effect_priority method
+ Lets the user have a minimum of control over effects priorities
+
+2011-02-04 11:26:11 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: Do not rely on the fact that the trackobject list is sorted
+
+2011-02-03 16:03:10 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * tests/check/ges/effects.c:
+ test: Better priorities height setting testing
+
+2011-02-03 15:40:05 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: Update TrackObject priorities handling
+ make use of the new TrackObject getters
+
+2011-02-03 15:30:30 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: make debugging symbols more usefull
+
+2011-02-03 15:11:54 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackEffect: add getters for the: start, inpoint, duration, priority, active properties
+ docs: add new symbols
+
+2011-02-01 21:22:04 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-effect.c:
+ * tests/check/ges/effects.c:
+ TimelineEffect: implement the create_track_object vmethod
+ tests: test the new vmethod
+
+2011-02-01 21:14:29 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: take a private update_height method out of priority_update_cb
+
+2011-02-01 18:47:09 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-effect.h:
+ TimelineEffect: create 2 properties for bin_descrption, one for the audio track, another for the video one
+ This is more for testing purposes since in the long run we should use Materials
+
+2011-02-01 18:02:23 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * tests/check/ges/effects.c:
+ test: check the height of a TimelineObject when adding effects to it
+
+2011-01-31 13:28:44 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-effect.c:
+ * ges/ges-timeline-effect.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ TimelineEffect: Add the basis for GESTimelineEffect implementation
+
+2011-01-31 13:26:50 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * ges/ges-track-effect.c:
+ TrackEffect: Fixe the documentation
+
+2011-01-31 11:53:38 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: fixe new API documentation
+
+2011-01-31 11:41:37 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/effects.c:
+ TimelineObject: add the ges_timeline_object_get_top_effect_position method
+ tests: adapt the effect testsuite to use this function
+ docs: add the method to the documentation
+
+2011-01-31 11:33:56 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ TimelineObject: remove trailling spaces
+
+2011-01-31 11:32:14 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/effects.c:
+ TimelineObject: add ges_timeline_object_get_effects API
+ tests: Test the new TimelineObject API
+ docs: add the corresponding fonction
+
+2011-01-31 11:22:31 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: adapt the add_track_object so we can add effects to it.
+ We keep the list of contained TrackObject-s order by priority
+
+2011-01-31 11:15:33 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: add a function to sort list of applied effects
+
+2011-01-31 11:10:35 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: add a property to count the number of effects applied on it
+
+2011-02-01 21:23:22 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.h:
+ TrackEffect: add padding to give a margin for API expension without breaking ABI
+
+2011-01-31 11:43:04 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.c:
+ TrackEffect: change the create_element function arguments to fit what is expected
+
+2011-01-31 11:09:47 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: spelling correction in the effect design document
+
+2011-01-31 11:05:10 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: add a description of the ges_timeline_object_get_top_effect_postion method
+
+2011-01-25 19:53:36 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ Specs: Add a description of the GESEffect class
+
+2011-01-21 11:11:12 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-effect.h:
+ TrackEffect: Change copyright
+
+2011-01-21 10:43:09 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: fixe effects API after Edward review
+
+2011-01-18 20:03:42 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ pending fixups/comments
+
+2011-01-18 20:05:54 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-effect.h:
+ * ges/ges.h:
+ * tests/check/ges/effects.c:
+ effects: Make TrackEffect implementation corresponding to the new effect API description
+ Make the effects testsuite correspond to the new API
+ Fixe a few compilation issues due to TrackEffect
+
+2011-01-18 20:03:51 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: move GESTimlineSource new API to GESTimelineObject
+
+2011-01-12 11:47:30 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ design: Effect API draft V2
+
+2010-12-09 16:01:02 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ Specs: review effect API Draft
+
+2010-12-07 13:47:47 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ Specs: Add effect API Draft
+
+2010-12-04 12:22:54 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/design/effects.txt:
+ Specs: Review design of effect implementation proposal
+
+2010-11-15 23:32:23 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-effect.h:
+ * tests/check/ges/effects.c:
+ GESTrackEffect: add private struct
+
+2010-11-08 21:53:26 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/design/effects.txt:
+ Specs: proposal for effects implementation
+
+2010-11-05 12:12:24 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-effect.c:
+ * ges/ges-track-effect.h:
+ * ges/ges-types.h:
+ * tests/check/Makefile.am:
+ * tests/check/ges/effects.c:
+ GESTrackEffect: implementation of this new class
+
+2011-01-17 16:46:15 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch-0.10: Cleanup for error cases and print statements
+ * Use g_error for fatal errors
+ * Don't exit the application from the middle of nowhere
+ * Properly cleanup even in error cases
+ * Don't print out things which aren't needed
+
+2011-04-24 14:07:33 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From c3cafe1 to 46dfcea
+
+2011-01-27 17:47:25 +0100 Alessandro Decina <alessandro.decina@collabora.co.uk>
+
+ * Android.mk:
+ * android/ges-launch.mk:
+ * android/ges.mk:
+ * ges/Makefile.am:
+ * tools/Makefile.am:
+ android: make it ready for androgenizer
+ Remove the android/ top dir
+ Fixe the Makefile.am to be androgenized
+ To build gstreamer for android we are now using androgenizer which generates the needed Android.mk files.
+ Androgenizer can be found here: http://git.collabora.co.uk/?p=user/derek/androgenizer.git
+
+2011-04-04 16:00:37 +0300 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 1ccbe09 to c3cafe1
+
+2011-03-25 22:39:04 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 193b717 to 1ccbe09
+
+2011-03-25 14:58:45 +0200 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From b77e2bf to 193b717
+
+2011-03-25 10:01:45 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * Makefile.am:
+ build: Include lcov.mak to allow tests coverage report generation
+
+2011-03-25 09:35:38 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From d8814b6 to b77e2bf
+
+2011-03-25 09:12:04 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 6aaa286 to d8814b6
+
+2011-03-24 18:51:48 +0200 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From 6aec6b9 to 6aaa286
+
+2011-03-16 19:58:54 -0400 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: the _init_ method shouldn't return before the end
+ Fixes #644939
+
+2011-03-18 19:34:57 +0100 Luis de Bethencourt <luis@debethencourt.com>
+
+ * autogen.sh:
+ autogen: wingo signed comment
+
+2011-03-15 14:05:07 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ examples: Make sure we don't end up using NULL values
+
+2011-03-15 14:04:49 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/overlays.c:
+ * tests/examples/text_properties.c:
+ examples: Remove unused code
+
+2011-03-15 14:02:14 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Handle return value of regcomp()
+
+2011-03-15 14:01:41 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-keyfile-formatter.c:
+ KeyFileFormatter: Handle return value of gst_value_deserialize()
+
+2011-03-01 17:38:52 +0100 Alessandro Decina <alessandro.decina@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ ges: fix compiler warnings
+
+2011-02-16 15:21:48 +0000 Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ ges: make uri strings const
+
+2011-02-28 18:35:14 +0100 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 1de7f6a to 6aec6b9
+
+2011-02-14 12:57:00 +0200 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From f94d739 to 1de7f6a
+
+2011-02-09 11:21:02 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: Store GParamSpec for height and layer
+
+2011-02-07 12:19:18 +0000 Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>
+
+ * ges/ges.c:
+ * ges/ges.h:
+ * tools/ges-launch.c:
+ ges: Check that the gnonlin elements are present at initialization time
+ This avoids hanging with no obvious cause later when they're not.
+ https://bugzilla.gnome.org/show_bug.cgi?id=641246
+
+2011-01-31 19:01:46 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tools/.gitignore:
+ tools: ignore unversioned ges-launch as well
+
+2011-01-31 19:01:24 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: fix printf format issue
+
+2011-01-31 19:00:49 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ ges: fix a couple of printf format warnings
+
+2011-01-27 17:46:19 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * tests/examples/concatenate.c:
+ examples: Fix uninitialized variable compiler warning with gcc 4.6
+
+2011-01-27 17:43:47 +0100 Alessandro Decina <alessandro.decina@collabora.co.uk>
+
+ * ges/ges-keyfile-formatter.c:
+ ges: fix compiler warnings
+
+2011-01-26 23:50:00 +0200 Stefan Kost <ensonic@users.sf.net>
+
+ * tools/ges-launch.c:
+ launch: fix typo in help output
+
+2011-01-25 11:21:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: And back to development we go
+
+=== release 0.10.1 ===
+
+2011-01-20 22:04:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ChangeLog:
+ * Makefile.am:
+ * NEWS:
+ * RELEASE:
+ * configure.ac:
+ * gst-editing-services.doap:
+ Release 0.10.1
+
+2011-01-18 19:06:45 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ random: Update goals/features document
+ So that everybody can know what features we want.
+
+2011-01-17 14:01:28 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: 0.10.0.4 pre-release
+
+2011-01-17 13:59:44 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Set restriction on video profile if present
+
+2011-01-12 17:52:10 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Remove dead code and make functions/variables static
+
+2011-01-12 17:45:23 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Fix and cleanup enum listing
+ It wasn't displaying anything lately.
+
+2011-01-11 20:28:25 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ 0.10.0.3 pre-release
+
+2011-01-11 16:57:45 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * tools/.gitignore:
+ * tools/Makefile.am:
+ ges-launch: Add GStreamer major/minor version to the executable filename
+ https://bugzilla.gnome.org/show_bug.cgi?id=639222
+
+2011-01-11 18:14:41 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ * ges/ges-keyfile-formatter.c:
+ ges: Fix more ges_timeline_get_layers() usage memory leaks
+
+2011-01-11 17:19:54 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-internal.h:
+ * ges/ges.c:
+ ges: Don't install ges-internal.h and hide the GES debug category symbols
+ Fixes bug #639219.
+
+2011-01-11 17:55:25 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-utils.c:
+ ges-utils: minor doc update
+
+2011-01-11 16:32:56 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/Makefile.am:
+ ges: Only export symbols starting with ges_ or GES_
+ https://bugzilla.gnome.org/show_bug.cgi?id=639218
+
+2011-01-11 16:35:05 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ ges: Mark some private symbols static
+ https://bugzilla.gnome.org/show_bug.cgi?id=639218
+
+2011-01-11 15:32:51 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * configure.ac:
+ configure: Require G-I 0.9.6 for the --identifier-prefix parameter
+
+2011-01-11 15:29:01 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * pkgconfig/gst-editing-services-uninstalled.pc.in:
+ * pkgconfig/gst-editing-services.pc.in:
+ pkg-config: Require gstreamer-controller and gstreamer-pbutils
+ Their headers are included by public GES headers
+
+2011-01-11 15:26:08 +0100 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * configure.ac:
+ configure: Add parameter to select GTK+ version to use and default to 2.0
+
+2011-01-11 15:52:57 +0200 Stefan Kost <ensonic@users.sf.net>
+
+ * common:
+ Automatic update of common submodule
+ From e572c87 to f94d739
+
+2011-01-10 16:40:02 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * common:
+ Automatic update of common submodule
+ From 8b72fde to e572c87
+
+2011-01-10 16:51:34 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ common: Update to current master
+
+2011-01-10 16:50:51 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ TimelinePipeline: minor doc fix
+
+2011-01-10 16:50:41 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ docs: Fix image inclusion
+
+2011-01-10 15:49:42 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: 0.10.0.2 pre-release
+
+2011-01-10 15:24:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ docs: Fix for uploading docs
+
+2011-01-10 14:28:35 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track.c:
+ docs: Document all the undocumented public functions
+
+2011-01-10 15:10:01 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ TrackObject: Small cleanup
+
+2011-01-10 15:09:40 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ TrackObject: Remove deprecated FIXME
+ You just need to connect to the notify signal to get updates
+
+2011-01-10 11:18:27 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests: Unref the GList returned by ges_timeline_get_layers...
+ ... in addition to the content themselves
+
+2011-01-10 11:13:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Fixup the sections for missing/renamed/removed symbols
+
+2011-01-10 11:12:55 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.h:
+ Track: Mark as private the instance private structure
+
+2011-01-10 11:12:38 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ Timeline: Fix documentation of return value
+
+2011-01-08 16:01:31 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ GESFormatter: Add private instance and move private variables to it
+
+2011-01-08 15:25:22 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline.c:
+ * tests/examples/ges-ui.c:
+ TimelineFileSource: Create instance private and move private variables to it
+ Fixe/Add getter and setters methods for those variables
+ Fixup documentation
+
+2011-01-08 11:22:36 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/save_and_load.c:
+ Timeline: Add instance private and Move private variables to it
+ Fixe/Add getter methods to get those variables
+ Fixup documentation
+
+2011-01-07 19:36:31 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * tests/check/ges/backgroundsource.c:
+ * tests/examples/ges-ui.c:
+ TimelineTestSource: Move private variables to instance private
+ Fixe/Add getter and setter methods for those variables
+ Fixup documentation
+
+2011-01-07 14:37:56 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-track-text-overlay.c:
+ TimelineTextOverlay: Move private variables to instance private
+ Fixe/Add getter and setter methods for those variables
+ Fixup documentation
+
+2011-01-07 13:48:53 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * tests/examples/ges-ui.c:
+ TimelineTitleSource: Move private variables to instance private
+ Fixe/Add getter and setters methods for those variables
+ Fixup documentation
+
+2011-01-06 16:59:52 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-test-source.h:
+ * tests/check/ges/backgroundsource.c:
+ TrackAudioTestSource: Move private variables to instance private
+ Add getter methods to get those variables
+ Fixup documentation
+
+2011-01-06 16:35:20 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-audio-transition.h:
+ TrackAudioTransition: : Move private variables to instance private
+
+2011-01-06 15:35:42 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/text_properties.c:
+ TrackTextOverlay: Move private variables to instance private
+ Add getter methods to get those variables
+ Add/Fixup documentation
+
+2011-01-08 01:40:18 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ test: make unit tests compile and work in uninstalled setup
+
+2011-01-08 01:36:13 +0000 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ gobject-introspection: fix g-i build for uninstalled setup
+ Requires gst-plugins-base git (> 0.10.31.2) to actually work.
+
+2011-01-06 12:06:24 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-timeline-standard-transition.c:
+ enums: Fix transition enum
+ Leftovers from when we were using the old name
+
+2011-01-06 12:04:53 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * tests/check/ges/titles.c:
+ TrackTitleSource: Move private variables to instance private
+ Add getter methods to get those variables
+ Add/Fixup documentation
+
+2011-01-06 11:30:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ TimelinePipeline: Fix thumbnail method docs and arguments
+ The provided gchar* aren't modified
+
+2011-01-06 11:29:44 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-test-source.h:
+ * tests/check/ges/backgroundsource.c:
+ TrackVideoTestSource: Move private data to instance private
+ Add a getter for the pattern
+ Document methods
+
+2011-01-06 10:55:37 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ * tests/check/ges/transition.c:
+ TrackVideoTransition: Move private variable to instance private
+ Also add/fixup methods to get/set the transition type and document them.
+
+2011-01-06 10:55:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ TimelinePipeline: Updates for pbutils API change
+
+2011-01-05 11:32:29 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: Require core git
+
+2010-12-21 15:24:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ GESTrackAudioTransition: Fix empty if() body
+
+2010-12-20 19:09:48 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Fix usage of encodebin
+ The property name is now avoid-reencoding
+
+2010-12-20 12:02:40 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: minor cleanup
+
+2010-12-20 12:01:04 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/layer.c:
+ GESTimelineObject: Subclass from GInitiallyUnowned
+ The floating reference will be owned by the Layer
+
+2010-12-20 12:00:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline.c:
+ * tests/check/ges/layer.c:
+ GESTimelineLayer: Subclass from GInitiallyUnowned
+ The floating reference will be owned by the Timeline
+
+2010-12-20 11:58:21 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ GESTrackObject: Subclass from GInitiallyUnowned
+ The floating reference will be owned by the Track
+
+2010-12-20 11:56:37 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/filesource.c:
+ TimelineObject: Hold a reference to the controlled TrackObject
+
+2010-12-20 11:38:31 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ tests: Put clearer names on tests
+ Makes it easier to figure out which test failed :)
+
+2010-12-18 11:40:19 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Update common submodule
+
+2010-12-17 11:27:37 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ GES*Object: only use g_object_notify_by_pspec if available
+
+2010-12-17 11:27:23 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: Remove unused quarks
+
+2010-12-17 11:26:49 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: Require GLib 2.22
+ Same requirement as for GStreamer
+
+2010-12-16 19:36:15 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ GESSimpleTimelineLayer: reverting const-ification
+
+2010-12-16 16:47:54 +0000 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ GESSimpleTimelineLayer: add test for _index() method
+
+2010-12-16 16:50:35 +0000 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ GESSimpleTimelineLayer: add _index() method
+
+2010-12-16 19:29:14 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-pipeline.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/timelineobject.c:
+ GESTimelineObject: Add mapping/offset support [start/priority properties]
+ Allows moving independently (or not) timelineobjects and trackobjects and
+ have them synchronized with the offsets taken into account.
+ Right now only the start and priority properties are synchronized. The duration
+ and in-point properties will require more thoughts.
+
+2010-12-16 19:24:52 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/mapping.txt:
+ random: Add explanation about TimelineObject<=>TrackObject mapping
+
+2010-12-16 19:24:25 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: priority offset is handled by the TimelineObject
+
+2010-12-16 18:20:47 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: re-factor property setting code
+ And make sure notifications are emitted at the right time
+
+2010-12-16 16:27:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * tests/check/ges/timelineobject.c:
+ GESTrackObject: Add a 'locked' property for position synchronization
+ And update all code using it
+
+2010-12-16 15:05:29 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/#ges-sections.txt#:
+ * docs/libs/.#ges-sections.txt:
+ * docs/libs/ges-decl-list.txt.bak:
+ * docs/libs/ges-decl.txt.bak:
+ docs: Remove more bogus files
+
+2010-12-16 15:00:46 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/#ges-sections.txt#:
+ * docs/libs/.#ges-sections.txt:
+ * docs/libs/ges-decl-list.txt.bak:
+ * docs/libs/ges-decl.txt.bak:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track.c:
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/filesource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/timelineobject.c:
+ * tests/check/ges/titles.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/overlays.c:
+ * tests/examples/test1.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ GESTrackObject: Hide more variables and provide accessors for them
+
+2010-12-16 12:46:48 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Remove useless variable
+
+2010-12-16 12:41:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-pipeline.c:
+ * tests/check/ges/layer.c:
+ GESTimelineLayer: Hide the object list and priority
+ Add needed setters/getters
+
+2010-12-15 19:40:11 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline.c:
+ ges: Add more documentation and annotations
+ This should make GES gobject-introspection compliant now.
+
+2010-12-15 19:18:42 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.h:
+ TimelineObject: Put more function name in sync with others
+
+2010-12-15 19:18:16 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * tests/check/ges/simplelayer.c:
+ GESSimpleTimelineLayer: _nth() returns a const
+ The refcount isn't incremented.
+
+2010-12-15 19:05:48 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/save_and_load.c:
+ GESTrack: Make more properties private
+ And ensure exported symbols are properly documented and have
+ argument checking.
+
+2010-12-15 15:50:44 +0000 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ GESSimpleTimelineLayer: add test for _nth() method
+
+2010-12-15 16:40:59 +0000 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ GESSimpleTimelineLayer: ensure the object can be located before "object-added" fires
+
+2010-12-15 15:51:23 +0000 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ GESSimpleTimelineLayer: add _nth() method
+
+2010-12-15 15:56:38 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/text_properties.c:
+ test: Disable the text_properties_in_layer test until it's properly implemented
+
+2010-12-15 15:52:03 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitignore:
+ * ges/.gitignore:
+ * pkgconfig/.gitignore:
+ all: add/extend more .gitignore
+
+2010-12-15 15:51:41 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * Makefile.am:
+ * configure.ac:
+ * pkgconfig/Makefile.am:
+ * pkgconfig/gst-editing-services-uninstalled.pc.in:
+ * pkgconfig/gst-editing-services.pc.in:
+ Add .pc files
+
+2010-12-15 13:29:53 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-decl-list.txt.bak:
+ * docs/libs/ges-decl.txt.bak:
+ docs: Remove .bak files
+ Added by error when I added the doc system
+
+2010-12-15 13:27:39 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-decl-list.txt.bak:
+ * docs/libs/ges-decl.txt.bak:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-video-transition.h:
+ ges: Don't shorten symbol names
+ It wasn't making us gain anything, and confuses the hell out of g-ir-scanner.
+
+2010-12-15 12:58:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ ges: Add gobject-introspection support
+
+2010-12-15 12:36:25 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * tests/examples/Makefile.am:
+ * tests/examples/concatenate.c:
+ * tests/examples/test4.c:
+ * tests/examples/thumbnails.c:
+ * tools/Makefile.am:
+ * tools/ges-launch.c:
+ ges: Switch to encoding-profile API from base
+ Remove dependency on gst-convenience.
+
+2010-12-15 11:17:21 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/mapping.txt:
+ random: Add notes about Track mapping
+
+2010-12-14 17:38:55 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: Make base_priority/priority-offset a private field
+
+2010-12-14 17:37:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/mapping.txt:
+ random: Add brainstorming about Timeline<=>Track object mapping
+
+2010-12-10 12:15:54 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-transition.c:
+ GESTrackObject: Add create_element vmethod
+ API: GESTrackObjectClass::gnlobject_factorytype
+ API: GESTrackObjectClass::create_element
+ Most track objects are only specific by the contents of the gnlobject,
+ therefore move the 'create_element' vmethod which was already present
+ in some subclasses to the top-level class.
+ Also make the code more robust
+
+2010-12-10 12:14:32 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ GESTrackTransition: Make it a subclass of GESTrackOperation
+
+2010-12-09 19:36:44 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/text_properties.c:
+ GESTimelineSource: Remove textoverlay properties
+ This will be made more generic by allowing any overlay/effect to
+ be put on any source object.
+
+2010-12-09 18:53:29 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/lifecycle:
+ random: Add lifecycle document
+
+2010-12-09 17:43:08 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/timelineobject.c:
+ tests: Make sure gst_bin_add succeeds
+ And detect when we're trying to add contents to a gnlsource which
+ already has something
+
+2010-12-09 17:09:11 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-standard-transition.c:
+ * ges/ges-timeline-standard-transition.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/transition.c:
+ * tests/examples/ges-ui.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ GESTransition: Make it a base class and add GESTimelineStandardTransition
+ This is to ensure people can create their own Layer Transition subclass.
+ API : GESTimelineTransition is now GESTimelineStandardTransition
+
+2010-12-09 15:21:10 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-track-object.c:
+ ges: Make some classes abstract with G_DEFINE_ABSTRACT_TYPE
+
+2010-12-09 15:13:27 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-transition.c:
+ ges: Avoid leaking a GList of GESTrackObject
+
+2010-12-09 15:12:34 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-source.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-video-test-source.c:
+ ges: Remove unused GObject vmethods
+
+2010-12-09 14:25:22 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-timeline-operation.c:
+ * ges/ges-timeline-operation.h:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ GESTimelineOperation: New abstract class for operations
+ This is a new class for all timeline objects that both produce and
+ consume data.
+ The existing subclasses of it are now:
+ * GESTimelineOverlay
+ * GESTimelineTransition
+
+2010-12-09 12:53:07 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ GESTimelineObject: Clarify usage of create_track_object(s) by subclasses
+
+2010-12-09 12:52:15 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ GESTimelineSource: Remove empty create_track_object vmethod
+ The parent class will check if it is present or not, and call
+ track_objects if needed.
+
+2010-12-09 11:56:00 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-formatter.h:
+ GESFormatter: Hide the save/load vmethod from the docs
+ We need to deprecated them before API/ABI freeze
+
+2010-12-08 16:09:35 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ ges: Remove creators for base classes
+
+2010-12-08 15:48:55 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ GESTimelineSource: Remove _new() since it's a base class
+
+2010-12-08 15:36:55 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-formatter.c:
+ * ges/ges-keyfile-formatter.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-image-source.h:
+ * ges/ges-track-object.h:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-track.h:
+ docs: A round of updates
+
+2010-12-08 15:36:00 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ docs: expose the TrackImageSource docs
+
+2010-12-08 15:32:05 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges.types:
+ docs: Update ges.types with all types
+
+2010-11-28 13:24:07 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline.c:
+ * tests/check/ges/basic.c:
+ * tests/check/ges/save_and_load.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/examples/overlays.c:
+ * tests/examples/text_properties.c:
+ * tests/examples/transition.c:
+ GESTimelineObject: add private structure
+
+2010-11-17 19:53:32 +0100 Thibault Saunier <tsaunier@gnome.org>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * tests/examples/ges-ui.c:
+ GESSimpleTimelineLayer: add private structure
+
+2010-12-04 19:54:13 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-image-source.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ ges: Add instance private structures
+
+2010-11-28 16:40:15 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: add a private structure
+
+2010-11-26 18:43:36 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ GESCustomTimelineSource: add private structure
+
+2010-12-02 19:47:23 +0000 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ Tests: fix CMP_FAIL on 32-bit machines
+
+2010-11-10 19:52:16 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/design:
+ docs: TODO idea dumping
+ Only the beginning
+
+2010-12-02 12:28:15 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: Make sure we specify guint64 with g_object_set arguments
+ Yup, had missed those because of weird macros :(
+
+2010-12-01 12:16:37 +0100 Thibault Saunier <thibault.saunier@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * tests/check/ges/simplelayer.c:
+ SimpleTimelineLayer: Remove bogus check and extend unit test to validate it
+
+2010-12-02 11:54:03 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: Make sure we specify guint64 with g_object_set arguments
+ Avoids crashers on 32bit machines
+
+2010-11-29 13:24:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-formatter.h:
+ * ges/ges-keyfile-formatter.h:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.h:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-image-source.h:
+ * ges/ges-track-object.h:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-track.h:
+ * ges/ges-types.h:
+ ges: Add padding to all public structures
+ This will give us margin for API expansion without breaking ABI.
+ The ABI restriction will only come in place once we do the first
+ official release (i.e. 0.x.0).
+
+2010-11-27 18:38:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Update common
+
+2010-10-22 15:57:45 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ ges-ui: allow enabling/disabling audio/video tracks
+
+2010-10-11 11:53:35 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ ges-ui: implement save as command in gtk demo
+
+2010-10-11 11:38:11 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ ges-ui: implement load command
+
+2010-10-11 11:37:51 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ ges-ui: don't assume we always have 1 layer and two tracks
+
+2010-10-08 12:32:15 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ ges-ui: add new command, remove quit command (closing last window quits app)
+
+2010-11-27 16:56:10 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ test: Fix typo in simplelayer test
+
+2010-11-27 16:55:49 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ check: Small cleanup of the save/load test
+
+2010-11-26 18:39:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-keyfile-formatter.c:
+ Formatter: Extend and fixup documentation
+
+2010-11-26 18:38:49 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ Timeline: Add doc and more comments/fixmes
+
+2010-11-27 18:11:56 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ GESTimeline: Prevent saving timelines without any layers
+
+2010-11-26 13:02:48 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ TimelineLayer: Add debug statement regarding priorities
+
+2010-10-20 18:01:37 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-keyfile-formatter.h:
+ GESFormatter: fix a few typos in documention
+
+2010-10-19 13:35:58 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: add more fixmes
+
+2010-10-07 16:52:51 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: add save->load test case
+
+2010-10-07 16:51:38 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-keyfile-formatter.c:
+ GESKeyFileFormatter: use ges_formatter_get/set data
+
+2010-10-07 14:55:14 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: use ges_formatter_{get,set}_data accessors in unit tests
+
+2010-10-07 14:25:22 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ GESFormatter: Add data-related methods
+
+2010-10-07 14:07:18 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/save_and_load.c:
+ tests: update unit tests
+
+2010-10-07 13:49:15 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-keyfile-formatter.c:
+ * ges/ges-keyfile-formatter.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ GESKeyFileFormatter: New GKeyFile GESFormatter
+
+2010-09-24 19:31:53 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ tools: add project file support to ges-launch
+
+2010-09-29 12:43:47 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ GESTimeline: implementation of save_to/load_from uri
+
+2010-09-21 15:39:07 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/.gitignore:
+ * tests/check/ges/save_and_load.c:
+ tests: Add save/load tests
+
+2010-09-13 16:21:15 -0700 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/Makefile.am:
+ * ges/ges-formatter.c:
+ * ges/ges-formatter.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ GESFormatter: Project file format support
+
+2010-10-22 15:58:22 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: set track state to NULL before removing from timeline
+
+2010-10-07 12:29:05 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: release timeline objects when a layer is removed
+
+2010-10-22 14:02:29 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ GESTrack: keep track of trackobjects and remove/release them in dispose
+
+2010-10-22 14:01:34 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests: test for track removal while timeline contains timeline objects
+
+2010-10-20 16:23:22 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests: test that adding tracks after adding layers works
+
+2010-10-19 17:56:37 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests: unit test to check that objects in layers are properly added to the timeline
+
+2010-09-22 12:32:47 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ GESSimpleTimelineLayer: override get_objects () virtual method
+
+2010-09-22 12:29:26 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ GESTimelineLayer: add get_objects virtual method
+
+2010-10-20 18:00:24 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: allow adding tracks after layers
+
+2010-10-19 16:39:43 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: add existing timeline objects when adding layers
+
+2010-09-23 14:49:04 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges.c:
+ ges: ensure built-in timeline object classes are registered
+
+2010-09-23 14:47:48 +0100 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: remove layers before removing tracks in dispose ()
+
+2010-11-25 14:03:07 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Gracefully handle missing properties
+
+2010-11-25 14:02:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ Timeline: Remove unneeded variable
+
+2010-11-25 14:01:15 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: Forgot a break in a switch/case
+
+2010-11-23 18:24:38 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Make sure playsink events are sent to all incoming streams
+ PlaySink will not send seek events to all incoming streams since it assumes that
+ they all come from the same source (like a file).
+ When used with multiple gnonlin compositions we need to make sure those seek events
+ are sent to all of them.
+
+2010-11-23 17:34:07 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitignore:
+ ges: Ignore more
+
+2010-11-23 17:33:32 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/.gitignore:
+ * tests/examples/.gitignore:
+ tests: Ignore more files
+
+2010-10-23 17:38:31 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ GesTrackVideoTransition: Prefer videomixer2 to videomixer
+ If present
+
+2010-11-10 16:13:07 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * AUTHORS:
+ * README:
+ AUTHORS/README: cleanup
+
+2010-11-11 17:39:32 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: g_print => g_printerr for relevant messages
+
+2010-11-09 16:27:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/concatenate.c:
+ examples: Fix for latest GstDiscoverer API changes
+
+2010-11-04 12:29:20 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: Require GStreamer core/base 0.10.30.4
+
+2010-11-04 12:28:46 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Change for updated GstDiscoverer API
+
+2010-09-28 16:30:30 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ Add gstvideo in build dependencies
+
+2010-09-23 18:39:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ common: Update to master
+
+2010-09-23 18:33:27 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * docs/libs/Makefile.am:
+ * ges/Makefile.am:
+ * ges/ges-screenshot.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * tests/check/Makefile.am:
+ * tests/examples/Makefile.am:
+ * tests/examples/concatenate.c:
+ * tools/Makefile.am:
+ all: Changes for discoverer being merged upstream
+ Along with a whole bunch of Makefile fixups
+
+2010-09-16 09:07:05 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Return 0 if no errors, else 1
+
+2010-09-16 08:42:50 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ Update common
+
+2010-09-14 16:04:02 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-screenshot.c:
+ * ges/ges-screenshot.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/test4.c:
+ * tests/examples/thumbnails.c:
+ * tools/ges-launch.c:
+ Update for factorylist/convertframe being merged to gst core/base
+
+2010-08-20 12:40:05 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ * ges/ges-screenshot.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline.c:
+ * tests/examples/concatenate.c:
+ * tests/examples/test4.c:
+ * tests/examples/thumbnails.c:
+ * tools/ges-launch.c:
+ Update to moved gst-convenience
+
+2010-09-02 18:19:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ common: Update to latest version
+
+2010-08-05 18:32:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ relax restrictions on adding transitions
+ We don't need these any more: the valid property tells us whether it is safe
+ to go to GST_STATE_PLAYING or not.
+
+2010-08-05 18:11:49 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ move a callback into the proper section of the file
+
+2010-08-05 17:50:48 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ fix bug in time format regex
+
+2010-08-05 17:48:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ show duration text entry for all object types
+
+2010-08-05 16:19:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ connect to delete event instead of destroy
+
+2010-08-05 16:14:09 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ hide empty menus
+
+2010-08-05 16:12:45 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ ignore some UI signals when selection changes
+
+2010-08-05 15:46:34 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ disable playback when layer is in invalid state
+
+2010-08-05 15:46:02 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * tests/check/ges/simplelayer.c:
+ layer is also invalid when there are transitoins at the beginning/end
+
+2010-08-05 15:21:57 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * tests/check/ges/simplelayer.c:
+ add vaid property and unit tests
+
+2010-08-05 15:21:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ add doc comment for object-moved signal
+
+2010-08-05 12:52:13 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update list store layer emits object-moved
+
+2010-08-05 12:51:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ add unit tests for 'object-moved' signal
+
+2010-08-05 12:50:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/gesmarshal.list:
+ add object-moved signal to simple timeline layer
+
+2010-08-04 18:49:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ implement move up/down commands
+
+2010-08-04 18:31:34 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ connect to move_{up,down actions
+
+2010-08-04 18:25:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ also disallow moving past the start or end of timeline
+
+2010-08-04 18:02:14 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ can move only when exactly one clip is selected (and not in playback/paused)
+
+2010-08-04 17:48:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ add move up/down actions
+
+2010-08-04 17:43:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ re-arrange tool-bar buttons
+
+2010-08-04 17:34:51 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ don't allow changes to timeline if we're paused
+
+2010-08-04 17:27:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ implement the stop button
+
+2010-08-04 17:17:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ add stop button
+
+2010-08-04 16:57:18 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ code clean-up and comments
+
+2010-08-04 16:36:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ eliminate unecessary function prototypes
+
+2010-08-04 16:26:39 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ move UI callbacks to end of file
+
+2010-08-04 16:25:00 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ clean up application struct
+
+2010-08-03 19:53:34 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ more xml tweaks
+
+2010-08-03 19:42:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ don't connect to selected objects unless selection is homogenous
+
+2010-08-03 19:38:13 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ UI layout tweaks
+
+2010-08-03 19:31:23 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ only allow transitions to be added when the last object isn't a transition
+
+2010-08-03 16:19:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ use a private struct with get_selection_foreach
+
+2010-08-03 15:59:38 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ disable add_transition
+
+2010-08-03 15:54:25 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ get add_transition action from xml
+
+2010-08-03 15:51:29 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ menu/toolbar for adding transitions
+
+2010-08-03 15:40:44 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ allow multiple selections
+
+2010-08-03 15:17:25 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ set pipeline to ready on EOS
+
+2010-08-03 15:03:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ make sure all actions that mutate timeline are disabled during playback
+
+2010-08-03 14:58:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ set frequency property when spin button changed
+
+2010-08-03 14:53:22 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update frequency spin button when selection changed
+
+2010-08-03 14:46:21 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ add frequency and volume widgets, with signal handlers
+
+2010-08-03 14:43:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update source volume when slider is moved
+
+2010-08-03 14:39:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update volume slider when volume changes
+
+2010-08-03 14:38:42 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ get freq/volume widgets
+
+2010-08-02 19:06:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ implement background widget
+
+2010-08-02 17:26:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ fill backround type table from enum values
+
+2010-08-02 17:25:26 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ adjust visibility calculations
+
+2010-08-02 17:24:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ allow adding test sources
+
+2010-08-02 17:22:39 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ get background widgets from xml
+
+2010-08-02 17:21:36 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ test sources
+
+2010-07-27 15:25:20 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ add text box to set durations from formatted strings
+
+2010-07-26 20:40:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ set sate of pipeline to NULL prior to exit
+
+2010-07-23 19:14:21 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update text property from text widget
+
+2010-07-23 18:59:40 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ call ges_simple_timeline_layer_add_object intead of base method
+
+2010-07-23 18:59:11 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ completely hide properties widgets when nothign is selected
+
+2010-07-23 18:56:48 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ refactor connecto_to_* family of functions
+
+2010-07-23 18:43:37 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ connect to text property widgets
+
+2010-07-23 18:42:53 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ add widgets to UI for editing text properties
+
+2010-07-23 18:42:13 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ add signal handlers for text properties
+
+2010-07-23 18:38:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ keep track of the type of selected objects
+
+2010-07-23 18:36:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ add title source when action activates
+
+2010-07-23 10:58:11 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ add text properties to UI
+
+2010-07-22 18:07:26 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ allow multiple files to be added
+
+2010-07-22 17:58:00 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ use file-chooser dialog when adding files
+
+2010-07-22 13:13:20 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ disable playback when there's nothing in the timeline
+
+2010-07-22 12:54:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ disable add_file during playback
+
+2010-07-22 12:51:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ disable delete during playback
+
+2010-07-22 12:48:34 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ disable properties during playback
+
+2010-07-22 12:43:13 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update state from bus; set button icon from playback state
+
+2010-07-22 12:20:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ set the action sensitivity, not the menu item
+
+2010-07-22 12:17:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ toggle playback when button clicked
+
+2010-07-22 12:08:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ fix signal handler signatures
+
+2010-07-22 12:05:09 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ add play action/controls
+
+2010-07-22 11:00:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ add toolbar; connect to action signals instead of menu items directly
+
+2010-07-22 10:18:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ also set state of delete menu item
+
+2010-07-21 20:21:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ update in-point from in-point slider
+
+2010-07-21 19:02:09 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ add in-point slider
+
+2010-07-21 19:01:33 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ format duration nicely
+
+2010-07-21 18:15:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ set slider range before setting duration
+
+2010-07-21 18:15:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ remove some unneeded function protos
+
+2010-07-21 18:03:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ slider updates duration of selected object now
+
+2010-07-21 17:00:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ connect to duration and max-duration changed of filesources
+
+2010-07-21 16:29:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ do housekeeping each time selection is updated
+
+2010-07-21 15:43:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ clean up the create_ui function a bit
+
+2010-07-21 15:23:18 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ disable properties editor when nothing is selected
+
+2010-07-21 15:22:29 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ rename a few attributes
+
+2010-07-21 13:54:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ update list model when objects are removed from layer
+
+2010-07-21 13:53:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ delete selected objects from layer when user issues delete command
+
+2010-07-21 13:53:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ store a pointer to the list selection
+
+2010-07-21 11:40:18 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ display filename and duration of added clip
+
+2010-07-21 11:36:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.glade:
+ don't create tree model in XML
+
+2010-07-20 19:41:58 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ make sure we get everythign we need from the XML file
+
+2010-07-20 19:13:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ connect to layer object-{added,removed} and display message
+
+2010-07-20 19:05:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ add some simple code to add a source to the timeline
+
+2010-07-20 18:53:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ add liscence and some organizational comments
+
+2010-07-20 18:38:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ we don't have to free all that much, in fact
+
+2010-07-20 18:24:14 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ create a simple timeline layer
+
+2010-07-20 17:59:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ pass app instance to signal handlers
+
+2010-07-20 17:55:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ move app data to private struct
+
+2010-07-20 17:52:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ export dynamic symbols in examples so signal autoconnect works
+
+2010-07-20 17:05:26 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ add protos so it builds under c90 mode
+
+2010-07-20 16:44:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ add ges-ui.c to build system
+
+2010-07-20 16:43:03 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * configure.ac:
+ add GTK+ to configure.ac as optional dependency for examples UI
+
+2010-07-20 16:23:40 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/ges-ui.c:
+ * tests/examples/ges-ui.glade:
+ move ui to tests/examples
+
+2010-07-20 15:56:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-ui.c:
+ * tools/ges-ui.glade:
+ add 'add_file' menu item
+
+2010-07-20 13:57:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-ui.c:
+ * tools/ges-ui.glade:
+ flesh out ui design a bit more
+
+2010-07-19 19:39:26 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-ui.c:
+ * tools/ges-ui.glade:
+ quit when main window closes
+
+2010-07-19 19:02:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-ui.c:
+ check in UI implementation
+
+2010-07-19 18:09:32 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-ui.glade:
+ check in ui file
+
+2010-09-02 17:55:20 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: Fix debug statement
+
+2010-09-02 17:54:48 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/filesource.c:
+ tests: Fix a leak in test_filesource_images
+
+2010-09-01 17:04:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ GESTimelineFileSource: Don't leak strings
+
+2010-07-16 16:43:38 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ update unit tests to catch an earlier bug
+
+2010-07-14 16:50:16 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/text_properties.c:
+ check in text properties example
+
+2010-07-14 16:14:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ deactivate overlay when no text is present
+
+2010-07-14 16:12:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/text_properties.c:
+ add unit tests for text properties
+
+2010-07-14 15:23:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ expose text, font-desc, and alignment properties in GESTimelineSource
+
+2010-07-14 15:19:30 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ fix typo in docstring
+
+2010-07-14 13:18:57 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-text-overlay.c:
+ add conversion elements to TrackTextOverlay
+
+2010-07-14 13:14:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ tweak gstl recalculate: cur track object sets priority for next transition
+
+2010-07-13 18:44:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/backgroundsource.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/titles.c:
+ supply type param to _find_track_objects in unit tests
+
+2010-07-13 18:42:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ add type argument to ges_timeline_object_find_track_objects()
+
+2010-07-13 18:14:33 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ unref timeline in unit test
+
+2010-07-13 18:12:34 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ don't add the same track object twice
+
+2010-07-13 17:13:02 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ check for proper instance type in _set_* functions
+
+2010-07-13 12:11:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.h:
+ fix documentation comment
+
+2010-07-09 18:59:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ create a text overlay with default text for every TimelineSource
+
+2010-07-09 18:29:27 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ * tests/check/ges/layer.c:
+ * tests/check/ges/simplelayer.c:
+ increase default priority offset for sources; update unit tests
+
+2010-07-09 18:27:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ move this commit down where it belongs
+
+2010-07-09 18:26:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ override create_track_objects (plural) in TimelineSource
+
+2010-07-09 15:59:44 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/layer.c:
+ test height notification
+
+2010-07-09 13:49:23 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ update height when track object priority offset changes
+
+2010-07-09 13:48:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ reword priority documentation comments
+
+2010-07-09 12:10:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ use object height in gstl recalcuate
+
+2010-07-09 12:09:29 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ initialize height to 1
+
+2010-07-09 12:09:08 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.h:
+ add accessor macro
+
+2010-07-09 11:51:21 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ add height property
+
+2010-07-09 11:50:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ document timelineobject::priority
+
+2010-07-08 19:01:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * tests/check/ges/layer.c:
+ expose priority-offset as a property
+
+2010-07-08 18:52:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * tests/check/ges/layer.c:
+ update documentation and unit tests
+
+2010-07-08 18:51:38 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ distinguish between base_priority, priority_offset, and gnl_priority
+
+2010-07-07 17:07:33 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ split timeline_object_add_track_object out of create_track_object
+
+2010-07-07 16:51:39 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline.c:
+ add create_track_objects
+
+2010-07-07 15:47:51 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.h:
+ add create_track_objects declarations
+
+2010-07-07 15:47:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ call create_track_objects in timeline.c
+
+2010-08-31 13:49:21 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ GESTimelineTransition: Remove unneeded variable
+
+2010-08-31 13:29:37 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ ges-launch: Fix memory leak
+
+2010-08-12 15:45:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-image-source.c:
+ allow borders on still image videoscale
+
+2010-08-12 15:44:47 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ always support audio on still images
+
+2010-08-11 18:23:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-image-source.c:
+ remove ffmpegcolorspace after freeze (see 626518)
+
+2010-08-10 16:17:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ don't set max duration on still images
+
+2010-08-10 10:54:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-image-source.c:
+ implement still image sources
+
+2010-08-09 18:36:00 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-image-source.c:
+ naive implementation of still images (seems broken)
+
+2010-08-09 18:35:26 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ * tests/check/ges/filesource.c:
+ create GESTrackAudioTestSource for audio tracks when is-image is true
+
+2010-08-09 18:34:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ set 'is-image' property true when source has an image stream type
+
+2010-08-09 13:27:25 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/filesource.c:
+ set supported formats in new unit test
+
+2010-08-09 13:26:20 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ create image sources when is_image is set to true
+
+2010-08-09 12:01:34 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ Add "is-image" property to GESTimelineFileSource
+
+2010-08-09 11:59:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/filesource.c:
+ unit test for image sources
+
+2010-08-06 12:58:08 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-image-source.c:
+ * ges/ges-track-image-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in GESTrackImageSource
+
+2010-08-05 12:19:32 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/Makefile.am:
+ ges: Also dist the generated files
+
+2010-08-05 11:40:49 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/Makefile.am:
+ marshal: Fix typo in the Makefile that prevented marshal .c being built
+
+2010-07-23 18:22:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-title-source.c:
+ link to the right sink pad on textoverlay object
+
+2010-07-16 18:41:02 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ fwrite doesn't return the number of bytes written. check that fwrite is non-zero and that ferror() isn't set instead.
+
+2010-07-16 18:39:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-screenshot.c:
+ don't plug encoders when raw caps are given
+
+2010-07-16 18:37:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/thumbnails.c:
+ add thumbnailing example which tests rest of thumbnailing api
+
+2010-07-16 17:38:44 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ ges_caps_set_simple was being called incorrectly
+
+2010-07-16 18:17:27 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ docs: Use the proper location for header files
+
+2010-07-16 18:00:05 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ ges: Don't forget to dist ges-timeline-overlay.h
+
+2010-07-16 17:29:05 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ TimelinePipeline: Make sure fwrite completes successfully
+
+2010-07-15 19:50:22 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ add option to ges-launch to save thumbnails periodicaly
+
+2010-07-15 19:49:53 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ update documentation
+
+2010-07-15 19:49:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ add routine to save a thumbnail in the specified encoding
+
+2010-07-15 19:19:57 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-screenshot.c:
+ add todo item
+
+2010-07-15 19:12:53 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-screenshot.c:
+ factor encoder-finding code into separate function
+
+2010-07-15 18:59:50 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-screenshot.c:
+ use gstprofile to plug an encoder and encode the current frame
+
+2010-07-15 16:58:22 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ add methods to retreive the current frame as a thumbnail
+
+2010-07-15 16:56:00 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-screenshot.c:
+ * ges/ges-screenshot.h:
+ duplicate code from gstscreenshot.{c,h} and gstplaysink.{c,h}
+
+2010-07-15 12:09:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Use smartencoder feature from encodebin
+
+2010-07-14 13:29:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure: Require new core/base for fast pad linking
+
+2010-07-14 13:29:23 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-transition.c:
+ GES: Switch to new fast pad linking
+
+2010-07-08 17:10:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ fix mem leak in unit test
+
+2010-07-08 16:35:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-timeline-test-source.c:
+ Don't expose test source enum value table
+
+2010-07-08 15:54:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ don't expose transition enum value table
+
+2010-07-08 15:54:27 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ use gtype to get the enum value for the nick
+
+2010-07-08 13:20:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ check whether setting vtype property actually succeeds
+
+2010-07-07 18:00:21 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-video-transition.c:
+ initialize transitions to type _TRANSITION_TYPE_NONE
+
+2010-07-08 13:41:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ add new invalid enum type
+
+2010-07-08 13:20:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-video-transition.c:
+ refactor duration_changed method as this is now a TrackObjectClass method
+
+2010-07-08 13:20:08 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ remove notify::duration signal handler
+
+2010-07-08 12:35:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ add track object virtual methods for property change notifications
+
+2010-07-07 17:58:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-video-transition.c:
+ remove unneeded assertions
+
+2010-07-07 17:34:58 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ remove unneeded _new methods on certain base classes
+
+2010-07-06 19:08:56 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add ges_track_video_test_source_set_pattern
+
+2010-07-06 19:07:50 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ TrackTransition: Don't return anything for unhandled tracks
+
+2010-07-06 19:06:24 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-transition.c:
+ TrackTransition: Remove second argument from duration_changed vmethod
+ And get properties directly from parent classes instead
+
+2010-07-06 19:05:38 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-video-test-source.c:
+ GES: whitespace fixes
+
+2010-07-06 19:04:42 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ GES: Simplify loops
+
+2010-07-06 19:03:52 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ GES: Remove un-needed branches
+ res it initialized to NULL
+
+2010-07-06 19:03:05 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ GES: Fix initialization values
+
+2010-07-06 19:02:02 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-transition.c:
+ GES: Remove useless variables
+
+2010-07-06 19:00:50 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-video-test-source.h:
+ GES: Fix function prototypes
+
+2010-07-06 18:58:16 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-test-source.c:
+ TimelineTestSource: Set freq/volume whether mute or not
+
+2010-07-06 18:57:22 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-overlay.c:
+ TimelineOverlay: Fix doc
+
+2010-07-06 18:54:33 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ * tests/check/ges/transition.c:
+ TrackVideoTransition: Avoid switching from crossfade to other types
+ This now exposes a bug in the TimelineTransition, since it will have
+ a transition type different from its track objects.
+
+2010-07-06 16:27:21 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ SimpleTimelineLayer: Fix top-level doc
+
+2010-07-06 16:27:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ TimelineLayer: Document _set_priority
+
+2010-07-06 16:26:48 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ TrackVideoTransition: Use proper transition type
+
+2010-07-06 16:26:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-utils.c:
+ utils: Document timeline_new_audio_video
+
+2010-07-06 16:25:50 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ docs: hide _get_type/TYPE in private sections
+
+2010-07-02 16:39:33 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-transition.h:
+ make type field of video transition private
+
+2010-07-02 16:23:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges.types:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-track-video-transition.h:
+ * ges/ges.h:
+ another massive documentation update
+
+2010-07-02 15:42:48 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-track-audio-test-source.c:
+ * tests/check/ges/backgroundsource.c:
+ expose freq and volume props in GESTimelineTestSource
+
+2010-07-02 14:46:09 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-test-source.h:
+ add routines to AudioTestSource to set freq and volume
+
+2010-07-02 13:14:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * tests/check/ges/overlays.c:
+ remove 'mute' property from GESTimelineTextOverlay
+
+2010-07-02 12:57:38 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ fix incorrect type name strings
+
+2010-07-02 12:48:11 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges.types:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-test-source.h:
+ * tests/check/ges/backgroundsource.c:
+ convert rest of code to use GESVideoTestPattern
+
+2010-07-02 12:47:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ add GESVideoTestPattern enum
+
+2010-07-02 12:26:55 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/titles.c:
+ update unit tests
+
+2010-07-02 12:26:42 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ make sure to set properties on new track objects
+
+2010-07-02 12:25:58 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ replace existing text position enums
+
+2010-07-02 12:25:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ replace existing text position enums
+
+2010-07-02 12:12:30 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ add text positioning enums
+
+2010-07-01 18:53:08 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ move missing symbol to enums section
+
+2010-07-01 18:50:55 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ update documentation files
+
+2010-07-01 18:50:30 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-track-video-transition.c:
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/transition.c:
+ * tools/ges-launch.c:
+ move and rename TRANSITION_VTYPE into enums.h and rename
+
+2010-07-01 17:24:49 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ move track type enum to ges-enums.{h,c}
+
+2010-07-01 17:03:55 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-enums.c:
+ * ges/ges-enums.h:
+ * ges/ges.h:
+ check in skeletal ges-enums.{c,h}
+
+2010-07-01 16:48:45 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-track-source.h:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-transition.c:
+ massive documentation updates
+
+2010-07-01 12:35:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ GESTimelineTextOverlay inherits from GESTimelineOverlay
+
+2010-07-01 12:34:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in emtpy GESTimelineOverlay class
+
+2010-07-01 11:17:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-text-overlay.c:
+ * ges/ges-timeline-text-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/overlays.c:
+ * tests/examples/overlays.c:
+ GESTimelineOverlay -> GESTimelineTextOverlay
+
+2010-06-30 20:25:18 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-test-source.c:
+ * ges/ges-timeline-test-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ * tools/ges-launch.c:
+ GESTimelineBackgroundSource -> GESTimelineTestSource
+
+2010-06-30 20:01:18 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ GESTrackAudioBackgroundSource -> GESTrackAudioTestSource
+
+2010-06-30 19:34:29 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-track-audio-test-source.c:
+ * ges/ges-track-audio-test-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-video-test-source.c:
+ * ges/ges-track-video-test-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ GESTrackVideoBackgroundSource -> GESTrackVideoTestSource
+
+2010-06-30 18:13:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-track-text-overlay.c:
+ * ges/ges-track-text-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ * tests/check/ges/overlays.c:
+ GESTrackVideoOverlay -> GESTrackTextOverlay
+
+2010-06-30 18:02:49 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-overlay.c:
+ * ges/ges-track-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ remove GESTrackOverlay
+
+2010-06-30 17:59:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-track-video-overlay.c:
+ * ges/ges-track-video-overlay.h:
+ * tests/check/ges/overlays.c:
+ GESTrackVideoOverlay inherits directly from GESTrackOperation
+
+2010-06-30 17:50:49 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-operation.c:
+ * ges/ges-track-operation.h:
+ * ges/ges-types.h:
+ check in GESTrackOperation
+
+2010-06-30 17:34:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * ges/ges-track-video-background-source.h:
+ documentation fixes
+
+2010-06-30 17:29:32 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ update documentation
+
+2010-06-30 17:29:21 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-track-overlay.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-video-overlay.c:
+ * ges/ges-track-video-overlay.h:
+ * ges/ges-types.h:
+ * tests/check/ges/overlays.c:
+ * tests/check/ges/titles.c:
+ GESTrackVideoTitleSource -> GESTrackTitleSource
+
+2010-06-30 17:02:10 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-track-overlay.c:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-video-overlay.c:
+ * ges/ges-track-video-overlay.h:
+ * ges/ges-track-video-title-source.c:
+ * ges/ges-track-video-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ remove GESTrackTitleSource
+
+2010-06-30 16:47:29 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-source.h:
+ fix doc comments
+
+2010-06-30 16:47:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-track-video-title-source.c:
+ * ges/ges-track-video-title-source.h:
+ GESTrackVideoTitleSource inherits directly from GESTrackObject
+
+2010-06-30 16:34:47 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-track-audio-background-source.h:
+ * ges/ges-track-background-source.c:
+ * ges/ges-track-background-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ remove GESTrackBackgroundSource class
+
+2010-06-30 16:29:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-background-source.c:
+ * ges/ges-track-audio-background-source.h:
+ GESTrackAudioBackgroundSource inherits from GESTrackSource
+
+2010-06-30 16:25:01 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-background-source.c:
+ * ges/ges-track-video-background-source.h:
+ GESTrackVideoBackgroundSource inherits directly from track object
+
+2010-06-30 15:40:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ move create_element virtual method up to TimelineSource class
+
+2010-06-30 15:39:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ fix documentation mistake
+
+2010-06-30 13:22:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-audio-title-source.c:
+ * ges/ges-track-audio-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ remove GESTrackAudioTitleSource
+
+2010-06-28 18:24:12 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ create test track object instead of audio-title-source
+
+2010-06-28 18:23:37 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ keep track of and release request pads for smpte also
+
+2010-06-28 18:20:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-transition.c:
+ free mixer in dispose separately from sink pads
+
+2010-06-28 17:33:53 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/.gitignore:
+ examples: Ignore files
+
+2010-06-28 17:33:34 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/.gitignore:
+ check: Ignore files
+
+2010-06-28 17:24:25 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ docs: Add links to all new documentation files
+
+2010-06-28 17:23:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/overlays.c:
+ tests: Add <stdlib.h> for exit usage
+
+2010-06-25 12:04:47 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-overlay.c:
+ don't forget to unref pad targets
+
+2010-06-23 18:23:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-overlay.c:
+ rough overlay implementation
+
+2010-06-23 18:22:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/overlays.c:
+ check in overlay test app
+
+2010-06-23 16:42:14 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-overlay.c:
+ activate property setting functions
+
+2010-06-23 16:38:45 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/overlays.c:
+ activate remaining overlay tests
+
+2010-06-23 16:38:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges.h:
+ add video overlays to main header
+
+2010-06-23 16:32:25 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-overlay.c:
+ create timeline-overly creates appropriate track object
+
+2010-06-23 16:30:18 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-video-overlay.c:
+ * ges/ges-track-video-overlay.h:
+ * ges/ges-types.h:
+ check in GESTrackVideoOverlay
+
+2010-06-21 16:22:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-track-overlay.c:
+ * ges/ges-track-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in ges-track-overlay.{c,h}
+
+2010-06-21 16:04:22 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/overlays.c:
+ check in overlay unit tests
+
+2010-06-21 15:47:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-overlay.c:
+ * ges/ges-timeline-overlay.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in timelineoverlay, structural copy of GESTimelineTitleSource
+
+2010-06-21 16:04:50 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.h:
+ update doc comment
+
+2010-06-18 16:36:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-transition.c:
+ remove unneeded paramenter to create_element
+
+2010-06-18 16:26:24 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ update unit tests
+
+2010-06-18 16:22:38 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ update documentation
+
+2010-06-18 16:22:21 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ push struct fields down to VideoTransition
+
+2010-06-18 15:54:37 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-video-transition.c:
+ push make_video_bin() down into subclass
+
+2010-06-18 15:21:02 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ push relevant struct fields into AudioTransition
+
+2010-06-18 15:20:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ update documentation
+
+2010-06-18 15:04:50 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-transition.c:
+ push make_audio_bin down into subclass
+
+2010-06-18 13:42:47 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ add duration_changed virtual method to GESTrackTransition
+
+2010-06-18 12:55:30 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ add create_element vmethod to GESTrackTransition
+
+2010-06-18 11:50:08 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ create transition subtype according to track type
+
+2010-06-18 11:24:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-track-video-transition.c:
+ * ges/ges-track-video-transition.h:
+ * ges/ges-types.h:
+ check in GESTrackVideoTransition, empty subclass of TrackTransition
+
+2010-06-18 11:09:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-audio-transition.c:
+ * ges/ges-track-audio-transition.h:
+ * ges/ges-types.h:
+ check in GESTrackAudioTransition, empyt subclass of TrackTransition
+
+2010-06-17 18:31:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-title-source.c:
+ * ges/ges-track-video-title-source.h:
+ fix header file param names
+
+2010-06-17 12:25:27 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-title-source.c:
+ fix stupid copy/paste typo
+
+2010-06-17 11:22:30 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-video-title-source.c:
+ free existing strings before assigning new ones
+
+2010-06-17 11:21:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/titles.c:
+ clean up some memory leaks in the titles unit test
+
+2010-06-16 19:04:53 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/titles.c:
+ unit tests for {h,v}alignment properties
+
+2010-06-16 19:03:51 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ implement {h,v}alignment property for timeline titles
+
+2010-06-16 19:02:40 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ documentation
+
+2010-06-16 19:01:48 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-title-source.c:
+ * ges/ges-track-video-title-source.h:
+ add ..._set_{h,v}alignment() methods to video titles
+
+2010-06-16 16:58:42 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ add font-desc property to TimelineTitleSource
+
+2010-06-16 16:58:13 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-video-title-source.c:
+ * ges/ges-track-video-title-source.h:
+ add ...set_font_desc() method to VideoTitleSource
+
+2010-06-16 13:27:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-title-source.c:
+ set black background on titles by default
+
+2010-06-16 13:22:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ add title sources to ges-launch
+
+2010-06-16 13:21:19 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ have timeline title source create audio title sources
+
+2010-06-16 13:20:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-track-audio-title-source.c:
+ * ges/ges-track-audio-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in ges-track-audio-title-source.{c,h}
+
+2010-06-15 19:22:04 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-title-source.c:
+ don't forget to check for null
+
+2010-06-15 19:21:37 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ set text on video track objects when text property changes
+
+2010-06-15 19:20:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ implement ges_timeline_title_source_create_track_object
+
+2010-06-15 17:10:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-background-source.h:
+ remove trailing '$' accidentally pasted
+
+2010-06-15 17:09:50 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ update documentation
+
+2010-06-15 17:09:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-title-source.c:
+ * ges/ges-track-video-title-source.h:
+ these should have been checked in before
+
+2010-06-15 13:16:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-title-source.c:
+ add text property to GESTimelineTitleSource
+
+2010-06-15 13:14:14 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/titles.c:
+ check in unit tests for titles
+
+2010-06-14 19:19:23 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-timeline-background-source.h:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-audio-background-source.c:
+ * ges/ges-track-audio-background-source.h:
+ * ges/ges-track-background-source.c:
+ * ges/ges-track-background-source.h:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-track-video-background-source.c:
+ * ges/ges-track-video-background-source.h:
+ massive update to doc comments
+
+2010-06-14 19:18:46 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-track-video-background-source.h:
+ remove the zone plate and gamut enum values
+
+2010-06-14 17:52:29 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ massive update to documentation
+
+2010-06-14 17:52:09 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-track-video-background-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in skeletal GESTrackVideoTitleSource
+
+2010-06-14 15:34:08 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-track-title-source.c:
+ * ges/ges-track-title-source.h:
+ * ges/ges-types.h:
+ check in skeletal GESTrackTitleSource
+
+2010-06-14 13:31:15 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-timeline-title-source.c:
+ * ges/ges-timeline-title-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ check in sekeletal GESTimelineTitleSource
+
+2010-06-11 17:57:20 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/backgroundsource.c:
+ don't forget to unref objects in unit tests
+
+2010-06-11 17:21:45 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges.h:
+ * tests/check/ges/backgroundsource.c:
+ test vpatern property in unit tests
+
+2010-06-11 17:02:55 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-background-source.c:
+ make audio background-sources output silence
+
+2010-06-11 16:55:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ use ges_timeline_background_source_new_for_nick when creating pattern sources
+
+2010-06-11 16:53:03 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ don't initialize vpattern field
+
+2010-06-11 16:51:44 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-timeline-background-source.h:
+ implement ges_timeline_background_source_new_for_nick()
+
+2010-06-11 16:50:07 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ move enum table into file scope
+
+2010-06-11 15:28:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ rewrite print_pattern_list to use GEnumValues
+
+2010-06-11 15:28:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ remove unnecessary g_print
+
+2010-06-11 15:19:28 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ switch to using GESTimelineBackgroundSource objects for patterns
+
+2010-06-11 15:18:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ set pattern on newly-created video track objects
+
+2010-06-11 15:17:42 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ implement vpattern gobject property of tl background source
+
+2010-06-11 15:16:40 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ add big blob of pattern enum values copied from videotestsrc
+
+2010-06-11 15:15:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.h:
+ add vpattern field to GESTimelineBackground source
+
+2010-06-11 15:14:40 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-background-source.c:
+ * ges/ges-track-video-background-source.h:
+ implement setting pattern on video background sources
+
+2010-06-11 13:44:40 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-background-source.c:
+ * ges/ges-track-video-background-source.h:
+ add routines to set track object pattern
+
+2010-06-11 13:41:44 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-background-source.h:
+ add enum for video patterns
+
+2010-06-11 13:40:54 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/backgroundsource.c:
+ test Backgroudn sources in layers
+
+2010-06-11 10:42:00 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ implment GESTimelineBackground source create_track_object
+
+2010-06-11 10:40:02 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ add audio background source to build system
+
+2010-06-11 10:39:14 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-types.h:
+ add audio background source to types.h
+
+2010-06-11 10:37:49 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-audio-background-source.c:
+ * ges/ges-track-audio-background-source.h:
+ check in audio background source
+
+2010-06-10 17:44:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-types.h:
+ add ges video-track background to build system
+
+2010-06-10 13:21:47 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-video-background-source.c:
+ * ges/ges-track-video-background-source.h:
+ check in ges-track-video-background-source.{c,h}
+
+2010-06-10 13:29:22 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ add GESTrackBackgroundSource to build system
+
+2010-06-10 17:42:09 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges.h:
+ add track background source to header
+
+2010-06-10 17:43:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-types.h:
+ add track background source to ges-types.h
+
+2010-06-10 13:51:33 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-background-source.c:
+ * ges/ges-track-background-source.h:
+ check in GESTrackBackgroundSource
+
+2010-06-10 13:23:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-types.h:
+ add GESTimelineBackgroundSource to build system
+
+2010-06-10 17:41:57 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges.h:
+ add timeline background source to header
+
+2010-06-10 13:22:36 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/backgroundsource.c:
+ check in background source unit test
+
+2010-06-10 13:21:06 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-background-source.c:
+ * ges/ges-timeline-background-source.h:
+ check in ges-timeline-background-source.{c,h}
+
+2010-06-28 17:23:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/simple1.c:
+ * tests/examples/transition.c:
+ tests: Add <stdlib.h> for exit usage
+
+2010-07-07 01:21:38 -0300 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ * tests/examples/simple1.c:
+ * tests/examples/transition.c:
+ * tools/ges-launch.c:
+ Fix building issues
+ Adds missing headers to some files and needed cflags to gtk-doc
+ scanner build
+
+2010-06-28 17:08:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Fix leaked caps
+
+2010-06-21 11:54:01 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: unref all pads
+
+2010-06-21 11:53:30 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Make a copy of the provided GstEncodingProfile
+
+2010-06-21 11:52:49 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Properly release playsink and encodebin
+
+2010-06-21 11:52:01 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: finalize => dispose
+ We want to release our objects before the parent GstBin class does so.
+
+2010-06-21 11:47:44 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Don't leak caps
+
+2010-06-21 11:47:21 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Properly free profile and outputuri
+
+2010-06-17 11:45:27 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * tools/ges-launch.c:
+ ges-launch: Cleanup profile
+
+2010-06-14 19:40:50 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-track-transition.c:
+ GESTrackTransition: Release all pads.
+ Whether calling get_request_pad or get_static_pad we always end up
+ with an extra reference.
+ Also keep a reference on videomixer so it doesn't go away before we
+ call _release_request_pad() on it with the proper pads to release.
+
+2010-06-14 19:12:42 +0200 Edward Hervey <bilboed@bilboed.com>
+
+ * common:
+ Update to latest common
+
+2010-06-11 19:34:39 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ examples: Fix debug statement
+
+2010-06-10 16:19:11 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ release the request pads in dispose
+
+2010-06-10 16:14:20 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ don't keep an extra reference to vsmpte
+
+2010-06-10 12:52:41 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ add elemt. to bin in arbitrary_fill_track_func
+
+2010-06-09 18:57:59 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ fix typos in comment block
+
+2010-06-09 18:56:55 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ gstl_recalculate() won't set priorities to -1
+
+2010-06-09 16:35:17 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-transition.h:
+ ges/ges-track-transition.h: add missing function prototype
+
+2010-06-09 17:11:56 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tools/ges-launch.c:
+ tools/ges-launch.c: C90 fixes
+
+2010-06-09 17:09:10 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: C90 fixes
+
+2010-06-09 17:08:31 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/test4.c:
+ tests/examples/test4.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/examples/concatenate.c:
+ tests/check/ges/concatenate.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ tests/check/ges/transition.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ tests/check/ges/simplelayer.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * tests/check/ges/filesource.c:
+ tests/check/ges/filesource.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-utils.h:
+ ges/ges-utils.h: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track.h:
+ ges/ges-track.h: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track.c:
+ ges/ges-track.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ ges/ges-track-object.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ ges/ges-timeline-transition.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ ges/ges-timeline-object.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ ges/ges-timeline-layer.c: C90 fixes
+
+2010-06-09 16:27:43 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ ges/ges-simple-timeline-layer.c: C90 fixes
+
+2010-06-09 13:53:32 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: Actually use the WARNING/ERROR CFLAGS
+ We weren't detecting all these issues previously
+
+2010-06-09 13:53:07 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ GESTrackTransition: Fix debug statement
+
+2010-06-09 13:52:35 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Remove all tracks/layers when being disposed
+
+2010-06-09 13:52:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ GESTimelineLayer: Release all layer/tracks when being disposed
+
+2010-06-09 11:22:05 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: set referece to vsmpte to NULL after freeing
+
+2010-06-09 11:21:26 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: was freeing same GstController twice in _dispose()
+
+2010-06-09 11:17:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ common: Update to latest submodule revision
+
+2010-06-08 18:38:44 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ check: Check that all objects are removed from the layer
+ This currently fails
+
+2010-06-08 18:37:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ check: Use release_track_object instead of unref
+
+2010-06-08 18:37:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ GESTrackTransition: Unref the ControlSource in dispose
+
+2010-06-08 18:36:37 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges.c:
+ ges: Initialize GstController in ges_init
+
+2010-06-04 19:53:35 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ ges/ges-simple-timeline-layer.c: print a warning when transitions overlap
+
+2010-06-04 18:31:25 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ ges/ges-timeline-transition.c: can't set enums by nick
+
+2010-06-04 18:07:39 +0200 Brandon Lewis <brandon@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ ges/ges-timeline-transition.c: initialize vtype enum type from static list of GEnumValues
+
+2010-06-04 17:53:15 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/.gitignore:
+ * tools/.gitignore:
+ tools/examples: Ignore more files
+
+2010-06-04 17:50:42 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * Makefile.am:
+ * configure.ac:
+ * tests/examples/Makefile.am:
+ * tools/Makefile.am:
+ * tools/ges-launch.c:
+ tools: Moving playlist from examples and making it installable
+ It is now called ges-launch
+
+2010-06-04 12:17:56 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: allow file / pattern durations to be 0 (but not transitions)
+
+2010-06-04 12:17:28 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: clean up playlist help text
+
+2010-06-03 19:14:41 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: better sanity checking of arguments
+
+2010-06-03 19:13:42 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: add option to print avail. transitions/patterns. update help strings
+
+2010-06-03 19:04:11 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/transition.c:
+ tests/check/ges/{simplelayer.c,transition.c}: update unit tests
+
+2010-06-03 19:02:58 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: adjust to the change in VTYPE_CROSSFADE
+
+2010-06-03 19:01:21 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ ges/ges-timeline-transition.{c,h}: value for VTYPE_CROSSFADE changed to 512 and exported in ges-timeline-transition.h
+
+2010-06-02 18:58:14 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ tests/check/ges/transition.c: test that changing timeline vtype sets trackobj vtype
+
+2010-06-02 18:57:10 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ ges/ges-timeline-transition.c: implement vtype gobject property on GESTimelineTransitions
+
+2010-06-02 18:55:52 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ ges/ges-track-transition.{c,h}: add ability to change smptealpha type
+
+2010-06-02 16:52:02 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/transition.c:
+ tests/check/ges/{simplelayer.c,tests/check/ges/transition.c}: sync with previous api change
+
+2010-06-02 16:50:07 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ ges/ges-timeline-transition.{c,h}: api change, pass gint instead of GEnumValue to new()
+
+2010-06-02 16:43:10 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * tests/check/ges/transition.c:
+ ges/ges-timeline-transition.{c,h},tests/.../transition.c: type change of vtype to gint from GEnumValue
+
+2010-06-02 16:35:57 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ ges/ges-timeline-transition.c: sync with API change in previous commit
+
+2010-06-02 16:27:58 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ ges/ges-track-transition.{c,h}: api change: pass gint instead of GEnumValue
+
+2010-06-02 15:18:55 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * tests/check/ges/transition.c:
+ ges/ges-track-transition.{c,h}, tests/check/ges/transition.c: change vtype from GEnumValue to simple gint;
+
+2010-06-02 13:50:06 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ tests/check/ges/transition.c: make sure unit tests work properly
+
+2010-06-02 13:20:09 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ tests/check/ges/transition.c: oops, unit tests using wrong api
+
+2010-06-02 12:46:05 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ tests/check/Makefile.am: add transition unit tests to make check
+
+2010-06-02 12:34:57 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/transition.c:
+ tests/check/ges/transition.c: check in transition unit tests
+
+2010-06-01 13:22:05 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-filesource.h:
+ ges/ges-track-filesource.h: fix typo in documentation commments
+
+2010-06-01 11:57:42 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-filesource.h:
+ ges/ges-track-filesource.h: fix incorrect definition of GESTrackFileSource structs.
+
+2010-05-31 18:59:12 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-object.h:
+ * ges/ges-track-source.h:
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ * ges/ges-track.h:
+ Add missing documentation
+
+2010-05-31 15:42:23 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ * tests/examples/transition.c:
+ tests/check/ges/{simplelayer.c,transition.c}: create audio tracks in demos
+
+2010-05-31 15:40:52 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: implement audio crossfades
+
+2010-05-31 15:38:14 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ ges/ges-track-transition.{c,h}: add fields for audio interpolation to obj
+
+2010-05-28 11:42:29 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * tests/check/ges/simplelayer.c:
+ fix bugs
+
+2010-05-28 03:02:49 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ ges/ges-track-transition.{c,h}: rename some members of ges-track-transition struct to separate between audio and video objects.
+
+2010-05-28 02:31:42 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: factor out code which produces video bin into a seprate routine
+
+2010-05-28 00:19:24 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: make audio stream of audiotestsrc silent (it's much less annoying).
+
+2010-05-28 00:16:28 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: fix inappropriate down-casts in playlist.py
+
+2010-05-28 00:12:45 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges/ges-track-transition.c: give gnloperations a unique name
+
+2010-05-28 00:11:51 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ ges/ges-simple-timeline-layer.c: also error when transition duration exceeds that of its neighbors
+
+2010-05-27 23:37:11 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ ges/ges-simple-timeline-layer.c: don't allow user to create timelines with adjacent transitions
+
+2010-05-27 23:36:10 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ ges/ges-simple-timeline-layer.c: implement simple stair-step like priority management scheme.
+
+2010-05-27 23:10:04 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ tests/check/ges/simplelayer.c: check in massive unit test case for GSTL with transitions
+
+2010-05-27 12:06:00 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: add transitions to playlist example
+
+2010-05-27 12:04:05 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: use ges_timeline_transition_new_from_nick()
+
+2010-05-27 12:02:10 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ ges/ges-timeline-transition.{c,h}: add convenience routine for creating transitions docs/libs/ges-sections.txt: add routine to documentation
+
+2010-05-26 18:19:41 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ tests/examples/playlist.c: re-work pattern command line syntax
+
+2010-05-26 16:57:59 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ ests/examples/transition.c: create transition with specified type
+
+2010-05-26 16:36:24 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ ges/ges-track-transition.{c,h}: add support for other wipes with smptealpha
+
+2010-05-26 16:33:44 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ ges/ges-timeline-transition.{c,h}: add a type field
+
+2010-05-26 13:27:46 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: pass transition type to make_timeline
+
+2010-05-26 13:05:18 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: make -t option work with values supported by smpte + "crossfade"
+
+2010-05-26 13:04:06 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: add routines for identifying transitions
+
+2010-05-26 11:38:19 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: split out "make_timeline" into separate routine
+
+2010-05-26 10:48:13 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: remove some cruft from transition example
+
+2010-05-25 19:10:27 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: print values in seconds not nseconds
+
+2010-05-25 19:07:21 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges-track-transition.c: set interpolation control points properly from gnlobject properties
+
+2010-05-25 19:06:10 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.h:
+ ges-track-transition.c: GstControlSource -> GstInterpolationControlSource
+
+2010-05-25 16:44:58 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: cast arguments to g_object_set
+
+2010-05-25 16:42:47 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ ges-track-transition.c: create gst-controller for transition
+
+2010-05-25 16:41:53 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ ges-track-transition.c: add controller and control-source members
+
+2010-05-25 16:35:16 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ depend on GST_CONTROLLER
+
+2010-05-25 13:44:57 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-track-transition.c:
+ * tests/examples/transition.c:
+ hacking
+
+2010-05-24 17:51:31 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ tests/examples/transition.c: create transition when duration > 0
+
+2010-05-24 17:39:45 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ dist transition.c
+
+2010-05-24 17:39:07 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/transition.c:
+ check in single transition demo
+
+2010-05-24 14:58:55 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-types.h:
+ ges/ges-types.h: add typedefs for GESTrackTransition[Class] structs
+
+2010-05-24 14:57:12 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.h:
+ ges-track-transition.h: fix typo
+
+2010-05-24 14:55:53 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/Makefile.am:
+ ges/Makefile.am: dist ges-track-transition.c,h
+
+2010-05-24 13:08:32 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.c:
+ * ges/ges-track-transition.h:
+ skeletal implementation of GESTrackTransition
+
+2010-05-24 12:34:36 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * ges/ges-track-transition.h:
+ check-in ges-track-transition.h
+
+2010-05-24 10:59:43 +0200 Brandon Lewis <brandon.lewis@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ playlist.c: working pattern sources
+
+2010-06-02 11:49:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ examples: Add a looping feature to playlist example
+ Allows playing the timeline repeatedly a certain number of times
+
+2010-05-25 16:22:58 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Freeze state of Tracks when doing an async state change
+
+2010-05-20 10:46:38 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/.gitignore:
+ * tests/examples/Makefile.am:
+ * tests/examples/concatenate.c:
+ examples: New concatenate examples.
+ Allows concatenating several files of the same type together
+
+2010-05-20 10:44:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-utils.c:
+ * ges/ges-utils.h:
+ * ges/ges.h:
+ GES: Add a new utility file
+ API : ges_timeline_new_audio_video()
+
+2010-05-11 15:03:33 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/.gitignore:
+ examples: Ignore some files
+
+2010-05-20 12:29:30 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Only remove the playsink if it was used
+
+2010-05-19 15:50:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ docs: Use proper CFLAGS/LIBS when building docs
+
+2010-05-19 15:50:41 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Remove unused variable
+
+2010-05-19 12:39:23 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/.gitignore:
+ * tests/examples/.gitignore:
+ tests: ignore more files
+
+2010-05-19 12:38:21 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ examples: Allow setting null duration on files
+ This will make the timeline use GstDiscoverer to analyze the file.
+
+2010-05-19 12:36:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ GESTimeline: Use GstDiscoverer for incomplete filesources
+ If a GESTimelineFileSource is added to a layer and:
+ * It doesn't have specified supported formats
+ * OR it doesn't have a specified maximum duration
+ * OR it doesn't have a specifed duration
+ Then we asynchronously send it to the GstDiscoverer.
+ If this happens, the state change of the timeline from READY to
+ PAUSED will happen asynchronously and be completed when everything
+ has been properly discovered.
+ Part 2 of GstDiscoverer integration
+
+2010-05-19 12:24:44 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * tests/check/ges/filesource.c:
+ GESTimelineFileSource: Add 'max-duration' and 'supported-formats' properties
+ * max-duration is the total length of the File.
+ * supported-formats is the various track types this filesource can produce
+ trackobjects for. This should maybe be moved to parent classes in the
+ future
+ Step 1 of GstDiscoverer integration
+
+2010-05-19 12:19:37 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: Properly set default duration
+ Set it in the instance_init to GST_SECOND, But let the subclasses override
+ it.
+ This allows subclasses to set a different default duration
+
+2010-05-19 12:14:34 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ GESSimpleTimelineLayer: Recalculate positions when object duration change
+ This ensures that if someone changes the duration, the clips still remain
+ contiguous and in the proper order.
+
+2010-05-18 19:07:27 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * ges/Makefile.am:
+ configure: Depend on gstreamer-discoverer
+
+2010-05-18 17:43:28 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ GESTrack: Make GESTrackType a flag, and add GES_TRACK_TYPE_UNKNOWN
+ Also add a bit more documentation about it.
+
+2010-05-18 15:19:06 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/.gitignore:
+ * docs/libs/doc-registry.xml:
+ * docs/libs/ges-decl-list.txt.bak:
+ * docs/libs/ges-decl.txt.bak:
+ * docs/libs/html/GESCustomTimelineSource.html:
+ * docs/libs/html/GESSimpleTimelineLayer.html:
+ * docs/libs/html/GESTimeline.html:
+ * docs/libs/html/GESTimelineFileSource.html:
+ * docs/libs/html/GESTimelineLayer.html:
+ * docs/libs/html/GESTimelineObject.html:
+ * docs/libs/html/GESTimelinePipeline.html:
+ * docs/libs/html/GESTimelineSource.html:
+ * docs/libs/html/GESTimelineTransition.html:
+ * docs/libs/html/GESTrack.html:
+ * docs/libs/html/GESTrackFileSource.html:
+ * docs/libs/html/GESTrackObject.html:
+ * docs/libs/html/GESTrackSource.html:
+ * docs/libs/html/api-index-full.html:
+ * docs/libs/html/architecture.xml:
+ * docs/libs/html/ch01.html:
+ * docs/libs/html/ch02.html:
+ * docs/libs/html/ch03.html:
+ * docs/libs/html/ch04.html:
+ * docs/libs/html/ges-Initialization.html:
+ * docs/libs/html/ges-architecture.html:
+ * docs/libs/html/ges-hierarchy.html:
+ * docs/libs/html/ges.devhelp:
+ * docs/libs/html/ges.devhelp2:
+ * docs/libs/html/home.png:
+ * docs/libs/html/index.html:
+ * docs/libs/html/index.sgml:
+ * docs/libs/html/layer_track_overview.png:
+ * docs/libs/html/left.png:
+ * docs/libs/html/right.png:
+ * docs/libs/html/style.css:
+ * docs/libs/html/up.png:
+ * docs/libs/tmpl/ges-common.sgml:
+ * docs/libs/tmpl/ges-common.sgml.bak:
+ * docs/libs/tmpl/ges-custom-timeline-source.sgml:
+ * docs/libs/tmpl/ges-custom-timeline-source.sgml.bak:
+ * docs/libs/tmpl/ges-simple-timeline-layer.sgml:
+ * docs/libs/tmpl/ges-simple-timeline-layer.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-filesource.sgml:
+ * docs/libs/tmpl/ges-timeline-filesource.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-layer.sgml:
+ * docs/libs/tmpl/ges-timeline-layer.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-object.sgml:
+ * docs/libs/tmpl/ges-timeline-object.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-pipeline.sgml:
+ * docs/libs/tmpl/ges-timeline-pipeline.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-source.sgml:
+ * docs/libs/tmpl/ges-timeline-source.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-transition.sgml:
+ * docs/libs/tmpl/ges-timeline-transition.sgml.bak:
+ * docs/libs/tmpl/ges-timeline.sgml:
+ * docs/libs/tmpl/ges-timeline.sgml.bak:
+ * docs/libs/tmpl/ges-track-filesource.sgml:
+ * docs/libs/tmpl/ges-track-filesource.sgml.bak:
+ * docs/libs/tmpl/ges-track-object.sgml:
+ * docs/libs/tmpl/ges-track-object.sgml.bak:
+ * docs/libs/tmpl/ges-track-source.sgml:
+ * docs/libs/tmpl/ges-track-source.sgml.bak:
+ * docs/libs/tmpl/ges-track.sgml:
+ * docs/libs/tmpl/ges-track.sgml.bak:
+ * docs/libs/tmpl/ges-unused.sgml:
+ * docs/libs/xml/api-index-deprecated.xml:
+ * docs/libs/xml/api-index-full.xml:
+ * docs/libs/xml/ges-common.xml:
+ * docs/libs/xml/ges-custom-timeline-source.xml:
+ * docs/libs/xml/ges-doc.bottom:
+ * docs/libs/xml/ges-doc.top:
+ * docs/libs/xml/ges-simple-timeline-layer.xml:
+ * docs/libs/xml/ges-timeline-filesource.xml:
+ * docs/libs/xml/ges-timeline-layer.xml:
+ * docs/libs/xml/ges-timeline-object.xml:
+ * docs/libs/xml/ges-timeline-pipeline.xml:
+ * docs/libs/xml/ges-timeline-source.xml:
+ * docs/libs/xml/ges-timeline-transition.xml:
+ * docs/libs/xml/ges-timeline.xml:
+ * docs/libs/xml/ges-track-filesource.xml:
+ * docs/libs/xml/ges-track-object.xml:
+ * docs/libs/xml/ges-track-source.xml:
+ * docs/libs/xml/ges-track.xml:
+ * docs/libs/xml/object_index.sgml:
+ * docs/libs/xml/tree_index.sgml:
+ * docs/libs/xml/version.entities:
+ docs: And remove all the stuff that's meant to be generated at runtime
+
+2010-05-18 12:56:24 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/doc-registry.xml:
+ * docs/libs/ges-decl-list.txt.bak:
+ * docs/libs/ges-decl.txt.bak:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/html/GESCustomTimelineSource.html:
+ * docs/libs/html/GESSimpleTimelineLayer.html:
+ * docs/libs/html/GESTimeline.html:
+ * docs/libs/html/GESTimelineFileSource.html:
+ * docs/libs/html/GESTimelineLayer.html:
+ * docs/libs/html/GESTimelineObject.html:
+ * docs/libs/html/GESTimelinePipeline.html:
+ * docs/libs/html/GESTimelineSource.html:
+ * docs/libs/html/GESTimelineTransition.html:
+ * docs/libs/html/GESTrack.html:
+ * docs/libs/html/GESTrackFileSource.html:
+ * docs/libs/html/GESTrackObject.html:
+ * docs/libs/html/GESTrackSource.html:
+ * docs/libs/html/api-index-full.html:
+ * docs/libs/html/architecture.xml:
+ * docs/libs/html/ch01.html:
+ * docs/libs/html/ch02.html:
+ * docs/libs/html/ch03.html:
+ * docs/libs/html/ch04.html:
+ * docs/libs/html/ges-Initialization.html:
+ * docs/libs/html/ges-architecture.html:
+ * docs/libs/html/ges-hierarchy.html:
+ * docs/libs/html/ges.devhelp:
+ * docs/libs/html/ges.devhelp2:
+ * docs/libs/html/home.png:
+ * docs/libs/html/index.html:
+ * docs/libs/html/index.sgml:
+ * docs/libs/html/layer_track_overview.png:
+ * docs/libs/html/left.png:
+ * docs/libs/html/right.png:
+ * docs/libs/html/style.css:
+ * docs/libs/html/up.png:
+ * docs/libs/tmpl/ges-common.sgml:
+ * docs/libs/tmpl/ges-common.sgml.bak:
+ * docs/libs/tmpl/ges-custom-timeline-source.sgml:
+ * docs/libs/tmpl/ges-custom-timeline-source.sgml.bak:
+ * docs/libs/tmpl/ges-simple-timeline-layer.sgml:
+ * docs/libs/tmpl/ges-simple-timeline-layer.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-filesource.sgml:
+ * docs/libs/tmpl/ges-timeline-filesource.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-layer.sgml:
+ * docs/libs/tmpl/ges-timeline-layer.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-object.sgml:
+ * docs/libs/tmpl/ges-timeline-object.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-pipeline.sgml:
+ * docs/libs/tmpl/ges-timeline-pipeline.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-source.sgml:
+ * docs/libs/tmpl/ges-timeline-source.sgml.bak:
+ * docs/libs/tmpl/ges-timeline-transition.sgml:
+ * docs/libs/tmpl/ges-timeline-transition.sgml.bak:
+ * docs/libs/tmpl/ges-timeline.sgml:
+ * docs/libs/tmpl/ges-timeline.sgml.bak:
+ * docs/libs/tmpl/ges-track-filesource.sgml:
+ * docs/libs/tmpl/ges-track-filesource.sgml.bak:
+ * docs/libs/tmpl/ges-track-object.sgml:
+ * docs/libs/tmpl/ges-track-object.sgml.bak:
+ * docs/libs/tmpl/ges-track-source.sgml:
+ * docs/libs/tmpl/ges-track-source.sgml.bak:
+ * docs/libs/tmpl/ges-track.sgml:
+ * docs/libs/tmpl/ges-track.sgml.bak:
+ * docs/libs/tmpl/ges-unused.sgml:
+ * docs/libs/xml/api-index-deprecated.xml:
+ * docs/libs/xml/api-index-full.xml:
+ * docs/libs/xml/ges-common.xml:
+ * docs/libs/xml/ges-custom-timeline-source.xml:
+ * docs/libs/xml/ges-doc.bottom:
+ * docs/libs/xml/ges-doc.top:
+ * docs/libs/xml/ges-simple-timeline-layer.xml:
+ * docs/libs/xml/ges-timeline-filesource.xml:
+ * docs/libs/xml/ges-timeline-layer.xml:
+ * docs/libs/xml/ges-timeline-object.xml:
+ * docs/libs/xml/ges-timeline-pipeline.xml:
+ * docs/libs/xml/ges-timeline-source.xml:
+ * docs/libs/xml/ges-timeline-transition.xml:
+ * docs/libs/xml/ges-timeline.xml:
+ * docs/libs/xml/ges-track-filesource.xml:
+ * docs/libs/xml/ges-track-object.xml:
+ * docs/libs/xml/ges-track-source.xml:
+ * docs/libs/xml/ges-track.xml:
+ * docs/libs/xml/object_index.sgml:
+ * docs/libs/xml/tree_index.sgml:
+ * docs/libs/xml/version.entities:
+ * ges/ges-timeline-object.h:
+ GESTimelineObject: Document CreateTrackObjectFunc vmethod
+
+2010-05-18 12:32:31 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * docs/libs/scanobj-build.stamp:
+ docs: Make sure hierarchy/properties/signals get built for all classes
+
+2010-05-10 12:44:56 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Fix 32bit runtime issues
+
+2010-05-07 13:30:07 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Limit encodebin buffering to 1 buffer
+ We don't need to queue more than that since we only need thread decoupling
+ and the various streams going into encodebin are guaranteed to come
+ from different streaming threads (since they're separate gnlcomposition).
+
+2010-05-06 19:57:25 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/playlist.c:
+ examples: Add option to specify video restriction
+ Some encoders don't handle _get_caps() properly :(
+
+2010-04-27 11:45:15 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Remove unused variable/label.
+
+2010-04-20 13:41:20 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Cleanup properly when pads are removed
+
+2010-04-20 13:26:00 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Implement smart rendering
+
+2010-04-20 13:08:27 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/playlist.c:
+ examples: New playlist examples
+ Allows giving lists of file/start/duration triplets and testing the
+ various timeline-pipeline modes (preview, render, smart-render)
+
+2010-04-20 13:04:31 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ check: Use GST_CFLAGS so we get new compilation flags
+
+2010-04-20 13:00:38 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: Store encoding profile
+
+2010-04-20 12:59:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: New Smart Render mode
+
+2010-04-20 12:57:53 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: new functions to search/create OutputChain
+
+2010-04-20 12:53:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Remove unused code
+
+2010-04-20 12:50:34 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ GESTrack: set caps on the composition
+ This will allow them to be propagated to all objects contained within
+
+2010-04-20 12:50:09 +0200 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ GESTimeLine(PipeLine): remove additional unref
+
+2010-04-20 12:47:22 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac : round of cleanup
+ Add extra CFLAGS
+ Change GST_CVS to GST_GIT
+ Add -DGST_USE_UNSTABLE_API for gstprofile, since we know it's unstable.
+
+2010-04-20 12:28:59 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ ges: Link gstprofile
+
+2010-04-20 11:48:21 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ GESTimeline: New method ges_timeline_get_tracks
+
+2010-03-13 16:43:59 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/simple1.c:
+ examples: Simple Audio/Video example
+ Currently one can:
+ * Give a multimedia file
+ * modify the inpoint
+ * modify the duration
+ * mute the audio
+
+2010-03-13 16:05:37 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Fix minor bug in get_compatible_unlinked_pad
+ We weren't breaking and ended up doing a fallthrough to the loop
+ completion.
+
+2010-03-13 15:53:16 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-object.h:
+ GESTimelineObject: Fix doc of priority property
+
+2010-03-13 15:51:16 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-track-filesource.c:
+ GESTrackFileSource: Don't forget to free the URI string
+
+2010-03-12 19:07:15 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/layer.c:
+ tests: Add unit test for layer property.
+ Still needs more work though
+
+2010-03-12 19:06:42 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ GESTimelineLayer: Add a 'priority' property
+
+2010-03-12 18:42:28 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Properly iterate TrackObject lists when removing them
+
+2010-03-12 19:05:36 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * tests/check/ges/basic.c:
+ tests: Check refcount of created trackobjects
+
+2010-03-13 15:52:14 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * ges/ges-timeline-object.c:
+ * tests/check/ges/filesource.c:
+ GESTimelineObject: Don't leak a reference when creating TrackObject
+
+2010-03-12 17:17:30 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ GESTimelineObject: new API : _find_track_object
+ This allows getting the TrackObject for a corresponding TimelineObject
+ and Track combination
+
+2010-03-12 17:08:00 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * Makefile.am:
+ * configure.ac:
+ configure: use automake 1. 11 silent rules instead of shave if available
+
+2010-03-12 17:09:03 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * common:
+ common: Update to latest common
+
+2010-03-11 11:06:50 +0100 Edward Hervey <bilboed@bilboed.com>
+
+ * .gitignore:
+ * tests/check/ges/.gitignore:
+ * tests/examples/.gitignore:
+ ignore more files
+
+2010-03-05 16:10:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ examples: Fix linking/include order
+
+2010-03-05 15:50:49 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/filesource.c:
+ check: Add a test for checking timelinefilesource properties
+ This include the mute feature
+
+2010-02-09 17:45:42 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ examples: Use profile LIBS
+ and fix a typo with GST_LIBS
+
+2010-02-09 17:44:54 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: gst-profile is now a standalone pkgconfig
+
+2010-01-20 11:09:56 +0100 Jarkko Pallviainen <ext-jarkko.palviainen@nokia.com>
+
+ * tests/examples/test1.c:
+ examples: Fix build on 32bit systems
+
+2010-01-08 18:21:08 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Add comment for _set_render_settings
+
+2010-01-08 18:16:16 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/test4.c:
+ examples: test4: variant of test3 with rendering.
+ Usage: ./test4 output_uri audio_files
+ This will render in ogg/vorbis the first seconds of all the provided
+ audio files to the output_uri
+ Ex : ./test4 file:///data/audio1s.ogg /data/music/*.ogg
+
+2010-01-08 18:14:46 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Also get static pads for static pads from encodebin.
+ This is for the cases where the provided GstStreamEncodingProfile has
+ a non-zero presence.
+
+2010-01-08 17:05:01 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: detect gst-convenience
+
+2009-12-11 15:24:56 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-track-object.h:
+ ges: Small doc fixups
+
+2009-12-11 15:17:02 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/timelineobject.c:
+ tests: Fix macro by making it use the proper argument types
+
+2009-12-11 15:16:26 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ tests: Fix linking order.
+ This ensures that "make check" will run with the local libraries and not
+ the system-wide ones
+
+2009-12-11 15:15:29 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: Move property setting to an earlier stage.
+ This ensures that any properties set on the TimelineObject will be
+ propagated to the created TrackObjects just after they're created
+
+2009-12-11 15:13:19 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: Store pending values when GnlObject isn't created yet
+
+2009-12-11 15:17:25 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Sync state of newly added element to container
+
+2009-12-09 15:03:30 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Don't forget to remember the mode
+
+2009-12-09 15:03:15 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ GESTimelineFileSource: Properly handle mute
+ mute != active
+
+2009-12-09 12:22:34 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: More render support
+
+2009-12-04 10:49:32 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: beginning of render support
+
+2009-11-30 15:14:25 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-internal.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-filesource.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * ges/ges-types.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ ges/: Fix copyright in headers
+
+2009-11-30 15:14:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * AUTHORS:
+ AUTHORS: Add myself
+
+2009-11-25 13:13:49 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ timelinefilesource: Free URI when finalizing
+
+2009-11-25 13:11:32 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ trackobject: priority is a uint32
+
+2009-11-25 12:53:13 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/timelineobject.c:
+ tests: release TrackObject when we're done with it
+
+2009-11-25 12:52:50 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/simplelayer.c:
+ * tests/check/ges/timelineobject.c:
+ tests: Don't forget to cast to guint64 when using g_object_set
+ ... else total failure ensues on 32bit machines
+
+2009-11-25 11:56:58 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ TimelineObject: Add missing argument to printf statement
+
+2009-11-25 11:55:50 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ customtimelinesource: Fix indentation
+
+2009-11-25 11:14:02 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/working-diagrams.svg:
+ docs: updates to working diagram, still needs more love
+
+2009-11-15 18:23:33 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * docs/libs/Makefile.am:
+ * docs/libs/architecture.xml:
+ * docs/libs/layer_track_overview.png:
+ * docs/working-diagrams.svg:
+ docs: Improve docs some more
+
+2009-11-12 20:11:28 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ * docs/libs/Makefile.am:
+ * docs/libs/architecture.xml:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/ges.types:
+ * ges/ges.c:
+ docs: Add overview and architecture document and cleanup docs more.
+
+2009-11-12 19:14:35 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/design/gstprofile.h:
+ docs: Update to gstprofile header
+
+2009-11-09 15:55:06 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitignore:
+ ignore more files
+
+2009-11-09 15:54:18 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * m4/Makefile.am:
+ * m4/codeset.m4:
+ * m4/gettext.m4:
+ * m4/glibc2.m4:
+ * m4/glibc21.m4:
+ * m4/iconv.m4:
+ * m4/intdiv0.m4:
+ * m4/intl.m4:
+ * m4/intldir.m4:
+ * m4/intlmacosx.m4:
+ * m4/intmax.m4:
+ * m4/inttypes-pri.m4:
+ * m4/inttypes_h.m4:
+ * m4/lcmessage.m4:
+ * m4/lib-ld.m4:
+ * m4/lib-link.m4:
+ * m4/lib-prefix.m4:
+ * m4/libtool.m4:
+ * m4/lock.m4:
+ * m4/longlong.m4:
+ * m4/ltoptions.m4:
+ * m4/ltsugar.m4:
+ * m4/ltversion.m4:
+ * m4/lt~obsolete.m4:
+ * m4/nls.m4:
+ * m4/po.m4:
+ * m4/printf-posix.m4:
+ * m4/progtest.m4:
+ * m4/size_max.m4:
+ * m4/stdint_h.m4:
+ * m4/uintmax_t.m4:
+ * m4/visibility.m4:
+ * m4/wchar_t.m4:
+ * m4/wint_t.m4:
+ * m4/xsize.m4:
+ remove m4/*.m4, will be automatically created by autogen.sh
+
+2009-11-05 10:22:57 +0100 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * common:
+ update common submodule version used
+
+2009-10-22 17:37:54 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ docs: Add missing symbol to documentation
+
+2009-10-19 18:32:23 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/design/encoding-research.txt:
+ * docs/design/encoding.txt:
+ * docs/design/gstprofile.h:
+ docs/design: Fixups/additions based on feedback
+
+2009-10-07 16:23:22 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/design/encoding-research.txt:
+ * docs/design/encoding.txt:
+ * docs/design/gstencodebin.h:
+ * docs/design/gstprofile.h:
+ docs/design: Add encoding/profile proposal/design
+
+2009-09-30 16:45:13 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/timelineobject.c:
+ tests: New unit test for GESTimelineObject
+
+2009-09-30 16:44:41 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ GESTimelineObject: First set the duration and priority before the inpoint.
+
+2009-09-30 16:44:12 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ GESTimelineFileSource: Add a 'mute' property.
+ This property deactivates the audio trackobjects if set to TRUE
+
+2009-09-30 16:43:12 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: Listen to property change from gnlobject
+ TODO: add/emit the 'changed' signal
+
+2009-09-30 16:42:31 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.h:
+ GESTrackObject: Add convenience macros for accessing properties
+
+2009-09-30 16:42:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ GESTrackObject: Set sane default values
+
+2009-09-30 16:40:59 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: Add 'active' property.
+ This property allows (de)activating a track object
+
+2009-09-29 15:32:23 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/test3.c:
+ tests/examples: test3, same as test2, but uses a SimpleTimelineLayer
+
+2009-09-29 15:29:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/simplelayer.c:
+ tests/check: Add test for GESSimpleTimelineLayer
+
+2009-09-29 15:27:55 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ GESSimpleTimelineLayer: Implement add()/move() and parent class changes
+ If changes happen when accessing the GESTimelineLayer API, they will be taken
+ into account.
+
+2009-09-29 15:27:17 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.h:
+ ges-timeline-layer.h: Add comment
+
+2009-09-29 15:25:54 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-timeline-object.h:
+ ges-timeline-object: Add convenience macros
+
+2009-09-21 18:11:19 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/Makefile.am:
+ * tests/examples/test2.c:
+ tests/example: New small example of timeline file sources.
+ This examples takes a list of files with audio tracks, and plays the first
+ second of each.
+ This demonstrates the usage of GESTimelineFileSource
+
+2009-09-21 18:08:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/Makefile.am:
+ * tests/check/ges/filesource.c:
+ tests/check: New test for GESTimelineFileSource
+
+2009-09-21 12:51:16 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/Makefile.am:
+ * ges/ges-timeline-file-source.c:
+ * ges/ges-timeline-file-source.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-track-filesource.c:
+ * ges/ges-track-filesource.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ New GESTimelineFileSource and GESTrackFileSource classes
+
+2009-09-16 12:37:45 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline.c:
+ Finish public API documentation
+
+2009-09-16 12:37:13 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ docs: Add index and object hierarchy
+
+2009-09-14 19:45:43 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests: Make basic test check for proper reference counting.
+
+2009-09-14 19:44:03 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ GESTrack: The track steals the refcount to the caps. document that.
+
+2009-09-14 19:42:58 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/.gitignore:
+ docs/libs: ignore more files
+
+2009-09-14 19:24:28 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Fix reference counting of tracks, add docs.
+
+2009-09-14 19:23:52 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ GESTimelineLayer: Fix reference handling of objects, add docs.
+
+2009-09-14 19:23:21 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ configure.ac: Require latest gst-plugins-base for 'playsink'
+
+2009-09-14 17:00:13 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.h:
+ GESTimeline: start more documentation
+
+2009-09-14 16:33:25 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ GESTrackObject: Document some more
+
+2009-09-14 15:51:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-sections.txt:
+ * ges/ges-track.c:
+ GESTrack: document more
+
+2009-09-10 18:17:38 +0100 Tim-Philipp Müller <tim.muller@collabora.co.uk>
+
+ * docs/libs/Makefile.am:
+ * docs/libs/ges.types:
+ docs: fix gtk-doc build and make distcheck for me
+
+2009-09-10 18:53:31 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/test1.c:
+ Document first high-level demo.
+
+2009-09-10 18:40:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-source.c:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * ges/ges.c:
+ More documentation. Coverage now at 25%
+
+2009-09-10 16:23:12 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/random/scenarios:
+ docs: move working document
+
+2009-09-10 16:22:00 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * Makefile.am:
+ * configure.ac:
+ * docs/Makefile.am:
+ * docs/libs/Makefile.am:
+ * docs/libs/ges-docs.sgml:
+ * docs/libs/ges-sections.txt:
+ * docs/libs/scanobj-build.stamp:
+ * docs/version.entities.in:
+ docs: Add gtk-doc API documentation
+ current coverage : 8%
+
+2009-09-09 15:53:53 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/test1.c:
+ test1: Expand example to also use an audio track.
+
+2009-09-09 15:51:52 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ GESTimeline: Make sure added ghostpads are unique.
+
+2009-09-09 13:57:37 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/test1.c:
+ test1: Actually change videotestsrc patterns to make changes obvious.
+
+2009-09-09 13:55:30 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/test1.c:
+ test1: Use mainloop, required for proper gnonlin behaviour.
+
+2009-09-09 12:42:29 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ GESTimelinePipeline: Reconfigure playsink once we've added a pad.
+
+2009-09-08 19:46:54 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/examples/test1.c:
+ examples: Add timeline to pipeline and set it to playing.
+ ... and now we need to go an fix playsink :)
+
+2009-09-08 19:46:26 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ test/ges/basic: Adapt to API changes.
+
+2009-09-08 19:45:08 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: Use 'playsink', track added/removed pads.
+ Also add a method to set a GESTimeline on the pipeline.
+
+2009-09-08 19:44:03 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ GESTimeline: Track internal pads and tracks in sync.
+ Add method to get the Track associated to a ghostpad.
+
+2009-09-08 18:55:41 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitignore:
+ gitignore: Ignore more files
+
+2009-09-08 18:49:22 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ GESTrack: Add TrackType enum and constructor property.
+ This allows us to speed up detection of track content type.
+
+2009-09-08 18:47:46 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ CustomTimelineSource: Fix typo in debug statement
+
+2009-09-07 15:46:44 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ GESTimelinePipeline: Fix typo
+
+2009-08-07 20:33:40 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ TrackObject: Add debugging and make default duration 1s
+
+2009-08-07 20:33:18 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ TimelineObject: Add start/inpoint/duration/priority properties and propagate them
+
+2009-08-07 20:32:47 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ Track: Handle pads
+
+2009-08-07 20:32:29 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ Track: Add convenience methods for creating a raw Audio or Video track.
+
+2009-08-07 20:31:11 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * tests/check/ges/basic.c:
+ CustomTimelineSource: Allow giving user_data to the callback
+
+2009-08-07 20:29:35 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * configure.ac:
+ * tests/Makefile.am:
+ * tests/examples/Makefile.am:
+ * tests/examples/test1.c:
+ Add directory for examples along with a minimalistic first example.
+
+2009-08-07 18:18:42 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * tests/check/ges/basic.c:
+ Add 'caps' property on Track
+
+2009-08-07 17:09:59 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * COPYING:
+ * COPYING.LIB:
+ * INSTALL:
+ * Makefile.am:
+ * RELEASE:
+ * ges/Makefile.am:
+ * gst-editing-services.doap:
+ Fix build. Can now run make distcheck.
+
+2009-08-07 16:51:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ Track: Add/Remove the GnlComposition from ourself.
+
+2009-08-07 16:47:18 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests/ges/basic: Extend test to remove the object.
+
+2009-08-07 16:46:57 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-source.h:
+ TrackSource: Include TrackObject header file
+
+2009-08-07 16:46:35 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges.c:
+ ges.c: Add debug line to inform of initialization
+
+2009-08-07 16:45:16 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-custom-timeline-source.c:
+ * ges/ges-custom-timeline-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ New CustomTimelineSource class.
+
+2009-08-07 16:43:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ Track: Implement remove_object()
+
+2009-08-07 16:43:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ TrackObject: Add 'valid' property, Make _set_track() return a bool
+
+2009-08-07 16:41:23 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline.c:
+ Timeline: Implement remove_track, remove_layer, and _layer_object_removed_cb
+
+2009-08-07 16:40:51 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ TimelineSource: Implenent _create_track_object() virtual-method
+
+2009-08-07 16:39:45 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ TimelineObject: Implement _release_track_object()
+
+2009-08-07 16:39:09 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ TimelineLayer: Implement _remove_object()
+
+2009-08-06 20:04:59 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * tests/check/ges/basic.c:
+ tests: Add a simple scenario
+
+2009-08-06 19:59:25 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track.c:
+ ges: Remove unused private structures. We'll re-add on a per-case basis.
+
+2009-08-06 19:51:29 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * Makefile.am:
+ * configure.ac:
+ * ges/Makefile.am:
+ * ges/ges-internal.h:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline.c:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track-source.c:
+ * ges/ges-track.c:
+ * ges/ges.c:
+ * ges/ges.h:
+ * tests/Makefile.am:
+ * tests/check/Makefile.am:
+ * tests/check/ges/basic.c:
+ Add unit test system. Adjust GST_DEBUG usage.
+
+2009-08-06 18:54:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-track-source.c:
+ * ges/ges-track-source.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ Add new GESTrackSource
+
+2009-08-06 17:38:43 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/scenarios:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ intermediary commit. Still need to fill in more blanks :(
+
+2009-08-06 12:47:38 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/gesmarshal.list:
+ Add signal marshalling code
+
+2009-08-06 12:14:37 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * Makefile.am:
+ * autogen.sh:
+ * configure.ac:
+ * ges/Makefile.am:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.h:
+ * ges/ges-types.h:
+ * ges/ges.h:
+ build fixed again. Moved type declarations in a standalone file.
+
+2009-08-06 11:24:04 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * ges/Makefile.am:
+ * ges/ges-simple-timeline-layer.c:
+ * ges/ges-simple-timeline-layer.h:
+ * ges/ges-timeline-layer.c:
+ * ges/ges-timeline-layer.h:
+ * ges/ges-timeline-object.c:
+ * ges/ges-timeline-object.h:
+ * ges/ges-timeline-pipeline.c:
+ * ges/ges-timeline-pipeline.h:
+ * ges/ges-timeline-source.c:
+ * ges/ges-timeline-source.h:
+ * ges/ges-timeline-transition.c:
+ * ges/ges-timeline-transition.h:
+ * ges/ges-timeline.c:
+ * ges/ges-timeline.h:
+ * ges/ges-track-object.c:
+ * ges/ges-track-object.h:
+ * ges/ges-track.c:
+ * ges/ges-track.h:
+ * ges/ges.c:
+ * ges/ges.h:
+ src/ => ges/
+
+2009-08-06 11:23:01 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * docs/scenarios:
+ * src/Makefile.am:
+ * src/ges-simple-timeline-layer.h:
+ * src/ges-timeline-object.c:
+ * src/ges-timeline-object.h:
+ * src/ges-timeline.h:
+ * src/ges-track-object.c:
+ * src/ges-track-object.h:
+ * src/ges-track.h:
+ * src/ges.c:
+ * src/ges.h:
+ MORE HACKING
+
+2009-08-04 19:27:07 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * README:
+ README: Mention license
+
+2009-08-04 19:21:49 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitignore:
+ .gitignore: ignore cruft
+
+2009-08-04 17:16:31 +0200 Edward Hervey <edward.hervey@collabora.co.uk>
+
+ * .gitmodules:
+ * AUTHORS:
+ * ChangeLog:
+ * Makefile.am:
+ * NEWS:
+ * autogen.sh:
+ * common:
+ * configure.ac:
+ * gst-editing-services.spec.in:
+ * m4/Makefile.am:
+ * m4/codeset.m4:
+ * m4/gettext.m4:
+ * m4/glibc2.m4:
+ * m4/glibc21.m4:
+ * m4/iconv.m4:
+ * m4/intdiv0.m4:
+ * m4/intl.m4:
+ * m4/intldir.m4:
+ * m4/intlmacosx.m4:
+ * m4/intmax.m4:
+ * m4/inttypes-pri.m4:
+ * m4/inttypes_h.m4:
+ * m4/lcmessage.m4:
+ * m4/lib-ld.m4:
+ * m4/lib-link.m4:
+ * m4/lib-prefix.m4:
+ * m4/libtool.m4:
+ * m4/lock.m4:
+ * m4/longlong.m4:
+ * m4/ltoptions.m4:
+ * m4/ltsugar.m4:
+ * m4/ltversion.m4:
+ * m4/lt~obsolete.m4:
+ * m4/nls.m4:
+ * m4/po.m4:
+ * m4/printf-posix.m4:
+ * m4/progtest.m4:
+ * m4/size_max.m4:
+ * m4/stdint_h.m4:
+ * m4/uintmax_t.m4:
+ * m4/visibility.m4:
+ * m4/wchar_t.m4:
+ * m4/wint_t.m4:
+ * m4/xsize.m4:
+ * src/Makefile.am:
+ * src/ges-simple-timeline-layer.c:
+ * src/ges-simple-timeline-layer.h:
+ * src/ges-timeline-layer.c:
+ * src/ges-timeline-layer.h:
+ * src/ges-timeline-object.c:
+ * src/ges-timeline-object.h:
+ * src/ges-timeline-pipeline.c:
+ * src/ges-timeline-pipeline.h:
+ * src/ges-timeline-source.c:
+ * src/ges-timeline-source.h:
+ * src/ges-timeline-transition.c:
+ * src/ges-timeline-transition.h:
+ * src/ges-timeline.c:
+ * src/ges-timeline.h:
+ * src/ges-track-object.c:
+ * src/ges-track-object.h:
+ * src/ges-track.c:
+ * src/ges-track.h:
+ It builds !!!! :)
+
--- /dev/null
+DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc --with-bash-completion-dir=no
+
+if BUILD_EXAMPLES
+EXAMPLES_SUBDIRS= examples
+else
+EXAMPLES_SUBDIRS=
+endif
+
+SUBDIRS = ges tests tools common m4 pkgconfig docs bindings plugins $(EXAMPLES_SUBDIRS)
+
+DIST_SUBDIRS = $(SUBDIRS)
+
+EXTRA_DIST = \
+ depcomp \
+ AUTHORS COPYING NEWS README RELEASE \
+ ChangeLog autogen.sh gst-editing-services.doap \
+ $(shell find "$(top_srcdir)" -type f -name meson.build ! -path "$(top_srcdir)/$(PACKAGE_TARNAME)-*" ) \
+ meson_options.txt
+
+DISTCLEANFILES = _stdint.h
+
+ACLOCAL_AMFLAGS = -I m4 -I common/m4
+
+include $(top_srcdir)/common/release.mak
+include $(top_srcdir)/common/po.mak
+
+include $(top_srcdir)/common/coverage/lcov.mak
+
+check-valgrind:
+ $(MAKE) -C tests/check check-valgrind
+
+# Test actual high-level functionnality.
+check-integration:
+ $(MAKE) -C tests/check check-integration
+
+if ENABLE_BASH_COMPLETION
+bashcompletiondir = $(BASH_COMPLETION_DIR)
+dist_bashcompletion_DATA = data/completions/ges-launch-1.0
+endif
+
+if HAVE_GST_CHECK
+check-torture:
+ $(MAKE) -C tests/check torture
+build-checks:
+ $(MAKE) -C tests/check build-checks
+else
+check-torture:
+ true
+build-checks:
+ true
+endif
+
+# cruft: plugins that have been merged or moved or renamed
+CRUFT_FILES = \
+ $(top_builddir)/gst-editing-services.spec \
+ $(top_builddir)/common/shave \
+ $(top_builddir)/common/shave-libtool
+
+include $(top_srcdir)/common/cruft.mak
+
+all-local: check-cruft
--- /dev/null
+
+
+GSTREAMER 1.16 RELEASE NOTES
+
+
+GStreamer 1.16.0 was originally released on 19 April 2019.
+
+The latest bug-fix release in the 1.16 series is 1.16.2 and was released
+on 3 December 2019.
+
+See https://gstreamer.freedesktop.org/releases/1.16/ for the latest
+version of this document.
+
+_Last updated: Tuesday 03 December 2019, 08:00 UTC (log)_
+
+
+Introduction
+
+The GStreamer team is proud to announce a new major feature release in
+the stable 1.x API series of your favourite cross-platform multimedia
+framework!
+
+As always, this release is again packed with many new features, bug
+fixes and other improvements.
+
+
+Highlights
+
+- GStreamer WebRTC stack gained support for data channels for
+ peer-to-peer communication based on SCTP, BUNDLE support, as well as
+ support for multiple TURN servers.
+
+- AV1 video codec support for Matroska and QuickTime/MP4 containers
+ and more configuration options and supported input formats for the
+ AOMedia AV1 encoder
+
+- Support for Closed Captions and other Ancillary Data in video
+
+- Support for planar (non-interleaved) raw audio
+
+- GstVideoAggregator, compositor and OpenGL mixer elements are now in
+ -base
+
+- New alternate fields interlace mode where each buffer carries a
+ single field
+
+- WebM and Matroska ContentEncryption support in the Matroska demuxer
+
+- new WebKit WPE-based web browser source element
+
+- Video4Linux: HEVC encoding and decoding, JPEG encoding, and improved
+ dmabuf import/export
+
+- Hardware-accelerated Nvidia video decoder gained support for VP8/VP9
+ decoding, whilst the encoder gained support for H.265/HEVC encoding.
+
+- Many improvements to the Intel Media SDK based hardware-accelerated
+ video decoder and encoder plugin (msdk): dmabuf import/export for
+ zero-copy integration with other components; VP9 decoding; 10-bit
+ HEVC encoding; video post-processing (vpp) support including
+ deinterlacing; and the video decoder now handles dynamic resolution
+ changes.
+
+- The ASS/SSA subtitle overlay renderer can now handle multiple
+ subtitles that overlap in time and will show them on screen
+ simultaneously
+
+- The Meson build is now feature-complete (*) and it is now the
+ recommended build system on all platforms. The Autotools build is
+ scheduled to be removed in the next cycle.
+
+- The GStreamer Rust bindings and Rust plugins module are now
+ officially part of upstream GStreamer.
+
+- The GStreamer Editing Services gained a gesdemux element that allows
+ directly playing back serialized edit list with playbin or
+ (uri)decodebin
+
+- Many performance improvements
+
+
+Major new features and changes
+
+Noteworthy new API
+
+- GstAggregator has a new "min-upstream-latency" property that forces
+ a minimum aggregate latency for the input branches of an aggregator.
+ This is useful for dynamic pipelines where branches with a higher
+ latency might be added later after the pipeline is already up and
+ running and where a change in the latency would be disruptive. This
+ only applies to the case where at least one of the input branches is
+ live though, it won’t force the aggregator into live mode in the
+ absence of any live inputs.
+
+- GstBaseSink gained a "processing-deadline" property and
+ setter/getter API to configure a processing deadline for live
+ pipelines. The processing deadline is the acceptable amount of time
+ to process the media in a live pipeline before it reaches the sink.
+ This is on top of the systemic latency that is normally reported by
+ the latency query. This defaults to 20ms and should make pipelines
+ such as v4l2src ! xvimagesink not claim that all frames are late in
+ the QoS events. Ideally, this should replace the "max-lateness"
+ property for most applications.
+
+- RTCP Extended Reports (XR) parsing according to RFC 3611:
+ Loss/Duplicate RLE, Packet Receipt Times, Receiver Reference Time,
+ Delay since the last Receiver (DLRR), Statistics Summary, and VoIP
+ Metrics reports. This only provides the ability to parse such
+ packets, generation of XR packets is not supported yet and XR
+ packets are not automatically parsed by rtpbin / rtpsession but must
+ be actively handled by the application.
+
+- a new mode for interlaced video was added where each buffer carries
+ a single field of interlaced video, with buffer flags indicating
+ whether the field is the top field or bottom field. Top and bottom
+ fields are expected to alternate in this mode. Caps for this
+ interlace mode must also carry a format:Interlaced caps feature to
+ ensure backwards compatibility.
+
+- The video library has gained support for three new raw pixel
+ formats:
+
+ - Y410: packed 4:4:4 YUV, 10 bits per channel
+ - Y210: packed 4:2:2 YUV, 10 bits per channel
+ - NV12_10LE40: fully-packed 10-bit variant of NV12_10LE32,
+ i.e. without the padding bits
+
+- GstRTPSourceMeta is a new meta that can be used to transport
+ information about the origin of depayloaded or decoded RTP buffers,
+ e.g. when mixing audio from multiple sources into a single stream. A
+ new "source-info" property on the RTP depayloader base class
+ determines whether depayloaders should put this meta on outgoing
+ buffers. Similarly, the same property on RTP payloaders determines
+ whether they should use the information from this meta to construct
+ the CSRCs list on outgoing RTP buffers.
+
+- gst_sdp_message_from_text() is a convenience constructor to parse
+ SDPs from a string which is particularly useful for language
+ bindings.
+
+Support for Planar (Non-Interleaved) Raw Audio
+
+Raw audio samples are usually passed around in interleaved form in
+GStreamer, which means that if there are multiple audio channels the
+samples for each channel are interleaved in memory,
+e.g. |LEFT|RIGHT|LEFT|RIGHT|LEFT|RIGHT| for stereo audio. A
+non-interleaved or planar arrangement in memory would look like
+|LEFT|LEFT|LEFT|RIGHT|RIGHT|RIGHT| instead, possibly with
+|LEFT|LEFT|LEFT| and |RIGHT|RIGHT|RIGHT| residing in separate memory
+chunks or separated by some padding.
+
+GStreamer has always had signalling for non-interleaved audio since
+version 1.0, but it was never actually properly implemented in any
+elements. audioconvert would advertise support for it, but wasn’t
+actually able to handle it correctly.
+
+With this release we now have full support for non-interleaved audio as
+well, which means more efficient integration with external APIs that
+handle audio this way, but also more efficient processing of certain
+operations like interleaving multiple 1-channel streams into a
+multi-channel stream which can be done without memory copies now.
+
+New API to support this has been added to the GStreamer Audio support
+library: There is now a new GstAudioMeta which describes how data is
+laid out inside the buffer, and buffers with non-interleaved audio must
+always carry this meta. To access the non-interleaved audio samples you
+must map such buffers with gst_audio_buffer_map() which works much like
+gst_buffer_map() or gst_video_frame_map() in that it will populate a
+little GstAudioBuffer helper structure passed to it with the number of
+samples, the number of planes and pointers to the start of each plane in
+memory. This function can also be used to map interleaved audio buffers
+in which case there will be only one plane of interleaved samples.
+
+Of course support for this has also been implemented in the various
+audio helper and conversion APIs, base classes, and in elements such as
+audioconvert, audioresample, audiotestsrc, audiorate.
+
+Support for Closed Captions and Other Ancillary Data in Video
+
+The video support library has gained support for detecting and
+extracting Ancillary Data from videos as per the SMPTE S291M
+specification, including:
+
+- a VBI (Vertical Blanking Interval) parser that can detect and
+ extract Ancillary Data from Vertical Blanking Interval lines of
+ component signals. This is currently supported for videos in v210
+ and UYVY format.
+
+- a new GstMeta for closed captions: GstVideoCaptionMeta. This
+ supports the two types of closed captions, CEA-608 and CEA-708,
+ along with the four different ways they can be transported (other
+ systems are a superset of those).
+
+- a VBI (Vertical Blanking Interval) encoder for writing ancillary
+ data to the Vertical Blanking Interval lines of component signals.
+
+The new closedcaption plugin in gst-plugins-bad then makes use of all
+this new infrastructure and provides the following elements:
+
+- cccombiner: a closed caption combiner that takes a closed captions
+ stream and another stream and adds the closed captions as
+ GstVideoCaptionMeta to the buffers of the other stream.
+
+- ccextractor: a closed caption extractor which will take
+ GstVideoCaptionMeta from input buffers and output them as a separate
+ closed captions stream.
+
+- ccconverter: a closed caption converter that can convert between
+ different formats
+
+- line21encoder, line21decoder: inject/extract line21 closed captions
+ to/from SD video streams
+
+- cc708overlay: decodes CEA 608/708 captions and overlays them on
+ video
+
+Additionally, the following elements have also gained Closed Caption
+support:
+
+- qtdemux and qtmux support CEA 608/708 Closed Caption tracks
+
+- mpegvideoparse, h264parse extracts Closed Captions from MPEG-2/H.264
+ video streams
+
+- avviddec, avvidenc, x264enc got support for extracting/injecting
+ Closed Captions
+
+- decklinkvideosink can output closed captions and decklinkvideosrc
+ can extract closed captions
+
+- playbin and playbin3 learned how to autoplug CEA 608/708 CC overlay
+ elements
+
+- the externally maintained ajavideosrc element for AJA capture cards
+ has support for extracting closed captions
+
+The rsclosedcaption plugin in the Rust plugins collection includes a
+MacCaption (MCC) file parser and encoder.
+
+New Elements
+
+- overlaycomposition: New element that allows applications to draw
+ GstVideoOverlayCompositions on a stream. The element will emit the
+ "draw" signal for each video buffer, and the application then
+ generates an overlay for that frame (or not). This is much more
+ performant than e.g. cairooverlay for many use cases, e.g. because
+ pixel format conversions can be avoided or the blitting of the
+ overlay can be delegated to downstream elements (such as
+ gloverlaycompositor). It’s particularly useful for cases where only
+ a small section of the video frame should be drawn on.
+
+- gloverlaycompositor: New OpenGL-based compositor element that
+ flattens any overlays from GstVideoOverlayCompositionMetas into the
+ video stream. This element is also always part of glimagesink.
+
+- glalpha: New element that adds an alpha channel to a video stream.
+ The values of the alpha channel can either be set to a constant or
+ can be dynamically calculated via chroma keying. It is similar to
+ the existing alpha element but based on OpenGL. Calculations are
+ done in floating point so results may not be identical to the output
+ of the existing alpha element.
+
+- rtpfunnel funnels together RTP streams into a single session. Use
+ cases include multiplexing and bundle. webrtcbin uses it to
+ implement BUNDLE support.
+
+- testsrcbin is a source element that provides an audio and/or video
+ stream and also announces them using the recently-introduced
+ GstStream API. This is useful for testing elements such as playbin3
+ or uridecodebin3 etc.
+
+- New closed caption elements: cccombiner, ccextractor, ccconverter,
+ line21encoder, line21decoder and cc708overlay (see above)
+
+- wpesrc: new source element acting as a Web Browser based on WebKit
+ WPE
+
+- Two new OpenCV-based elements: cameracalibrate and cameraundistort
+ that can communicate to figure out distortion correction parameters
+ for a camera and correct for the distortion.
+
+- New sctp plugin based on usrsctp with sctpenc and sctpdec elements.
+ These elements are used inside webrtcbin for implementing data
+ channels.
+
+New element features and additions
+
+- playbin3, playbin and playsink have gained a new "text-offset"
+ property to adjust the positioning of the selected subtitle stream
+ vis-a-vis the audio and video streams. This uses subtitleoverlay’s
+ new "subtitle-ts-offset" property. GstPlayer has gained matching API
+ for this, namely gst_player_get_text_video_offset().
+
+- playbin3 buffering improvements: in network playback scenarios there
+ may be multiple inputs to decodebin3, and buffering will be done
+ before decodebin3 using queue2 or downloadbuffer elements inside
+ urisourcebin. Since this is before any parsers or demuxers there may
+ not be any bitrate information available for the various streams, so
+ it was difficult to configure the buffering there smartly within
+ global constraints. This was improved now: The queue2 elements
+ inside urisourcebin will now use the new bitrate query to figure out
+ a bitrate estimate for the stream if no bitrate was provided by
+ upstream, and urisourcebin will use the bitrates of the individual
+ queues to distribute the globally-set "buffer-size" budget in bytes
+ to the various queues. urisourcebin also gained "low-watermark" and
+ "high-watermark" properties which will be proxied to the internal
+ queues, as well as a read-only "statistics" property which allows
+ querying of the minimum/maximum/average byte and time levels of the
+ queues inside the urisourcebin in question.
+
+- splitmuxsink has gained a couple of new features:
+
+ - new "async-finalize" mode: This mode is useful for muxers or
+ outputs that can take a long time to finalize a file. Instead of
+ blocking the whole upstream pipeline while the muxer is doing
+ its stuff, we can unlink it and spawn a new muxer + sink
+ combination to continue running normally. This requires us to
+ receive the muxer and sink (if needed) as factories via the new
+ "muxer-factory" and "sink-factory" properties, optionally
+ accompanied by their respective properties structures (set via
+ the new "muxer-properties" and "sink-properties" properties).
+ There are also new "muxer-added" and "sink-added" signals in
+ case custom code has to be called for them to configure them.
+
+ - "split-at-running-time" action signal: When called by the user,
+ this action signal ends the current file (and starts a new one)
+ as soon as the given running time is reached. If called multiple
+ times, running times are queued up and processed in the order
+ they were given.
+
+ - "split-after" action signal to finish outputting the current GOP
+ to the current file and then start a new file as soon as the GOP
+ is finished and a new GOP is opened (unlike the existing
+ "split-now" which immediately finishes the current file and
+ writes the current GOP into the next newly-started file).
+
+ - "reset-muxer" property: when unset, the muxer is reset using
+ flush events instead of setting its state to NULL and back. This
+ means the muxer can keep state across resets, e.g. mpegtsmux
+ will keep the continuity counter continuous across segments as
+ required by hlssink2.
+
+- qtdemux gained PIFF track encryption box support in addition to the
+ already-existing PIFF sample encryption support, and also allows
+ applications to select which encryption system to use via a
+ "drm-preferred-decryption-system-id" context in case there are
+ multiple options.
+
+- qtmux: the "start-gap-threshold" property determines now whether an
+ edit list will be created to account for small gaps or offsets at
+ the beginning of a stream in case the start timestamps of tracks
+ don’t line up perfectly. Previously the threshold was hard-coded to
+ 1% of the (video) frame duration, now it is 0 by default (so edit
+ list will be created even for small differences), but fully
+ configurable.
+
+- rtpjitterbuffer has improved end-of-stream handling
+
+- rtpmp4vpay will be preferred over rtpmp4gpay for MPEG-4 video in
+ autoplugging scenarios now
+
+- rtspsrc now allows applications to send RTSP SET_PARAMETER and
+ GET_PARAMETER requests using action signals.
+
+- rtspsrc has a small (100ms) configurable teardown delay by default
+ to try and make sure an RTSP TEARDOWN request gets sent out when the
+ source element shuts down. This will block the downward PAUSED to
+ READY state change for a short time, but can be disabled where it’s
+ a problem. Some servers only allow a limited number of concurrent
+ clients, so if no proper TEARDOWN is sent new clients may have
+ problems connecting to the server for a while.
+
+- souphttpsrc behaves better with low bitrate streams now. Before it
+ would increase the read block size too quickly which could lead to
+ it not reading any data from the socket for a very long time with
+ low bitrate streams that are output live downstream. This could lead
+ to servers kicking off the client.
+
+- filesink: do internal buffering to avoid performance regression with
+ small writes since we bypass libc buffering by using writev()
+ instead of fwrite()
+
+- identity: add "eos-after" property and fix "error-after" property
+ when the element is reused
+
+- input-selector: lets context queries pass through, so that
+ e.g. upstream OpenGL elements can use contexts and displays
+ advertised by downstream elements
+
+- queue2: avoid ping-pong between 0% and 100% buffering messages if
+ upstream is pushing buffers larger than one of its limits, plus
+ performance optimisations
+
+- opusdec: new "phase-inversion" property to control phase inversion.
+ When enabled, this will slightly increase stereo quality, but
+ produces a stream that when downmixed to mono will suffer audio
+ distortions.
+
+- The x265enc HEVC encoder also exposes a "key-int-max" property to
+ configure the maximum allowed GOP size now.
+
+- decklinkvideosink has seen stability improvements for long-running
+ pipelines (potential crash due to overflow of leaked clock refcount)
+ and clock-slaving improvements when performing flushing seeks
+ (causing stalls in the output timeline), pausing and/or buffering.
+
+- srtpdec, srtpenc: add support for MKIs which allow multiple keys to
+ be used with a single SRTP stream
+
+- srtpdec, srtpenc: add support for AES-GCM and also add support for
+ it in gst-rtsp-server and rtspsrc.
+
+- The srt Secure Reliable Transport plugin has integrated server and
+ client elements srt{client,server}{src,sink} into one (srtsrc and
+ srtsink), since SRT connection mode can be changed by uri
+ parameters.
+
+- h264parse and h265parse will handle SEI recovery point messages and
+ mark recovery points as keyframes as well (in addition to IDR
+ frames)
+
+- webrtcbin: "add-turn-server" action signal to pass multiple ICE
+ relays (TURN servers).
+
+- The removesilence element has received various new features and
+ properties, such as a "threshold" property, detecting silence only
+ after minimum silence time/buffers, a "silent" property to control
+ bus message notifications as well as a "squash" property.
+
+- AOMedia AV1 decoder gained support for 10/12bit decoding whilst the
+ AV1 encoder supports more image formats and subsamplings now and
+ acquired support for rate control and profile related configuration.
+
+- The Fraunhofer fdkaac plugin can now be built against the 2.0.0
+ version API and has improved multichannel support
+
+- kmssink now supports unpadded 24-bit RGB and can configure mode
+ setting from video info, which enables display of multi-planar
+ formats such as I420 or NV12 with modesetting. It has also gained a
+ number of new properties: The "restore-crtc" property does what it
+ says on the tin and is enabled by default. "plane-properties" and
+ "connector-properties" can be used to pass custom properties to the
+ DRM.
+
+- waylandsink has a "fullscreen" property now and supports the
+ XDG-Shell protocol.
+
+- decklinkvideosink, decklinkvideosrc support selecting between
+ half/full duplex
+
+- The vulkan plugin gained support for macOS and iOS via MoltenVK in
+ addition to the existing support for X11 and Wayland
+
+- imagefreeze has a new num-buffers property to limit the number of
+ buffers that are produced and to send an EOS event afterwards
+
+- webrtcbin has a new, introspectable get-transceiver signal in
+ addition to the old get-transceivers signal that couldn’t be used
+ from bindings
+
+- Support for per-element latency information was added to the latency
+ tracer
+
+Plugin and library moves
+
+- The stereo element was moved from -bad into the existing audiofx
+ plugin in -good. If you get duplicate type registration warnings
+ when upgrading, check that you don’t have a stale stereoplugin lying
+ about somewhere.
+
+GstVideoAggregator, compositor, and OpenGL mixer elements moved from -bad to -base
+
+GstVideoAggregator is a new base class for raw video mixers and muxers
+and is based on GstAggregator. It provides defined-latency mixing of raw
+video inputs and ensures that the pipeline won’t stall even if one of
+the input streams stops producing data.
+
+As part of the move to stabilise the API there were some last-minute API
+changes and clean-ups, but those should mostly affect internal elements.
+Most notably, the "ignore-eos" pad property was renamed to
+"repeat-after-eos" and the conversion code was moved to a
+GstVideoAggregatorConvertPad subclass to avoid code duplication, make
+things less awkward for subclasses like the OpenGL-based video mixer,
+and make the API more consistent with the audio aggregator API.
+
+It is used by the compositor element, which is a replacement for
+‘videomixer’ which did not handle live inputs very well. compositor
+should behave much better in that respect and generally behave as one
+would expected in most scenarios.
+
+The compositor element has gained support for per-pad blending mode
+operators (SOURCE, OVER, ADD) which determines what operator to use for
+blending this pad over the previous ones. This can be used to implement
+crossfading and the available operators can be extended in the future as
+needed.
+
+A number of OpenGL-based video mixer elements (glvideomixer, glmixerbin,
+glvideomixerelement, glstereomix, glmosaic) which are built on top of
+GstVideoAggregator have also been moved from -bad to -base now. These
+elements have been merged into the existing OpenGL plugin, so if you get
+duplicate type registration warnings when upgrading, check that you
+don’t have a stale openglmixers plugin lying about somewhere.
+
+Plugin removals
+
+The following plugins have been removed from gst-plugins-bad:
+
+- The experimental daala plugin has been removed, since it’s not so
+ useful now that all effort is focused on AV1 instead, and it had to
+ be enabled explicitly with --enable-experimental anyway.
+
+- The spc plugin has been removed. It has been replaced by the gme
+ plugin.
+
+- The acmmp3dec and acmenc plugins for Windows have been removed. ACM
+ is an ancient legacy API and there was no point in keeping the
+ plugins around for a licensed MP3 decoder now that the MP3 patents
+ have expired and we have a decoder in -good. We also didn’t ship
+ these in our cerbero-built Windows packages, so it’s unlikely that
+ they’ll be missed.
+
+
+Miscellaneous API additions
+
+- GstBitwriter: new generic bit writer API to complement the existing
+ bit reader
+
+- gst_buffer_new_wrapped_bytes() creates a wrap buffer from a GBytes
+
+- gst_caps_set_features_simple() sets a caps feature on all the
+ structures of a GstCaps
+
+- New GST_QUERY_BITRATE query: This allows determining from downstream
+ what the expected bitrate of a stream may be which is useful in
+ queue2 for setting time based limits when upstream does not provide
+ timing information. tsdemux, qtdemux and matroskademux have basic
+ support for this query on their sink pads.
+
+- elements: there is a new “Hardware” class specifier. Elements
+ interacting with hardware devices should specify this classifier in
+ their element factory class metadata. This is useful to advertise as
+ one might need to put such elements into READY state to test if the
+ hardware is present in the system for example.
+
+- protection: Add a new definition for unspecified system protection,
+ GST_PROTECTION_UNSPECIFIED_SYSTEM_ID
+
+- take functions for various mini objects that didn’t have them yet:
+ gst_query_take(), gst_message_take(), gst_tag_list_take(),
+ gst_buffer_list_take(). Unlike the various _replace() functions
+ _take() does not increase the reference count but takes ownership of
+ the mini object passed.
+
+- clear functions for various mini object types and GstObject which
+ unrefs the object or mini object (if non-NULL) and sets the variable
+ pointed to to NULL: gst_clear_structure(), gst_clear_tag_list(),
+ gst_clear_query(), gst_clear_message(), gst_clear_event(),
+ gst_clear_caps(), gst_clear_buffer_list(), gst_clear_buffer(),
+ gst_clear_mini_object(), gst_clear_object()
+
+- miniobject: new API gst_mini_object_add_parent() and
+ gst_mini_object_remove_parent() to set parent pointers on mini
+ objects to ensure correct writability: Every container of
+ miniobjects now needs to store itself as parent in the child object,
+ and remove itself again later. A mini object is then only writable
+ if there is at most one parent, that parent is writable itself, and
+ the reference count of the mini object is 1. GstBuffer (for
+ memories), GstBufferList (for buffers), GstSample (for caps, buffer,
+ bufferlist), and GstVideoOverlayComposition were updated
+ accordingly. Without this it was possible to have e.g. a buffer list
+ with a refcount of 2 used in two places at once that both modify the
+ same buffer with refcount 1 at the same time wrongly thinking it is
+ writable even though it’s really not.
+
+- poll: add API to watch for POLLPRI and stop treating POLLPRI as a
+ read. This is useful to wait for video4linux events which are
+ signalled via POLLPRI.
+
+- sample: new API to update the contents of a GstSample and make it
+ writable: gst_sample_set_buffer(), gst_sample_set_caps(),
+ gst_sample_set_segment(), gst_sample_set_info(), plus
+ gst_sample_is_writable() and gst_sample_make_writable(). This makes
+ it possible to reuse a sample object and avoid unnecessary memory
+ allocations, for example in appsink.
+
+- ClockIDs now keep a weak reference to underlying clock to avoid
+ crashes in basesink in corner cases where a clock goes away while
+ the ClockID is still in use, plus some new API
+ (gst_clock_id_get_clock(), gst_clock_id_uses_clock()) to check the
+ clock a ClockID is linked to.
+
+- The GstCheck unit test library gained a
+ fail_unless_equals_clocktime() convenience macro as well as some new
+ GstHarness API for for proposing meta APIs from the allocation
+ query: gst_harness_add_propose_allocation_meta(). ASSERT_CRITICAL()
+ checks in unit tests are now skipped if GStreamer was compiled with
+ GST_DISABLE_GLIB_CHECKS.
+
+- gst_audio_buffer_truncate() convenience function to truncate a raw
+ audio buffer
+
+- GstDiscoverer has support for caching the results of discovery in
+ the default cache directory. This can be enabled with the use-cache
+ property and is disabled by default.
+
+- GstMeta that are attached to GstBuffers are now always stored in the
+ order in which they were added.
+
+- Additional support for signalling ONVIF specific features were
+ added: the SEEK event can store a trickmode-interval now and support
+ for the Rate-Control and Frames RTSP headers was added to the RTSP
+ library.
+
+
+Miscellaneous performance and memory optimisations
+
+As always there have been many performance and memory usage improvements
+across all components and modules. Some of them (such as dmabuf
+import/export) have already been mentioned elsewhere so won’t be
+repeated here.
+
+The following list is only a small snapshot of some of the more
+interesting optimisations that haven’t been mentioned in other contexts
+yet:
+
+- The GstVideoEncoder and GstVideoDecoder base classes now release the
+ STREAM_LOCK when pushing out buffers, which means (multi-threaded)
+ encoders and decoders can now receive and continue to process input
+ buffers whilst waiting for downstream elements in the pipeline to
+ process the buffer that was pushed out. This increases throughput
+ and reduces processing latency, also and especially for
+ hardware-accelerated encoder/decoder elements.
+
+- GstQueueArray has seen a few API additions
+ (gst_queue_array_peek_nth(), gst_queue_array_set_clear_func(),
+ gst_queue_array_clear()) so that it can be used in other places like
+ GstAdapter instead of a GList, which reduces allocations and
+ improves performance.
+
+- appsink now reuses the sample object in pull_sample() if possible
+
+- rtpsession only starts the RTCP thread when it’s actually needed now
+
+- udpsrc uses a buffer pool now and the GstUdpSrc object structure was
+ optimised for better cache performance
+
+GstPlayer
+
+- API was added to fine-tune the synchronisation offset between
+ subtitles and video
+
+
+Miscellaneous changes
+
+- As a result of moving to newer FFmpeg APIs, encoder and decoder
+ elements exposed by the GStreamer FFmpeg wrapper plugin (gst-libav)
+ may have seen possibly incompatible changes to property names and/or
+ types, and not all properties exposed might be functional. We are
+ still reviewing the new properties and aim to minimise breaking
+ changes at least for the most commonly-used properties, so please
+ report any issues you run into!
+
+OpenGL integration
+
+- The OpenGL mixer elements have been moved from -bad to
+ gst-plugins-base (see above)
+
+- The Mesa GBM backend now supports headless mode
+
+- gloverlaycompositor: New OpenGL-based compositor element that
+ flattens any overlays from GstVideoOverlayCompositionMetas into the
+ video stream.
+
+- glalpha: New element that adds an alpha channel to a video stream.
+ The values of the alpha channel can either be set to a constant or
+ can be dynamically calculated via chroma keying. It is similar to
+ the existing alpha element but based on OpenGL. Calculations are
+ done in floating point so results may not be identical to the output
+ of the existing alpha element.
+
+- glupload: Implement direct dmabuf uploader, the idea being that some
+ GPUs (like the Vivante series) can actually perform the YUV->RGB
+ conversion internally, so no custom conversion shaders are needed.
+ To make use of this feature, we need an additional uploader that can
+ import DMABUF FDs and also directly pass the pixel format, relying
+ on the GPU to do the conversion.
+
+- The OpenGL library no longer restores the OpenGL viewport. This is a
+ performance optimization to not require performing multiple
+ expensive glGet*() function calls per frame. This affects any
+ application or plugin use of the following functions and objects:
+ - glcolorconvert library object (not the element)
+ - glviewconvert library object (not the element)
+ - gst_gl_framebuffer_draw_to_texture()
+ - custom GstGLWindow implementations
+
+
+Tracing framework and debugging improvements
+
+- There is now a GDB PRETTY PRINTER FOR VARIOUS GSTREAMER TYPES: For
+ GstObject pointers the type and name is added, e.g.
+ 0x5555557e4110 [GstDecodeBin|decodebin0]. For GstMiniObject pointers
+ the object type is added, e.g. 0x7fffe001fc50 [GstBuffer]. For
+ GstClockTime and GstClockTimeDiff the time is also printed in human
+ readable form, e.g. 150116219955 [+0:02:30.116219955].
+
+- GDB EXTENSION WITH TWO CUSTOM GDB COMMANDS gst-dot AND gst-print:
+
+ - gst-dot creates dot files that a very close to what
+ GST_DEBUG_BIN_TO_DOT_FILE() produces, but object properties and
+ buffer contents such as codec-data in caps are not available.
+
+ - gst-print produces high-level information about a GStreamer
+ object. This is currently limited to pads for GstElements and
+ events for the pads. The output may look like this:
+
+- gst_structure_to_string() now serialises the actual value of
+ pointers when serialising GstStructures instead of claiming they’re
+ NULL. This makes debug logging in various places less confusing,
+ because it’s clear now that structure fields actually hold valid
+ objects. Such object pointer values will never be deserialised
+ however.
+
+
+Tools
+
+- gst-inspect-1.0 has coloured output now and will automatically use a
+ pager if the output does not fit on a page. This only works in a
+ UNIX environment and if the output is not piped, and on Windows 10
+ build 16257 or newer. If you don’t like the colours you can disable
+ them by setting the GST_INSPECT_NO_COLORS=1 environment variable or
+ passing the --no-color command line option.
+
+
+GStreamer RTSP server
+
+- Improved backlog handling when using TCP interleaved for data
+ transport. Before there was a fixed maximum size for backlog
+ messages, which was prone to deadlocks and made it difficult to
+ control memory usage with the watch backlog. The RTSP server now
+ limits queued TCP data messages to one per stream, moving queuing of
+ the data into the pipeline and leaving the RTSP connection
+ responsive to RTSP messages in both directions, preventing all those
+ problems.
+
+- Initial ULP Forward Error Correction support in rtspclientsink and
+ for RECORD mode in the server.
+
+- API to explicitly enable retransmission requests (RTX)
+
+- Lots of multicast-related fixes
+
+- rtsp-auth: Add support for parsing .htdigest files
+
+
+GStreamer VAAPI
+
+- Support Wayland’s display for context sharing, so the application
+ can pass its own wl_display in order to be used for the VAAPI
+ display creation.
+
+- A lot of work to support new Intel hardware using media-driver as VA
+ backend.
+
+- For non-x86 devices, VAAPI display can instantiate, through DRM,
+ with no PCI bus. This enables the usage of libva-v4l2-request
+ driver.
+
+- Added support for XDG-shell protocol as wl_shell replacement which
+ is currently deprecated. This change add as dependency
+ wayland-protocol.
+
+- GstVaapiFilter, GstVaapiWindow, and GstVaapiDecoder classes now
+ inherit from GstObject, gaining all the GStreamer’s instrumentation
+ support.
+
+- The metadata now specifies the plugin as Hardware class.
+
+- H264 decoder is more stable with problematic streams.
+
+- In H265 decoder added support for profiles main-422-10 (P010_10LE),
+ main-444 (AYUV) and main-444-10 (Y410)
+
+- JPEG decoder handles dynamic resolution changes.
+
+- More specification adherence in H264 and H265 encoders.
+
+
+GStreamer OMX
+
+- Add support of NV16 format to video encoders input.
+
+- Video decoders now handle the ALLOCATION query to tell upstream
+ about the number of buffers they require. Video encoders will also
+ use this query to adjust their number of allocated buffers
+ preventing starvation when using dynamic buffer mode.
+
+- The OMX_PERFORMANCE debug category has been renamed to OMX_API_TRACE
+ and can now be used to track a widder variety of interactions
+ between OMX and GStreamer.
+
+- Video encoders will now detect frame rate only changes and will
+ inform OMX about it rather than doing a full format reset.
+
+- Various Zynq UltraScale+ specific improvements:
+ - Video encoders are now able to import dmabuf from upstream.
+ - Support for HEVC range extension profiles and more AVC profiles.
+ - We can now request video encoders to generate an IDR using the
+ force key unit event.
+
+
+GStreamer Editing Services and NLE
+
+- Added a gesdemux element, it is an auto pluggable element that
+ allows decoding edit list like files supported by GES
+
+- Added gessrc which wraps a GESTimeline as a standard source element
+ (implementing the ges protocol handler)
+
+- Added basic support for videorate::rate property potentially
+ allowing changing playback speed
+
+- Layer priority is now fully automatic and they should be moved with
+ the new ges_timeline_move_layer method, ges_layer_set_priority is
+ now deprecated.
+
+- Added a ges_timeline_element_get_layer_priority so we can simply get
+ all information about GESTimelineElement position in the timeline
+
+- GESVideoSource now auto orientates the images if it is defined in a
+ meta (overridable).
+
+- Added some PyGObject overrides to make the API more pythonic
+
+- The threading model has been made more explicit with safe guard to
+ make sure not thread safe APIs are not used from the wrong threads.
+ It is also now possible to properly handle in what thread the API
+ should be used.
+
+- Optimized GESClip and GESTrackElement creation
+
+- Added a way to compile out the old, unused and deprecated
+ GESPitiviFormatter
+
+- Re implemented the timeline editing API making it faster and making
+ the code much more maintainable
+
+- Simplified usage of nlecomposition outside GES by removing quirks in
+ it API usage and removing the need to treat it specially from an
+ application perspective.
+
+- ges-launch-1.0:
+
+ - Added support to add titles to the timeline
+ - Enhance the help auto generating it from the code
+
+- Deprecate ges_timeline_load_from_uri as loading the timeline should
+ be done through a project now
+
+- MANY leaks have been plugged and the unit testsuite is now “leak
+ free”
+
+
+GStreamer validate
+
+- Added an action type to verify the checksum of the sink last-sample
+
+- Added an include keyword to validate scenarios
+
+- Added the notion of variable in scenarios, with the set-vars keyword
+
+- Started adding support for “performance” like tests by allowing to
+ define the number of dropped buffers or the minimum buffer frequency
+ on a specific pad
+
+- Added a validateflow plugin which allows defining the data flow to
+ be seen on a particular pad and verifying that following runs match
+ the expectations
+
+- Added support for appsrc based test definition so we can instrument
+ the data pushed into the pipeline from scenarios
+
+- Added a mockdecryptor allowing adding tests with on encrypted files,
+ the element will potentially be instrumented with a validate
+ scenario
+
+- gst-validate-launcher:
+
+ - Cleaned up output
+
+ - Changed the default for “muting” tests as user doesn’t expect
+ hundreds of windows to show up when running the testsuite
+
+ - Fixed the outputted xunit files to be compatible with GitLab
+
+ - Added support to run tests on media files in push mode (using
+ pushfile://)
+
+ - Added support for running inside gst-build
+
+ - Added support for running ssim tests on rendered files
+
+ - Added a way to simply define tests on pipelines through a simple
+ .json file
+
+ - Added a python app to easily run python testsuite reusing all
+ the launcher features
+
+ - Added flatpak knowledge so we can print backtrace even when
+ running from within flatpak
+
+ - Added a way to automatically generated “known issues”
+ suppressions lines
+
+ - Added a way to rerun tests to check if they are flaky and added
+ a way to tolerate tests known to be flaky
+
+ - Add a way to output html log files
+
+
+GStreamer Python Bindings
+
+- add binding for gst_pad_set_caps()
+
+- pygobject dependency requirement was bumped to >= 3.8
+
+- new audiotestsrc, audioplot, and mixer plugin examples, and a
+ dynamic pipeline example
+
+
+GStreamer C# Bindings
+
+- bindings for the GstWebRTC library
+
+
+GStreamer Rust Bindings
+
+The GStreamer Rust bindings are now officially part of the GStreamer
+project and are also maintained in the GStreamer GitLab.
+
+The releases will generally not be synchronized with the releases of
+other GStreamer parts due to dependencies on other projects.
+
+Also unlike the other GStreamer libraries, the bindings will not commit
+to full API stability but instead will follow the approach that is
+generally taken by Rust projects, e.g.:
+
+1) 0.12.X will be completely API compatible with all other 0.12.Y
+ versions.
+2) 0.12.X+1 will contain bugfixes and compatible new feature additions.
+3) 0.13.0 will _not_ be backwards compatible with 0.12.X but projects
+ will be able to stay at 0.12.X without any problems as long as they
+ don’t need newer features.
+
+The current stable release is 0.12.2 and the next release series will be
+0.13, probably around March 2019.
+
+At this point the bindings cover most of GStreamer core (except for most
+notably GstAllocator and GstMemory), and most parts of the app, audio,
+base, check, editing-services, gl, net. pbutils, player, rtsp,
+rtsp-server, sdp, video and webrtc libraries.
+
+Also included is support for creating subclasses of the following types
+and writing GStreamer plugins:
+
+- gst::Element
+- gst::Bin and gst::Pipeline
+- gst::URIHandler and gst::ChildProxy
+- gst::Pad, gst::GhostPad
+- gst_base::Aggregator and gst_base::AggregatorPad
+- gst_base::BaseSrc and gst_base::BaseSink
+- gst_base::BaseTransform
+
+Changes to 0.12.X since 0.12.0
+
+Fixed
+
+- PTP clock constructor actually creates a PTP instead of NTP clock
+
+Added
+
+- Bindings for GStreamer Editing Services
+- Bindings for GStreamer Check testing library
+- Bindings for the encoding profile API (encodebin)
+
+- VideoFrame, VideoInfo, AudioInfo, StructureRef implements Send and
+ Sync now
+- VideoFrame has a function to get the raw FFI pointer
+- From impls from the Error/Success enums to the combined enums like
+ FlowReturn
+- Bin-to-dot file functions were added to the Bin trait
+- gst_base::Adapter implements SendUnique now
+- More complete bindings for the gst_video::VideoOverlay interface,
+ especially
+ gst_video::is_video_overlay_prepare_window_handle_message()
+
+Changed
+
+- All references were updated from GitHub to freedesktop.org GitLab
+- Fix various links in the README.md
+- Link to the correct location for the documentation
+- Remove GitLab badge as that only works with gitlab.com currently
+
+Changes in git master for 0.13
+
+Fixed
+
+- gst::tag::Album is the album tag now instead of artist sortname
+
+Added
+
+- Subclassing infrastructure was moved directly into the bindings,
+ making the gst-plugin crate deprecated. This involves many API
+ changes but generally cleans up code and makes it more flexible.
+ Take a look at the gst-plugins-rs crate for various examples.
+
+- Bindings for CapsFeatures and Meta
+- Bindings for
+ ParentBufferMeta,VideoMetaandVideoOverlayCompositionMeta`
+- Bindings for VideoOverlayComposition and VideoOverlayRectangle
+- Bindings for VideoTimeCode
+
+- UniqueFlowCombiner and UniqueAdapter wrappers that make use of the
+ Rust compile-time mutability checks and expose more API in a safe
+ way, and as a side-effect implement Sync and Send now
+
+- More complete bindings for Allocation Query
+- pbutils functions for codec descriptions
+- TagList::iter() for iterating over all tags while getting a single
+ value per tag. The old ::iter_tag_list() function was renamed to
+ ::iter_generic() and still provides access to each value for a tag
+- Bus::iter() and Bus::iter_timed() iterators around the corresponding
+ ::pop\*() functions
+
+- serde serialization of Value can also handle Buffer now
+
+- Extensive comments to all examples with explanations
+- Transmuxing example showing how to use typefind, multiqueue and
+ dynamic pads
+- basic-tutorial-12 was ported and added
+
+Changed
+
+- Rust 1.31 is the minimum supported Rust version now
+- Update to latest gir code generator and glib bindings
+
+- Functions returning e.g. gst::FlowReturn or other “combined” enums
+ were changed to return split enums like
+ Result<gst::FlowSuccess, gst::FlowError> to allow usage of the
+ standard Rust error handling.
+
+- MiniObject subclasses are now newtype wrappers around the underlying
+ GstRc<FooRef> wrapper. This does not change the API in any breaking
+ way for the current usages, but allows MiniObjects to also be
+ implemented in other crates and makes sure rustdoc places the
+ documentation in the right places.
+
+- BinExt extension trait was renamed to GstBinExt to prevent conflicts
+ with gtk::Bin if both are imported
+
+- Buffer::from_slice() can’t possible return None
+
+- Various clippy warnings
+
+
+GStreamer Rust Plugins
+
+Like the GStreamer Rust bindings, the Rust plugins are now officially
+part of the GStreamer project and are also maintained in the GStreamer
+GitLab.
+
+In the 0.3.x versions this contained infrastructure for writing
+GStreamer plugins in Rust, and a set of plugins.
+
+In git master that infrastructure was moved to the GLib and GStreamer
+bindings directly, together with many other improvements that were made
+possible by this, so the gst-plugins-rs repository only contains
+GStreamer elements now.
+
+Elements included are:
+
+- Tutorials plugin: identity, rgb2gray and sinesrc with extensive
+ comments
+
+- rsaudioecho, a port of the audiofx element
+
+- rsfilesrc, rsfilesink
+
+- rsflvdemux, a FLV demuxer. Not feature-equivalent with flvdemux yet
+
+- threadshare plugin: ts-appsrc, ts-proxysrc/sink, ts-queue, ts-udpsrc
+ and ts-tcpclientsrc elements that use a fixed number of threads and
+ share them between instances. For more background about these
+ elements see Sebastian’s talk “When adding more threads adds more
+ problems - Thread-sharing between elements in GStreamer” at the
+ GStreamer Conference 2017.
+
+- rshttpsrc, a HTTP source around the hyper/reqwest Rust libraries.
+ Not feature-equivalent with souphttpsrc yet.
+
+- togglerecord, an element that allows to start/stop recording at any
+ time and keeps all audio/video streams in sync.
+
+- mccparse and mccenc, parsers and encoders for the MCC closed caption
+ file format.
+
+Changes to 0.3.X since 0.3.0
+
+- All references were updated from GitHub to freedesktop.org GitLab
+- Fix various links in the README.md
+- Link to the correct location for the documentation
+
+Changes in git master for 0.4
+
+- togglerecord: Switch to parking_lot crate for mutexes/condition
+ variables for lower overhead
+- Merge threadshare plugin here
+- New closedcaption plugin with mccparse and mccenc elements
+- New identity element for the tutorials plugin
+
+- Register plugins statically in tests instead of relying on the
+ plugin loader to find the shared library in a specific place
+
+- Update to the latest API changes in the GLib and GStreamer bindings
+- Update to the latest versions of all crates
+
+
+Build and Dependencies
+
+- The MESON BUILD SYSTEM BUILD IS NOW FEATURE-COMPLETE (*) and it is
+ now the recommended build system on all platforms and also used by
+ Cerbero to build GStreamer on all platforms. The Autotools build is
+ scheduled to be removed in the next cycle. Developers who currently
+ use gst-uninstalled should move to gst-build. The build option
+ naming has been cleaned up and made consistent and there are now
+ feature options to enable/disable plugins and various other features
+ on a case-by-case basis. (*) with the exception of plugin docs which
+ will be handled differently in future
+
+- Symbol export in libraries is now controlled via explicit exports
+ using symbol visibility or export defines where supported, to ensure
+ consistency across all platforms. This also allows libraries to have
+ exports that vary based on detected platform features and configure
+ options as is the case with the GStreamer OpenGL integration library
+ for example. A few symbols that had been exported by accident in
+ earlier versions may no longer be exported. These symbols will not
+ have had declarations in any public header files then though and
+ would not have been usable.
+
+- The GStreamer FFmpeg wrapper plugin (gst-libav) now depends on
+ FFmpeg 4.x and uses the new FFmpeg 4.x API and stopped relying on
+ ancient API that was removed with the FFmpeg 4.x release. This means
+ that it is no longer possible to build this module against an older
+ system-provided FFmpeg 3.x version. Use the internal FFmpeg 4.x copy
+ instead if you build using autotools, or use gst-libav 1.14.x
+ instead which targets the FFmpeg 3.x API and _should_ work fine in
+ combination with a newer GStreamer. It’s difficult for us to support
+ both old and new FFmpeg APIs at the same time, apologies for any
+ inconvenience caused.
+
+- Hardware-accelerated Nvidia video encoder/decoder plugins nvdec and
+ nvenc can be built against CUDA Toolkit versions 9 and 10.0 now. The
+ dynlink interface has been dropped since it’s deprecated in 10.0.
+
+- The (optional) OpenCV requirement has been bumped to >= 3.0.0 and
+ the plugin can also be built against OpenCV 4.x now.
+
+- New sctp plugin based on usrsctp (for WebRTC data channels)
+
+Cerbero
+
+Cerbero is a meta build system used to build GStreamer plus dependencies
+on platforms where dependencies are not readily available, such as
+Windows, Android, iOS and macOS.
+
+Cerbero has seen a number of improvements:
+
+- Cerbero has been ported to Python 3 and requires Python 3.5 or newer
+ now
+
+- Source tarballs are now protected by checksums in the recipes to
+ guard against download errors and malicious takeover of projects or
+ websites. In addition, downloads are only allowed via secure
+ transports now and plain HTTP, FTP and git:// transports are not
+ allowed anymore.
+
+- There is now a new fetch-bootstrap command which downloads sources
+ required for bootstrapping, with an optional --build-tools-only
+ argument to match the bootstrap --build-tools-only command.
+
+- The bootstrap, build, package and bundle-source commands gained a
+ new --offline switch that ensures that only sources from the cache
+ are used and never downloaded via the network. This is useful in
+ combination with the fetch and fetch-bootstrap commands that acquire
+ sources ahead of time before any build steps are executed. This
+ allows more control over the sources used and when sources are
+ updated, and is particularly useful for build environments that
+ don’t have network access.
+
+- bootstrap --assume-yes will automatically say ‘yes’ to any
+ interactive prompts during the bootstrap stage, such as those from
+ apt-get or yum.
+
+- bootstrap --system-only will only bootstrap the system without build
+ tools.
+
+- Manifest support: The build manifest can be used in continuous
+ integration (CI) systems to fixate the Git revision of certain
+ projects so that all builds of a pipeline are on the same reference.
+ This is used in GStreamer’s gitlab CI for example. It can also be
+ used in order to re-produce a specific build. To set a manifest, you
+ can set manifest = 'my_manifest.xml' in your configuration file, or
+ use the --manifest command line option. The command line option will
+ take precedence over anything specific in the configuration file.
+
+- The new build-deps command can be used to build only the
+ dependencies of a recipe, without the recipe itself.
+
+- new --list-variants command to list available variants
+
+- variants can now be set on the command line via the -v option as a
+ comma-separated list. This overrides any variants set in any
+ configuration files.
+
+- new qt5, intelmsdk and nvidia variants for enabling Qt5 and hardware
+ codec support. See the Enabling Optional Features with Variants
+ section in the Cerbero documentation for more details how to enable
+ and use these variants.
+
+- When building on Windows, Cerbero can now build GStreamer recipes
+ and core dependencies such as glib with Visual Studio. This is
+ controlled by the visualstudio variant. Visual Studio 2015, 2017,
+ and 2019 are supported. Currently, only 64-bit x86 is supported due
+ to a known bug which will be fixed for the next release.
+
+- A new -t / --timestamp command line switch makes commands print
+ timestamps
+
+
+Platform-specific changes and improvements
+
+Android
+
+- toolchain: update compiler to clang and NDKr18. NDK r18 removed the
+ armv5 target and only has Android platforms that target at least
+ armv7 so the armv5 target is not useful anymore.
+
+- The way that GIO modules are named has changed due to upstream GLib
+ natively adding support for loading static GIO modules. This means
+ that any GStreamer application using gnutls for SSL/TLS on the
+ Android or iOS platforms (or any other setup using static libraries)
+ will fail to link looking for the g_io_module_gnutls_load_static()
+ function. The new function name is now
+ g_io_gnutls_load(gpointer data). data can be NULL for a static
+ library. Look at this commit for the necessary change in the
+ examples.
+
+- various build issues on Android have been fixed.
+
+macOS and iOS
+
+- various build issues on iOS have been fixed.
+
+- the minimum required iOS version is now 9.0. The difference in
+ adoption between 8.0 and 9.0 is 0.1% and the bump to 9.0 fixes some
+ build issues.
+
+- The way that GIO modules are named has changed due to upstream GLib
+ natively adding support for loading static GIO modules. This means
+ that any GStreamer application using gnutls for SSL/TLS on the
+ Android or iOS platforms (or any other setup using static libraries)
+ will fail to link looking for the g_io_module_gnutls_load_static()
+ function. The new function name is now
+ g_io_gnutls_load(gpointer data). data can be NULL for a static
+ library. Look at this commit for the necessary change in the
+ examples.
+
+Windows
+
+- The webrtcdsp element is shipped again as part of the Windows binary
+ packages, the build system issue has been resolved.
+
+- ‘Inconsistent DLL linkage’ warnings when building with MSVC have
+ been fixed
+
+- Hardware-accelerated Nvidia video encoder/decoder plugins nvdec and
+ nvenc build on Windows now, also with MSVC and using Meson.
+
+- The ksvideosrc camera capture plugin supports 16-bit grayscale video
+ now
+
+- The wasapisrc audio capture element implements loopback recording
+ from another output device or sink
+
+- wasapisink recover from low buffer levels in shared mode and some
+ exclusive mode fixes
+
+- dshowsrc now implements the GstDeviceMonitor interface
+
+
+Contributors
+
+Aaron Boxer, Aleix Conchillo Flaqué, Alessandro Decina, Alexandru Băluț,
+Alex Ashley, Alexey Chernov, Alicia Boya García, Amit Pandya, Andoni
+Morales Alastruey, Andreas Frisch, Andre McCurdy, Andy Green, Anthony
+Violo, Antoine Jacoutot, Antonio Ospite, Arun Raghavan, Aurelien Jarno,
+Aurélien Zanelli, ayaka, Bananahemic, Bastian Köcher, Branko Subasic,
+Brendan Shanks, Carlos Rafael Giani, Charlie Turner, Christoph Reiter,
+Corentin Noël, Daeseok Youn, Damian Vicino, Dan Kegel, Daniel Drake,
+Daniel Klamt, Danilo Spinella, Dardo D Kleiner, David Ing, David
+Svensson Fors, Devarsh Thakkar, Dimitrios Katsaros, Edward Hervey,
+Emilio Pozuelo Monfort, Enrique Ocaña González, Erlend Eriksen, Ezequiel
+Garcia, Fabien Dessenne, Fabrizio Gennari, Florent Thiéry, Francisco
+Velazquez, Freyr666, Garima Gaur, Gary Bisson, George Kiagiadakis, Georg
+Lippitsch, Georg Ottinger, Geunsik Lim, Göran Jönsson, Guillaume
+Desmottes, H1Gdev, Haihao Xiang, Haihua Hu, Harshad Khedkar, Havard
+Graff, He Junyan, Hoonhee Lee, Hosang Lee, Hyunjun Ko, Ilya Smelykh,
+Ingo Randolf, Iñigo Huguet, Jakub Adam, James Stevenson, Jan Alexander
+Steffens, Jan Schmidt, Jerome Laheurte, Jimmy Ohn, Joakim Johansson,
+Jochen Henneberg, Johan Bjäreholt, John-Mark Bell, John Bassett, John
+Nikolaides, Jonathan Karlsson, Jonny Lamb, Jordan Petridis, Josep Torra,
+Joshua M. Doe, Jos van Egmond, Juan Navarro, Julian Bouzas, Jun Xie,
+Junyan He, Justin Kim, Kai Kang, Kim Tae Soo, Kirill Marinushkin, Kyrylo
+Polezhaiev, Lars Petter Endresen, Linus Svensson, Louis-Francis
+Ratté-Boulianne, Lucas Stach, Luis de Bethencourt, Luz Paz, Lyon Wang,
+Maciej Wolny, Marc-André Lureau, Marc Leeman, Marco Trevisan (Treviño),
+Marcos Kintschner, Marian Mihailescu, Marinus Schraal, Mark Nauwelaerts,
+Marouen Ghodhbane, Martin Kelly, Matej Knopp, Mathieu Duponchelle,
+Matteo Valdina, Matthew Waters, Matthias Fend, memeka, Michael Drake,
+Michael Gruner, Michael Olbrich, Michael Tretter, Miguel Paris, Mike
+Wey, Mikhail Fludkov, Naveen Cherukuri, Nicola Murino, Nicolas Dufresne,
+Niels De Graef, Nirbheek Chauhan, Norbert Wesp, Ognyan Tonchev, Olivier
+Crête, Omar Akkila, Pat DeSantis, Patricia Muscalu, Patrick Radizi,
+Patrik Nilsson, Paul Kocialkowski, Per Forlin, Peter Körner, Peter
+Seiderer, Petr Kulhavy, Philippe Normand, Philippe Renon, Philipp Zabel,
+Pierre Labastie, Piotr Drąg, Roland Jon, Roman Sivriver, Roman Shpuntov,
+Rosen Penev, Russel Winder, Sam Gigliotti, Santiago Carot-Nemesio,
+Sean-Der, Sebastian Dröge, Seungha Yang, Shi Yan, Sjoerd Simons, Snir
+Sheriber, Song Bing, Soon, Thean Siew, Sreerenj Balachandran, Stefan
+Ringel, Stephane Cerveau, Stian Selnes, Suhas Nayak, Takeshi Sato,
+Thiago Santos, Thibault Saunier, Thomas Bluemel, Tianhao Liu,
+Tim-Philipp Müller, Tobias Ronge, Tomasz Andrzejak, Tomislav Tustonić,
+U. Artie Eoff, Ulf Olsson, Varunkumar Allagadapa, Víctor Guzmán, Víctor
+Manuel Jáquez Leal, Vincenzo Bono, Vineeth T M, Vivia Nikolaidou, Wang
+Fei, wangzq, Whoopie, Wim Taymans, Wind Yuan, Wonchul Lee, Xabier
+Rodriguez Calvar, Xavier Claessens, Haihao Xiang, Yacine Bandou,
+Yeongjin Jeong, Yuji Kuwabara, Zeeshan Ali,
+
+… and many others who have contributed bug reports, translations, sent
+suggestions or helped testing.
+
+
+Stable 1.16 branch
+
+After the 1.16.0 release there will be several 1.16.x bug-fix releases
+which will contain bug fixes which have been deemed suitable for a
+stable branch, but no new features or intrusive changes will be added to
+a bug-fix release usually. The 1.16.x bug-fix releases will be made from
+the git 1.16 branch, which is a stable branch.
+
+1.16.0
+
+1.16.0 was released on 19 April 2019.
+
+1.16.1
+
+The first 1.16 bug-fix release (1.16.1) was released on 23 September
+2019.
+
+This release only contains bugfixes and it _should_ be safe to update
+from 1.16.0.
+
+Highlighted bugfixes in 1.16.1
+
+- GStreamer-vaapi: fix green frames and decoding artefacts in some
+ cases
+- OpenGL: fix wayland event source burning CPU in certain
+ circumstances
+- Memory leak fixes and memory footprint improvements
+- Performance improvements
+- Stability and security fixes
+- Fix enum for GST_MESSAGE_DEVICE_CHANGED which is technically an API
+ break, but this is only used internally in GStreamer and duplicated
+ another message enum
+- hls: Make crypto dependency optional when hls-crypto is auto
+- player: fix switching back and forth between forward and reverse
+ playback
+- decklinkaudiosink: Drop late buffers
+- openh264enc: Fix compilation with openh264 v2.0
+- wasapisrc: fix segtotal value being always 2
+- android: Fix gnutls issue causing a FORTIFY crash on Android Q
+- windows: Fix two crashes due to cross-CRT free when using MSVC
+
+gstreamer core
+
+- device: gst_device_create_element() is transfer floating, not
+ transfer full
+- filesink, fdsink: respect IOV_MAX for the writev iovec array
+ (Solaris)
+- miniobject: free qdata array when the last qdata is removed (reduces
+ memory footprint)
+- bin: Fix minor race when adding to a bin
+- aggregator: Actually handle NEED_DATA return from update_src_caps()
+- aggregator: Ensure that the source pad is created as a
+ GstAggregatorPad if no type is given in the pad template
+- latency: fix custom event leaks
+- registry: Use plugin directory from the build system for
+ relocateable Windows builds
+- message: fix up enum value for GST_MESSAGE_DEVICE_CHANGED
+- info: Fix deadlock in gst_ring_buffer_logger_log()
+- downloadbuffer: Check for flush after seek
+- identity: Non-live upstream have no max latency
+- identity: Fix the ts-offset property getter
+- aggregator: Make parsing of explicit sink pad names more robust
+- bufferpool: Fix the buffer size reset code
+- fakesink, fakesrc, identity: sync gst_buffer_get_flags_string() with
+ new flags
+- multiqueue: never unref queries we do not own
+- concat: Reset last_stop on FLUSH_STOP too
+- aggregator: fix flow-return boolean return type mismatch
+- gstpad: Handle probes that reset the data field
+- gst: Add support for g_autoptr(GstPromise)
+- gst-inspect: fix unused-const-variable error in windows
+- base: Include gstbitwriter.h in the single-include header
+- Add various Since: 1.16 markers
+- GST_MESSAGE_DEVICE_CHANGED duplicates GST_MESSAGE_REDIRECT
+- Targetting wrong meson version
+- meson: Make get_flex_version.py script executable
+- meson: Link to objects instead of static helper library
+- meson: set correct install path for gdb helper
+- meson: fix warning about configure_file() install kwarg
+
+gst-plugins-base
+
+- video-info: parse field-order for all interleaved formats
+- tests: fix up valgrind suppressions for glibc getaddrinfo leaks
+- meson: Reenable NEON support (in audio resampler)
+- audio-resampler: Update NEON to handle remainders not multiples of 4
+- eglimage: Fix memory leak
+- audiodecoder: Set output caps with negotiated caps to avoid critical
+ info printed
+- video-frame: Take TFF flag from the video info if it was set in
+ there
+- glcolorconvert: Fix external-oes shader
+- video-anc: Fix ADF detection when trying to extract data from vanc
+- gl/wayland: fix wayland event source burning CPU
+- configure: add used attribute in order to make NEON detection
+ working with -flto.
+- audioaggregator: Return a valid rate range from caps query if
+ downstream supports a whole range
+- rtspconnection: data-offset increase not set
+- rtpsconnection: Fix number of n_vectors
+- video-color: Add compile-time assert for ColorimetryInfo enum
+- audiodecoder: Fix leak on failed audio gaps
+- glupload: Keep track of cached EGLImage texture format
+- playsink: Set ts-offset to text sink.
+- meson.build: use join_paths() on prefix
+- compositor: copy frames as-is when possible
+- compositor: Skip background when a pad obscures it completely
+- rtspconnection: Start CSeq at 1 (some servers don’t cope well with
+ seqnum 0)
+- viv-fb: fix build break for GST_GL_API
+- gl/tests: fix shader creation tests part 2
+- gl/tests: fix shader creation tests
+- wayland: set the event queue also for the xdg_wm_base object
+- video: Added GI annotation for gstvideoaffinetransformationmeta
+ apply_matrix
+- compositor: Remove unneeded left shift for ARGB/AYUV SOURCE operator
+- Colorimetry fixes
+- alsasrc: Don’t use driver timestamp if it’s zero
+- gloverlaycompositor: fix crash if buffer doesn’t have video meta
+- meson: Don’t try to find gio-unix on Windows
+- glshader: fix default external-oes shaders
+- subparse: fix pushing WebVTT cue with no newline at the end
+- meson: Missing “android” choice in gl_winsys
+- video test: Keep BE test inline with LE test
+- id3tag: Correctly validate the year from v1 tags before passing to
+ GstDateTime
+- gl/wayland: Don’t prefix wl_shell struct field
+- eglimage: Add compatibility define for DRM_FORMAT_NV24
+- Add various Since: 1.16 markers
+- video-anc: Handle SD formats correctly
+- Docs: add GL_CFLAGS to GTK_DOC_CFLAGS
+- GL: using vaapi and showing on glimagesink on wayland loads one core
+ for 100% on 1.16
+- GL: external-oes shader places precision qualifier before #extension
+ (was: androidmedia amcviddec fail after 1.15.90 1.16.0 update)
+
+gst-plugins-good
+
+- alpha: Fix one_over_kc calculation on arm/aarch64
+- souphttpsrc: Fix incompatible type build warning
+- rtpjitterbuffer: limit max-dropout-time to maxint32
+- rtpjitterbuffer: Clear clock master before unreffing
+- qtdemux: Use empty-array safe way to cleanup GPtrArray
+- v4l2: Fix type compatibility issue with glibc 2.30
+- valgrind: suppress Cond error coming from gnutls and Ignore leaks
+ caused by shout/sethostent
+- rtpfunnel: forward correct segment when switching pad
+- gtkglsink: fix crash when widget is resized after element
+ destruction
+- jpegdec: Don’t dereference NULL input state if we have no caps in
+ TIME segments
+- rtp: opuspay: fix memory leak in gst_rtp_opus_pay_setcaps
+- v4l2videodec: return right type for drain.
+- rtpssrcdemux: Avoid taking streamlock out-of-band
+- Support v4l2src buffer orphaning
+- splitmuxsink: Only set running time on finalizing sink element when
+ in async-finalize mode
+- rtpsession: Always keep at least one NACK on early RTCP
+- rtspsrc: do not try to send EOS with invalid seqnum
+- rtpsession: Call on-new-ssrc earlier
+- rtprawdepay: Don’t get rid of the buffer pool on FLUSH_STOP
+- rtpbin: Free storage when freeing session
+- scaletempo: Advertise interleaved layout in caps templates
+- Support v4l2src buffer orphaning
+
+gst-plugins-bad
+
+- hls: Make crypto dependency optional when hls-crypto is auto
+- player: fix switching back and forth between forward and reverse
+ playback
+- decklinkaudiosink: Drop late buffers
+- srt: Add stats property, include sender-side statistics and fix a
+ crash
+- dshowsrcwrapper: fix regression on device selection
+- tsdemux: Limit the maximum PES payload size
+- wayland: Define libdrm_dep in meson.build to fix meson configure
+ error when kms is disabled
+- sctp: Fix crash on free() when using the MSVC binaries
+- webrtc: Fix signals documentation
+- h264parse: don’t critical on VUI parameters > 2^31
+- rtmp: Fix crash inside free() with MSVC on Windows
+- iqa: fix leak of map_meta.data
+- d3dvideosink: Fix crash on WinProc handler
+- amc: Fix crash when a sync_meta survives its sink
+- pitch: Fix race between putSamples() and setting soundtouch
+ parameters
+- webrtc: fix type of max-retransmits, make it work
+- mxfdemux: Also allow picture essence element type 0x05 for VC-3
+- wasapi: fix symbol redefinition build error
+- decklinkvideosrc: Retrieve mode of the ancillary data from the frame
+- decklinkaudiosrc/decklinkvideosrc: Do nothing in
+ BaseSrc::negotiate() and…
+- adaptivedemux: do not retry downloads during shutdown.
+- webrtcbin: fix GInetAddress leak
+- dtls: fix dtls connection object leak
+- siren: fix a global buffer overflow spotted by asan
+- kmssink: Fix implicit declaration build error
+- Fix -Werror=return-type error in configure.
+- aiff: Fix infinite loop in header parsing.
+- nvdec: Fix possible frame drop on EOS
+- srtserversrc: yields malformed rtp payloads
+- srtsink: Fix crash in case no URI
+- dtlsagent: Fix leaked dtlscertificate
+- meson: bluez: Early terminate configure on Windows
+- decklink: Correctly ensure >=16 byte alignment for the buffers we
+ allocate
+- webrtcbin: fix DTLS when receivebin is set to DROP
+- zbar: Include running-time, stream-time and duration in the messages
+- uvch264src: Make sure we set our segment
+- avwait: Allow start and end timecode to be set back to NULL
+- avwait: Don’t print warnings for every buffer passed
+- hls/meson: fix dependency logic
+- Waylandsink gnome shell workaround
+- avwait: Allow setting start timecode after end timecode; protect
+ propeties with mutex
+- wayland/wlbuffer: just return if used_by_compositor is true when
+ attach
+- proxy: Set SOURCE flag on the source and SINK flag on the sink
+- ivfparse: Check the data size against IVF_FRAME_HEADER_SIZE
+- webrtc: Add various Since markers to new types after 1.14.0
+- msdk: fix the typo in debug category
+- dtlsagent: Do not overwrite openssl locking callbacks
+- meson: Fix typo in gsm header file name
+- srt: handle races in state change
+- webrtc: Add g_autoptr() support for public types
+- openh264enc: Fix compilation with openh264 v2.0
+- meson: Allow CUDA_PATH fallback on linux
+- meson: fix build with opencv=enabled and opencv4. Fixes #964
+- meson: Add support for the colormanagement plugin
+- autotools: gstsctp: set LDFLAGS
+- nvenc/nvdec: Add NVIDIA SDK headers to noinst_HEADERS
+- h264parse: Fix typo when setting multiview mode and flags
+- Add various Since: 1.16 markers
+- opencv: allow compilation against 4.1.x
+- Backport of some minor srt commits without MR into 1.16
+- meson: fix build with opencv=enabled and opencv4
+- wasapisrc: fix segtotal value being always 2 due to an unused
+ variable
+- meson: colormanagement missing
+- androidmedia amcviddec fail after 1.15.90 1.16.0 update
+
+gst-plugins-ugly
+
+- meson: Always require the gmodule dependency
+
+gst-libav
+
+- docs: don’t include the type hierarchy, fixing build with gtk-doc
+ 1.30
+- avvidenc: Correctly signal interlaced input to ffmpeg when the input
+ caps are interlaced
+- autotools: add bcrypt to win32 libs
+- gstav: Use libavcodec util function for version check
+- API documentation fails to build with gtk-doc 1.30
+
+gst-rtsp-server
+
+- rtsp-client: RTP Info must exist in PLAY response
+- onvif-media: fix “void function returning a value” compiler warning
+- Add various Since: 1.16 markers
+
+gstreamer-vaapi
+
+- fix egl context leak and display creation race
+- pluginutil: Remove Mesa from drivers white list
+- Classify vaapidecodebin as a hardware decoder
+- Fix two leak
+- vaapivideomemory: demote error message to info
+- encoder: vp8,vp9: reset frame_counter when input frame’s format
+ changes
+- encoder: mpeg2: No packed header for SPS and PPS
+- decoder: vp9: clear parser pointer after release
+- encoder: Fixes deadlock in change state function
+- encoder: h265: reset num_ref_idx_l1_active_minus1 when low delay B.
+- encoder: not call ensure_num_slices inside g_assert()
+- encoder: continue if roi meta is NULL
+- decoder: vp9: Set chroma_ ype by VP9 bit_depth
+- vaapipostproc: don’t do any color conversion when GL_TEXTURE_UPLOAD
+- libs: surface: fix double free when dmabuf export fails
+- h264 colors and artifacts upon upgrade to GStreamer Core Library
+ version 1.15.90
+
+gst-editing-services
+
+- element: Properly handle the fact that pasting can return NULL
+- Add various missing Since markers
+- launch: Fix caps restriction short names
+- python: Avoid warning about using deprecated methods
+- video-transition: When using non crossfade effect use ‘over’
+ operations
+- meson: Generate a pkgconfig file for the GES plugin
+
+gst-devtools
+
+- launcher: testsuites: skip systemclock stress tests
+- validate: fix build on macOS
+
+gst-build
+
+- Update win flex bison binaries
+- Update the flexmeson windows binary version
+- Don’t allow people to run meson inside the uninstalled env
+
+Cerbero build tool and packaging changes in 1.16.1
+
+- cerbero: Add enums for Fedora 30, Fedora 31 and Debian bullseye
+- gnutls.recipe: Fix crash when running on Android Q
+- recipes: Upgrade openssl to 1.1.1c
+- Fix some typos
+- add support for vs build tools 2019, fixes #183
+- android: Adjust gstreamer-1.0.mk for NDK r20
+- Fix license enums
+- bootstrap: Fix dnf usage on CentOS
+- Make _add_system_libs reentrant
+- meson.recipe: Fix setting of bitcode compiler options
+- cerbero: support Ubuntu disco dingo
+- cerbero: Set utf-8 to execution character set also on MSVC
+- git: simplify the reset of the source branch.
+- FORTIFY: %n not allowed on Android Q
+- Fails to build if there’s no license file for the given license
+ (GPL/LGPL without Plus, Proprietary, …)
+
+Contributors to 1.16.1
+
+Aaron Boxer, Adam Duskett, Alicia Boya García, Andoni Morales Alastruey,
+Antonio Ospite, Arun Raghavan, Askar Safin, A. Wilcox, Charlie Turner,
+Christoph Reiter, Damian Hobson-Garcia, Daniel Klamt, Danny Smith, David
+Gunzinger, David Ing, David Svensson Fors, Doug Nazar, Edward Hervey,
+Eike Hein, Fabrice Bellet, Fernando Herrrera, Georg Lippitsch, Göran
+Jönsson, Guillaume Desmottes, Haihao Xiang, Haihua Hu, Håvard Graff, Hou
+Qi, Ignacio Casal Quinteiro, Ilya Smelykh, Jan Schmidt, Javier Celaya,
+Jim Mason, Jonas Larsson, Jordan Petridis, Jose Antonio Santos Cadenas,
+Juan Navarro, Knut Andre Tidemann, Kristofer Björkström, Lucas Stach,
+Marco Felsch, Marcos Kintschner, Mark Nauwelaerts, Martin Liska, Martin
+Theriault, Mathieu Duponchelle, Matthew Waters, Michael Olbrich, Mike
+Gorse, Nicola Murino, Nicolas Dufresne, Niels De Graef, Niklas
+Hambüchen, Nirbheek Chauhan, Olivier Crête, Philippe Normand, Ross
+Burton, Sebastian Dröge, Seungha Yang, Song Bing, Thiago Santos,
+Thibault Saunier, Thomas Coldrick, Tim-Philipp Müller, Víctor Manuel
+Jáquez Leal, Vivia Nikolaidou, Xavier Claessens, Yeongjin Jeong,
+
+… and many others who have contributed bug reports, translations, sent
+suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.16.1
+
+- List of Merge Requests applied in 1.16
+- List of Issues fixed in 1.16.1
+
+1.16.2
+
+The second 1.16 bug-fix release (1.16.2) was released on 03 December
+2019.
+
+This release only contains bugfixes and it _should_ be safe to update
+from 1.16.1.
+
+Highlighted bugfixes in 1.16.2
+
+- Interlaced video scaling fixes
+- CineForm video support in AVI
+- audioresample: avoid glitches due to rounding errors after changing
+ rate
+- Command line tool output printing improvements on Windows
+- various performance improvements, memory leak fixes and security
+ fixes
+- VP9 decoding fixes
+- avfvideosrc: Explicitly request video permission on macOS 10.14+
+- wasapi: bug fixes and stability improvements
+- webrtc-audio-processing: fix segmentation fault on 32-bit windows
+- tsdemux: improved handling of certain discontinuities
+- vaapi h265 decoder: wait for I-frame before trying to decode
+
+gstreamer
+
+- gst-launch: Fix ugly stdout on Windows
+- tee: Make sure to actually deactivate pads that are released
+- bin: Drop need-context messages without source instead of crashing
+- gst: Don’t pass miniobjects to GST_DEBUG_OBJECT() and similar macros
+- tracers: Don’t leak temporary GstStructure
+
+gst-plugins-base
+
+- xvimagepool: Update size, stride, and offset with allocated XvImage
+- video-converter: Fix RGB-XYZ-RGB conversion
+- audiorate: Update next_offset on rate change
+- audioringbuffer: Reset reorder flag before check
+- audio-buffer: Don’t fail to map buffers with zero samples
+- videorate: Fix max-duplication-time handling
+- gl/gbm: ensure we call the resize callback before attempting to draw
+- video-converter: Various fixes for interlaced scaling
+- gstrtspconnection: messages_bytes not decreased
+- check: Don’t use real audio devices for tests
+- riff: add CineForm mapping
+- glfilters: Don’t use static variables for storing per-element state
+- glupload: Add VideoMetas and GLSyncMeta to the raw uploaded buffers
+- streamsynchronizer: avoid pad release race during logging.
+- gst-play: Use gst_print* to avoid broken stdout string on Windows
+
+gst-plugins-good
+
+- vp9dec: Fix broken 4:4:4 8bits decoding
+- rtpsession: add locking for clear-pt-map
+- rtpL16depay: don’t crash if data is not modulo channels*width
+- wavparse: Fix push mode ignoring audio with a size smaller than
+ segment buffer
+- wavparse: Fix push mode ignoring last audio payload chunk
+- aacparse: fix wrong offset of the channel number in adts header
+- jpegdec: Fix incorrect logic in EOI tag detection
+- videocrop: Also update the coordinate when in-place
+- jpegdec: don’t overwrite the last valid line
+- vpx: Error out if enabled and no features found
+- v4l2videodec: ensure pool exists before orphaning it
+- v4l2videoenc: fix type conversion errors
+- v4l2bufferpool: Queue number of allocated buffers to capture
+- v4l2object: fix mpegversion number typo
+- v4l2object: Work around bad TRY_FMT colorimetry implementations
+
+gst-plugins-bad
+
+- avfvideosrc: Explicitly request video permission on macOS 10.14+
+- wasapi: Various fixes and a workaround for a specific driver bug
+- wasapi: Move to CoInitializeEx for COM initialization
+- wasapi: Fix runtime/build warnings
+- waylandsink: Commit the parent after creating subsurface
+- msdkdec: fix surface leak in msdkdec_handle_frame
+- tsmux: Fix copying of buffer region
+- tsdemux: Handle continuity mismatch in more cases
+- tsdemux: Always issue a DTS even when it’s equal to PTS
+- openexr: Fix build with OpenEXR 2.4 (and also OpenEXR 2.2 on Ubuntu
+ 18.04)
+- ccextractor: Always forward all sticky events to the caption pad
+- pnmdec: Return early on ::finish() if we have no actual data to
+ parse
+- ass: avoid infinite unref loop with bad data
+- fluidsynth: add sf3 to soundfont search path
+- webrtcdsp/webrtcechoprobe segmentation fault on windows (1.16.0 x86)
+
+gst-libav
+
+- avvidenc: Fix error propagation
+- avdemux: Fix segmentation fault if long_name is NULL
+- avviddec: Fix huge leak caused by circular reference
+- avviddec: Enforce allocate new AVFrame per input frame
+- avdec_mpeg2video (and probably more): Huge memory leak in git master
+
+gst-rtsp-server
+
+- rtsp-media: Use lock in gst_rtsp_media_is_receive_only
+- rtsp-client: RTP Info when completed_sender
+- rtsp-client: fix location uri-format by getting uri directly from
+ context instead
+
+gstreamer-vaapi
+
+- meson build: halt configuration if no renderer API
+- libs: decoder: h265: skip all pictures prior the first I-frame
+- libs: window: x11: Avoid usage of deprecated API
+
+gst-editing-services
+
+- Initialize debug categories before usage
+
+gst-build
+
+- gst-env: Use locally built GStreamer utility programs
+
+Cerbero build tool and packaging changes in 1.16.2
+
+General
+
+- openssl: Update to 1.1.1d
+- Updated ffmpeg, expat, flac, freetype, croco, ogg, xml2, mpg123,
+ openjpeg, opus, pixman, speex, tiff recipes
+- Fix setting of git credentials in local source repos
+
+Windows
+
+- webrtc-audio-processing: fix segmentation fault on 32-bit windows
+ with webrtcdsp/webrtcechoprobe elemens
+- vpx plugin has no features when built with Visual Studio 2019
+- libvpx: Add support for Visual Studio 2019
+- mingw-runtime.recipe: Correctly package pkg-config in the MSI
+- GIO doesn’t load any modules on Windows with MSVC, which breaks TLS
+ support since glib-networking’s giognutls module isn’t loaded
+- Make the instructions for running Cerbero the same on all platforms
+
+macOS + iOS
+
+- Add support for macOS 10.15 Catalina
+- Updates for Xcode 11
+- macos/ios: expose objc++ compilers in env variables
+- srt.recipe: Fix crash in constructor on iOS
+- osx-framework.recipe: Dynamically generate the list of libraries and
+ ship pkg-config
+- macos: add -mmacosx-version-min for framework
+- gstreamer-1.0-osx-framework.recipe contains an outdated hard-coded
+ list of libraries
+- We need to ship pkg-config with macOS
+
+Linux
+
+- Fix filesprovider.find_shlib_regex when a lib_suffix is used in the
+ cerbero config file
+
+Contributors to 1.16.2
+
+Adam Nilsson, Amr Mahdi, Angus Ao, Charlie Turner, Edward Hervey, Fabian
+Greffrath, Fuwei Tang, Havard Graff, Hu Qian, James Cowgill, Jan
+Alexander Steffens (heftig), Jeffy Chen, Jeremy Lempereur, Joakim
+Johansson, Jochen Henneberg, Julien Isorce, Kevin Joly, Kristofer
+Bjorkstrom, Kyrylo Polezhaiev, Matthew Waters, Michael Olbrich, Muhammet
+Ilendemli, Nicolas Dufresne, Nirbheek Chauhan, Pablo Marcos Oltra, Roman
+Shpuntov, Ruben Gonzalez, Scott Kanowitz, Sebastian Dröge, Seungha Yang,
+Thibault Saunier, Tim-Philipp Müller, Víctor Manuel Jáquez Leal, Vivia
+Nikolaidou,
+
+… and many others who have contributed bug reports, translations, sent
+suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.16.2
+
+- List of Merge Requests applied in 1.16
+- List of Issues fixed in 1.16.2
+
+
+Known Issues
+
+- possibly breaking/incompatible changes to properties of wrapped
+ FFmpeg decoders and encoders (see above).
+
+- The way that GIO modules are named has changed due to upstream GLib
+ natively adding support for loading static GIO modules. This means
+ that any GStreamer application using gnutls for SSL/TLS on the
+ Android or iOS platforms (or any other setup using static libraries)
+ will fail to link looking for the g_io_module_gnutls_load_static()
+ function. The new function name is now
+ g_io_gnutls_load(gpointer data). See Android/iOS sections above for
+ further details.
+
+
+Schedule for 1.18
+
+Our next major feature release will be 1.18, and 1.17 will be the
+unstable development version leading up to the stable 1.18 release. The
+development of 1.17/1.18 will happen in the git master branch.
+
+The plan for the 1.18 development cycle is yet to be confirmed, but it
+is now expected that feature freeze will take place in December 2019,
+with the first 1.18 stable release ready in late January or February.
+
+1.18 will be backwards-compatible to the stable 1.16, 1.14, 1.12, 1.10,
+1.8, 1.6, 1.4, 1.2 and 1.0 release series.
+
+------------------------------------------------------------------------
+
+_These release notes have been prepared by Tim-Philipp Müller with_
+_contributions from Sebastian Dröge, Guillaume Desmottes, Matthew
+Waters, _ _Thibault Saunier, and Víctor Manuel Jáquez Leal._
+
+_License: CC BY-SA 4.0_
--- /dev/null
+GStreamer Editing Services
+--------------------------
+
+ This is a high-level library for facilitating the creation of audio/video
+non-linear editors.
+
+License:
+--------
+
+ This package and its contents are licensend under the GNU Lesser General
+Public License (LGPL).
+
+Dependencies:
+-------------
+
+ * GStreamer core
+ * gst-plugins-base
+
--- /dev/null
+This is GStreamer gst-editing-services 1.16.2.
+
+The GStreamer team is pleased to announce another bug-fix release in the
+stable 1.x API series of your favourite cross-platform multimedia framework!
+
+The 1.16 release series adds new features on top of the 1.14 series and is
+part of the API and ABI-stable 1.x release series of the GStreamer multimedia
+framework.
+
+Full release notes will one day be found at:
+
+ https://gstreamer.freedesktop.org/releases/1.16/
+
+Binaries for Android, iOS, Mac OS X and Windows will usually be provided
+shortly after the release.
+
+This module will not be very useful by itself and should be used in conjunction
+with other GStreamer modules for a complete multimedia experience.
+
+ - gstreamer: provides the core GStreamer libraries and some generic plugins
+
+ - gst-plugins-base: a basic set of well-supported plugins and additional
+ media-specific GStreamer helper libraries for audio,
+ video, rtsp, rtp, tags, OpenGL, etc.
+
+ - gst-plugins-good: a set of well-supported plugins under our preferred
+ license
+
+ - gst-plugins-ugly: a set of well-supported plugins which might pose
+ problems for distributors
+
+ - gst-plugins-bad: a set of plugins of varying quality that have not made
+ their way into one of core/base/good/ugly yet, for one
+ reason or another. Many of these are are production quality
+ elements, but may still be missing documentation or unit
+ tests; others haven't passed the rigorous quality testing
+ we expect yet.
+
+ - gst-libav: a set of codecs plugins based on the ffmpeg library. This is
+ where you can find audio and video decoders and encoders
+ for a wide variety of formats including H.264, AAC, etc.
+
+ - gstreamer-vaapi: hardware-accelerated video decoding and encoding using
+ VA-API on Linux. Primarily for Intel graphics hardware.
+
+ - gst-omx: hardware-accelerated video decoding and encoding, primarily for
+ embedded Linux systems that provide an OpenMax
+ implementation layer such as the Raspberry Pi.
+
+ - gst-rtsp-server: library to serve files or streaming pipelines via RTSP
+
+ - gst-editing-services: library an plugins for non-linear editing
+
+==== Download ====
+
+You can find source releases of gstreamer in the download
+directory: https://gstreamer.freedesktop.org/src/gstreamer/
+
+The git repository and details how to clone it can be found at
+https://gitlab.freedesktop.org/gstreamer/
+
+==== Homepage ====
+
+The project's website is https://gstreamer.freedesktop.org/
+
+==== Support and Bugs ====
+
+We have recently moved from GNOME Bugzilla to GitLab on freedesktop.org
+for bug reports and feature requests:
+
+ https://gitlab.freedesktop.org/gstreamer
+
+Please submit patches via GitLab as well, in form of Merge Requests. See
+
+ https://gstreamer.freedesktop.org/documentation/contribute/
+
+for more details.
+
+For help and support, please subscribe to and send questions to the
+gstreamer-devel mailing list (see below for details).
+
+There is also a #gstreamer IRC channel on the Freenode IRC network.
+
+==== Developers ====
+
+GStreamer source code repositories can be found on GitLab on freedesktop.org:
+
+ https://gitlab.freedesktop.org/gstreamer
+
+and can also be cloned from there and this is also where you can submit
+Merge Requests or file issues for bugs or feature requests.
+
+Interested developers of the core library, plugins, and applications should
+subscribe to the gstreamer-devel list:
+
+ https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
--- /dev/null
+#!/bin/sh
+#
+# gst-editing-services autogen.sh
+#
+# Run this to generate all the initial makefiles, etc.
+#
+# This file has been generated from common/autogen.sh.in via common/update-autogen
+
+
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
+
+olddir=`pwd`
+cd "$srcdir"
+
+package=gst-editing-services
+srcfile=gst-editing-services.doap
+
+# Make sure we have common
+if test ! -f common/gst-autogen.sh;
+then
+ echo "+ Setting up common submodule"
+ git submodule init
+fi
+git submodule update
+
+# source helper functions
+if test ! -f common/gst-autogen.sh;
+then
+ echo There is something wrong with your source tree.
+ echo You are missing common/gst-autogen.sh
+ exit 1
+fi
+. common/gst-autogen.sh
+
+# install pre-commit hook for doing clean commits
+if test ! \( -x .git/hooks/pre-commit -a -L .git/hooks/pre-commit \);
+then
+ rm -f .git/hooks/pre-commit
+ if ! ln -s ../../common/hooks/pre-commit.hook .git/hooks/pre-commit 2> /dev/null
+ then
+ echo "Failed to create commit hook symlink, copying instead ..."
+ cp common/hooks/pre-commit.hook .git/hooks/pre-commit
+ fi
+fi
+
+# GNU gettext automake support doesn't get along with git.
+# https://bugzilla.gnome.org/show_bug.cgi?id=661128
+if test -d po ; then
+ touch -t 200001010000 po/gst-editing-services-1.0.pot
+fi
+
+CONFIGURE_DEF_OPT='--enable-maintainer-mode --enable-gtk-doc'
+
+if test "x$package" = "xgstreamer"; then
+ CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --enable-failing-tests --enable-poisoning"
+elif test "x$package" = "xgst-plugins-bad"; then
+ CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-player-tests"
+fi
+
+autogen_options $@
+
+printf "+ check for build tools"
+if test -z "$NOCHECK"; then
+ echo
+
+ printf " checking for autoreconf ... "
+ echo
+ which "autoreconf" 2>/dev/null || {
+ echo "not found! Please install the autoconf package."
+ exit 1
+ }
+
+ printf " checking for pkg-config ... "
+ echo
+ which "pkg-config" 2>/dev/null || {
+ echo "not found! Please install pkg-config."
+ exit 1
+ }
+else
+ echo ": skipped version checks"
+fi
+
+# if no arguments specified then this will be printed
+if test -z "$*" && test -z "$NOCONFIGURE"; then
+ echo "+ checking for autogen.sh options"
+ echo " This autogen script will automatically run ./configure as:"
+ echo " ./configure $CONFIGURE_DEF_OPT"
+ echo " To pass any additional options, please specify them on the $0"
+ echo " command line."
+fi
+
+toplevel_check $srcfile
+
+# autopoint
+if test -d po && grep ^AM_GNU_GETTEXT_VERSION configure.ac >/dev/null ; then
+ tool_run "autopoint" "--force"
+fi
+
+# aclocal
+if test -f acinclude.m4; then rm acinclude.m4; fi
+
+autoreconf --force --install || exit 1
+
+test -n "$NOCONFIGURE" && {
+ echo "+ skipping configure stage for package $package, as requested."
+ echo "+ autogen.sh done."
+ exit 0
+}
+
+cd "$olddir"
+
+echo "+ running configure ... "
+test ! -z "$CONFIGURE_DEF_OPT" && echo " default flags: $CONFIGURE_DEF_OPT"
+test ! -z "$CONFIGURE_EXT_OPT" && echo " external flags: $CONFIGURE_EXT_OPT"
+echo
+
+echo "$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT
+"$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT || {
+ echo " configure failed"
+ exit 1
+}
+
+echo "Now type 'make' to compile $package."
--- /dev/null
+SUBDIRS =
+
+if WITH_PYTHON
+ SUBDIRS += python
+endif
+
--- /dev/null
+SUBDIRS = gi
--- /dev/null
+# GStreamer
+#
+# Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+# Boston, MA 02110-1335, USA.
+
+import os
+from gi.repository import Gst, GES, GLib
+
+
+class Simple:
+ def __init__(self, uri):
+ timeline = GES.Timeline.new_audio_video()
+ self.project = timeline.get_asset()
+
+ self.project.connect("asset-added", self._asset_added_cb)
+ self.project.connect("error-loading-asset", self._error_loading_asset_cb)
+ self.project.create_asset(uri, GES.UriClip)
+ self.layer = timeline.append_layer()
+ self._create_pipeline(timeline)
+ self.loop = GLib.MainLoop()
+
+ def _create_pipeline(self, timeline):
+ self.pipeline = GES.Pipeline()
+ self.pipeline.set_timeline(timeline)
+ bus = self.pipeline.get_bus()
+ bus.add_signal_watch()
+ bus.connect("message", self.bus_message_cb)
+
+ def bus_message_cb(self, unused_bus, message):
+ if message.type == Gst.MessageType.EOS:
+ print "eos"
+ self.loop.quit()
+ elif message.type == Gst.MessageType.ERROR:
+ error = message.parse_error()
+ print "error %s" % error[1]
+ self.loop.quit()
+
+ def start(self):
+ self.loop.run()
+
+ def _asset_added_cb(self, project, asset):
+ self.layer.add_asset(asset, 0, 0, Gst.SECOND * 5, GES.TrackType.UNKNOWN)
+ self.pipeline.set_state(Gst.State.PLAYING)
+
+ def _error_loading_asset_cb(self, project, error, asset_id, type):
+ print "Could not load asset %s: %s" % (asset_id, error)
+ self.loop.quit()
+
+if __name__ == "__main__":
+ if len(os.sys.argv) != 2:
+ print "You must specify a file URI"
+ exit(-1)
+
+ Gst.init(None)
+ GES.init()
+ simple = Simple(os.sys.argv[1])
+ simple.start()
--- /dev/null
+SUBDIRS = overrides
--- /dev/null
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# GES.py
+#
+# Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser 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.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+
+import sys
+from ..overrides import override
+from ..importer import modules
+
+
+if sys.version_info >= (3, 0):
+ _basestring = str
+ _callable = lambda c: hasattr(c, '__call__')
+else:
+ _basestring = basestring
+ _callable = callable
+
+GES = modules['GES']._introspection_module
+__all__ = []
+
+if GES._version == '0.10':
+ import warnings
+ warn_msg = "You have imported the GES 0.10 module. Because GES 0.10 \
+was not designed for use with introspection some of the \
+interfaces and API will fail. As such this is not supported \
+by the GStreamer development team and we encourage you to \
+port your app to GES 1 or greater. static python bindings is the recomended \
+python module to use with GES 0.10"
+
+ warnings.warn(warn_msg, RuntimeWarning)
+
+
+class TrackElement(GES.TrackElement):
+ def set_child_property(self, prop_name, prop_value):
+ return TimelineElement.set_child_property(self, prop_name, prop_value)
+
+
+TrackElement = override(TrackElement)
+__all__.append('TrackElement')
+
+
+class TimelineElement(GES.TimelineElement):
+ def __repr__(self):
+ return "%s [%s (%s) %s]" % (
+ self.props.name,
+ Gst.TIME_ARGS(self.props.start),
+ Gst.TIME_ARGS(self.props.in_point),
+ Gst.TIME_ARGS(self.props.duration),
+ )
+
+ def set_child_property(self, prop_name, prop_value):
+ res, child, unused_pspec = GES.TimelineElement.lookup_child(self, prop_name)
+ if not res:
+ return res
+
+ child.set_property(prop_name, prop_value)
+ return res
+
+
+TimelineElement = override(TimelineElement)
+__all__.append('TimelineElement')
+
+
+try:
+ from gi.repository import Gst
+ Gst
+except:
+ raise RuntimeError("GSt couldn't be imported, make sure you have gst-python installed")
--- /dev/null
+pygesdir = $(pkgpyexecdir)
+pyges_PYTHON = GES.py
+
+EXTRA_DIST = GES.py
--- /dev/null
+#!/usr/bin/env python
+#
+# __init__.py
+#
+# Copyright (C) 2012 Thibault Saunier <thibaul.saunier@collabora.com>
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser 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.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+
+from pkgutil import extend_path
+
+__path__ = extend_path(__path__, __name__)
--- /dev/null
+install_data(['gi/overrides/GES.py'], install_dir: pygi_override_dir)
\ No newline at end of file
--- /dev/null
+Subproject commit 59cb678164719ff59dcf6c8b93df4617a1075d11
--- /dev/null
+AC_PREREQ(2.62)
+
+dnl initialize autoconf
+dnl when going to/from release please set the nano (fourth number) right !
+dnl releases only do Wall, cvs and prerelease does Werror too
+AC_INIT(GStreamer Editing Services, 1.16.2,
+ http://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer,
+ gstreamer-editing-services)
+
+AG_GST_INIT
+
+dnl initialize automake
+AM_INIT_AUTOMAKE([-Wno-portability 1.11 no-dist-gzip dist-xz tar-ustar subdir-objects])
+
+dnl define PACKAGE_VERSION_* variables
+AS_VERSION
+
+dnl check if this is a release version
+AS_NANO(GST_GIT="no", GST_GIT="yes")
+
+dnl can autoconf find the source ?
+AC_CONFIG_SRCDIR([ges/ges-timeline.c])
+
+dnl define the output header for config
+AC_CONFIG_HEADERS([config.h])
+
+dnl AM_MAINTAINER_MODE only provides the option to configure to enable it
+AM_MAINTAINER_MODE([enable])
+
+dnl sets host_* variables
+AC_CANONICAL_HOST
+
+dnl use pretty build output with automake >= 1.11
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],
+ [AM_DEFAULT_VERBOSITY=1
+ AC_SUBST(AM_DEFAULT_VERBOSITY)])
+
+dnl GES versioning, this is mostly informational
+GES_VERSION_MAJOR=$PACKAGE_VERSION_MAJOR
+GES_VERSION_MINOR=$PACKAGE_VERSION_MINOR
+GES_VERSION_MICRO=$PACKAGE_VERSION_MICRO
+GES_VERSION_NANO=$PACKAGE_VERSION_NANO
+AC_SUBST(GES_VERSION_MAJOR)
+AC_SUBST(GES_VERSION_MINOR)
+AC_SUBST(GES_VERSION_MICRO)
+AC_SUBST(GES_VERSION_NANO)
+
+dnl our libraries and install dirs use major.minor as a version
+GST_API_VERSION=$GST_EDITING_SERVICES_VERSION_MAJOR.$GST_EDITING_SERVICES_VERSION_MINOR
+dnl we override it here if we need to for the release candidate of new series
+GST_API_VERSION=1.0
+AC_SUBST(GST_API_VERSION)
+
+AS_LIBTOOL(GST, 1602, 0, 1602)
+
+dnl *** required versions of GStreamer stuff ***
+GST_REQ=1.16.2
+GSTPB_REQ=1.16.2
+
+dnl *** autotools stuff ****
+
+dnl allow for different autotools
+AS_AUTOTOOLS_ALTERNATE
+
+dnl Add parameters for aclocal
+AC_SUBST(ACLOCAL_AMFLAGS, "-I m4 -I common/m4")
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl *** check for arguments to configure ***
+
+AG_GST_ARG_DISABLE_FATAL_WARNINGS
+AG_GST_ARG_ENABLE_EXTRA_CHECKS
+
+AG_GST_ARG_DEBUG
+AG_GST_ARG_PROFILING
+AG_GST_ARG_VALGRIND
+AG_GST_ARG_GCOV
+
+AG_GST_ARG_EXAMPLES
+
+AG_GST_ARG_WITH_PKG_CONFIG_PATH
+AG_GST_ARG_WITH_PACKAGE_NAME
+AG_GST_ARG_WITH_PACKAGE_ORIGIN
+
+AG_GST_PKG_CONFIG_PATH
+
+AG_GST_FLEX_CHECK
+
+dnl *** checks for platform ***
+
+dnl * hardware/architecture *
+
+dnl common/m4/gst-arch.m4
+dnl check CPU type
+AG_GST_ARCH
+
+dnl Determine endianness
+AC_C_BIGENDIAN
+
+dnl *** checks for programs ***
+
+dnl find a compiler
+AC_PROG_CC
+
+dnl check if the compiler supports '-c' and '-o' options
+AM_PROG_CC_C_O
+
+dnl check if the compiler supports do while(0) macros
+AG_GST_CHECK_DOWHILE_MACROS
+
+AC_PATH_PROG(VALGRIND_PATH, valgrind, no)
+AM_CONDITIONAL(HAVE_VALGRIND, test ! "x$VALGRIND_PATH" = "xno")
+
+dnl check for gobject-introspection
+GOBJECT_INTROSPECTION_CHECK([0.9.6])
+
+dnl check for documentation tools
+AG_GST_DOCBOOK_CHECK
+GTK_DOC_CHECK([1.3])
+AS_PATH_PYTHON([2.1])
+AG_GST_PLUGIN_DOCS([1.3],[2.1])
+
+dnl check for pygobject
+AC_SUBST(PYGOBJECT_REQ, 3.0)
+PKG_CHECK_MODULES(PYGOBJECT, pygobject-3.0 >= $PYGOBJECT_REQ,
+ [
+ HAVE_PYGOBJECT=yes
+ ], HAVE_PYGOBJECT=no)
+
+AM_CONDITIONAL(WITH_PYTHON, [test "x$HAVE_PYGOBJECT" = "xyes"])
+
+dnl check for gst-validate
+PKG_CHECK_MODULES(GST_VALIDATE, gst-validate-1.0 >= 1.12.1,
+ [
+ HAVE_GST_VALIDATE=yes
+ AC_DEFINE(HAVE_GST_VALIDATE, 1, [Define if build with gst-validate support])
+ ], HAVE_GST_VALIDATE=no)
+
+AC_SUBST(GST_VALIDATE_CFLAGS)
+AC_SUBST(GST_VALIDATE_LIBS)
+AM_CONDITIONAL(HAVE_GST_VALIDATE, [test "x$HAVE_GST_VALIDATE" = "xyes"])
+
+dnl needed for scenarios definition files
+GST_PREFIX="`$PKG_CONFIG --variable=prefix gstreamer-$GST_API_VERSION`"
+AC_SUBST(GST_PREFIX)
+GST_DATADIR="$GST_PREFIX/share"
+AC_DEFINE_UNQUOTED(GST_DATADIR, "$GST_DATADIR", [system wide data directory])
+
+dnl check for bash completion
+AC_ARG_WITH([bash-completion-dir],
+ AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
+ [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),
+ [],
+ [with_bash_completion_dir=yes])
+
+if test "x$with_bash_completion_dir" = "xyes"; then
+ PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
+ [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
+ [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
+else
+ BASH_COMPLETION_DIR="$with_bash_completion_dir"
+fi
+
+AC_SUBST([BASH_COMPLETION_DIR])
+AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])
+
+dnl *** checks for libraries ***
+
+dnl check for libm, for sin() etc.
+# LT_LIB_M
+# AC_SUBST(LIBM)
+
+dnl *** checks for header files ***
+
+AC_CHECK_HEADERS([unistd.h], HAVE_UNISTD_H=yes)
+AM_CONDITIONAL(HAVE_UNISTD_H, test "x$HAVE_UNISTD_H" = "xyes")
+
+if test "x$HAVE_UNISTD_H" != "xyes"; then
+ GST_PLUGINS_SELECTED=`echo $GST_PLUGINS_SELECTED | $SED -e s/festival//`
+fi
+
+dnl *** checks for gst-validate-launcher ***
+
+AC_CHECK_PROG(GST_VALIDATE_LAUNCHER, gst-validate-launcher, yes)
+AM_CONDITIONAL(HAVE_GST_VALIDATE_LAUNCHER, [test "x$GST_VALIDATE_LAUNCHER" = "xyes"])
+
+dnl *** checks for types/defines ***
+
+dnl *** checks for structures ***
+
+dnl *** checks for compiler characteristics ***
+
+dnl *** checks for library functions ***
+
+dnl *** checks for headers ***
+
+dnl *** checks for dependency libraries ***
+
+dnl GLib is required
+AG_GST_GLIB_CHECK([2.40.0])
+
+PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.16, HAVE_GIO=yes, HAVE_GIO=no)
+AC_SUBST(GIO_CFLAGS)
+AC_SUBST(GIO_LIBS)
+
+dnl checks for gstreamer
+dnl uninstalled is selected preferentially -- see pkg-config(1)
+AG_GST_CHECK_GST($GST_API_VERSION, [$GST_REQ], yes)
+AG_GST_CHECK_GST_BASE($GST_API_VERSION, [$GST_REQ], yes)
+#AG_GST_CHECK_GST_GDP($GST_API_VERSION, [$GST_REQ], yes)
+AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no)
+AG_GST_CHECK_GST_CONTROLLER($GST_API_VERSION, [$GST_REQ], yes)
+AG_GST_CHECK_GST_PLUGINS_BASE($GST_API_VERSION, [$GSTPB_REQ], yes)
+AG_GST_CHECK_GST_PLUGINS_BAD($GST_API_VERSION, [$GST_REQ], yes)
+AG_GST_CHECK_GST_PLUGINS_GOOD($GST_API_VERSION, [$GST_REQ], yes)
+AM_CONDITIONAL(HAVE_GST_CHECK, test "x$HAVE_GST_CHECK" = "xyes")
+
+AG_GST_ARG_WITH_PLUGINS
+AG_GST_CHECK_PLUGIN(plugins)
+
+dnl set location of plugin directory
+AG_GST_SET_PLUGINDIR
+
+GSTPB_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-base-$GST_API_VERSION --variable pluginsdir`
+AC_SUBST(GSTPB_PLUGINS_DIR)
+AC_MSG_NOTICE(Using GStreamer Base Plugins in $GSTPB_PLUGINS_DIR)
+
+dnl check for gstreamer-pbutils
+PKG_CHECK_MODULES(GST_PBUTILS, gstreamer-pbutils-$GST_API_VERSION, HAVE_GST_PBUTILS="yes", HAVE_GST_PBUTILS="no")
+if test "x$HAVE_GST_PBUTILS" != "xyes"; then
+ AC_ERROR([gst-pbutils is required for rendering support])
+fi
+AC_SUBST(GST_PBUTILS_LIBS)
+AC_SUBST(GST_PBUTILS_CFLAGS)
+
+dnl check for gst-controller
+PKG_CHECK_MODULES(GST_CONTROLLER, gstreamer-controller-$GST_API_VERSION, HAVE_GST_CONTROLLER="yes", HAVE_GST_CONROLLER="no")
+if test "x$HAVE_GST_CONTROLLER" != "xyes"; then
+ AC_ERROR([gst-controller is required for transition support])
+fi
+AC_SUBST(GST_CONTROLLER_LIBS)
+AC_SUBST(GST_CONTROLLER_CFLAGS)
+
+dnl check for gstvideo
+PKG_CHECK_MODULES(GST_VIDEO, gstreamer-video-$GST_API_VERSION, HAVE_GST_VIDEO="yes", HAVE_GST_CONROLLER="no")
+if test "x$HAVE_GST_VIDEO" != "xyes"; then
+ AC_ERROR([gst-video is required for transition support])
+fi
+AC_SUBST(GST_VIDEO_LIBS)
+AC_SUBST(GST_VIDEO_CFLAGS)
+
+dnl Check for documentation xrefs
+GLIB_PREFIX="`$PKG_CONFIG --variable=prefix glib-2.0`"
+GST_PREFIX="`$PKG_CONFIG --variable=prefix gstreamer-$GST_API_VERSION`"
+GSTPB_PREFIX="`$PKG_CONFIG --variable=prefix gstreamer-plugins-base-$GST_API_VERSION`"
+AC_SUBST(GLIB_PREFIX)
+AC_SUBST(GST_PREFIX)
+AC_SUBST(GSTPB_PREFIX)
+
+dnl pitivi formatter needs libxml
+PKG_CHECK_MODULES(XML, libxml-2.0, HAVE_LIBXML="yes", HAVE_LIBXML="no")
+if test "x$HAVE_LIBXML" != "xyes"; then
+ AC_ERROR([libxml2 is required])
+fi
+AC_SUBST(XML_LIBS)
+AC_SUBST(XML_CFLAGS)
+
+dnl GTK is optional and only used in examples
+HAVE_GTK=no
+HAVE_GTK_X11=no
+GTK_REQ=3.0.0
+if test "x$BUILD_EXAMPLES" = "xyes"; then
+ PKG_CHECK_MODULES(GTK, gtk+-3.0 >= $GTK_REQ, HAVE_GTK=yes, HAVE_GTK=no)
+ dnl some examples need gtk+-x11
+ PKG_CHECK_MODULES(GTK_X11, gtk+-x11-3.0 >= $GTK_REQ, HAVE_GTK_X11=yes, HAVE_GTK_X11=no)
+ AC_SUBST(GTK_LIBS)
+ AC_SUBST(GTK_CFLAGS)
+fi
+AM_CONDITIONAL(HAVE_GTK, test "x$HAVE_GTK" = "xyes")
+AM_CONDITIONAL(HAVE_GTK_X11, test "x$HAVE_GTK_X11" = "xyes")
+
+dnl Check for -Bsymbolic-functions linker flag used to avoid
+dnl intra-library PLT jumps, if available.
+AC_ARG_ENABLE(Bsymbolic,
+ [AS_HELP_STRING([--disable-Bsymbolic],[avoid linking with -Bsymbolic])],,
+ [SAVED_LDFLAGS="${LDFLAGS}" SAVED_LIBS="${LIBS}"
+ AC_MSG_CHECKING([for -Bsymbolic-functions linker flag])
+ LDFLAGS=-Wl,-Bsymbolic-functions
+ LIBS=
+ AC_TRY_LINK([], [return 0],
+ AC_MSG_RESULT(yes)
+ enable_Bsymbolic=yes,
+ AC_MSG_RESULT(no)
+ enable_Bsymbolic=no)
+ LDFLAGS="${SAVED_LDFLAGS}" LIBS="${SAVED_LIBS}"])
+
+dnl building of benchmarks
+AC_ARG_ENABLE(benchmarks,
+ AS_HELP_STRING([--disable-benchmarks],[disable building benchmarks apps]),
+ [
+ case "${enableval}" in
+ yes) BUILD_BENCHMARKS=yes ;;
+ no) BUILD_BENCHMARKS=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-benchmarks) ;;
+ esac
+ ],
+[BUILD_BENCHMARKS=yes]) dnl Default value
+AM_CONDITIONAL(BUILD_BENCHMARKS, test "x$BUILD_BENCHMARKS" = "xyes")
+
+dnl set license and copyright notice
+GST_LICENSE="LGPL"
+AC_DEFINE_UNQUOTED(GST_LICENSE, "$GST_LICENSE", [GStreamer license])
+AC_SUBST(GST_LICENSE)
+
+dnl define LIBDIR so we can inform people where we live
+AS_AC_EXPAND(LIBDIR, $libdir)
+AC_DEFINE_UNQUOTED(LIBDIR, "$LIBDIR", [library dir])
+
+dnl set location of plugin directory
+AG_GST_SET_PLUGINDIR
+
+dnl define an ERROR_CFLAGS Makefile variable
+AG_GST_SET_ERROR_CFLAGS($GST_GIT, [-Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wundef \
+ -Wwrite-strings -Wformat-security -Wold-style-definition \
+ -Winit-self -Wmissing-include-dirs -Waddress -Waggregate-return -Wno-multichar \
+ -Wnested-externs])
+
+dnl define correct level for debugging messages
+AG_GST_SET_LEVEL_DEFAULT($GST_GIT)
+
+dnl *** finalize CFLAGS, LDFLAGS, LIBS
+
+dnl Overview:
+dnl GST_OPTION_CFLAGS: common flags for profiling, debugging, errors, ...
+dnl GST_*: flags shared by built objects to link against GStreamer
+dnl GST_ALL_LDFLAGS: linker flags shared by all
+dnl GST_LIB_LDFLAGS: additional linker flags for all libaries
+dnl GST_LT_LDFLAGS: library versioning of our libraries
+dnl GST_PLUGIN_LDFLAGS: flags to be used for all plugins
+
+dnl GST_OPTION_CFLAGS
+if test "x$USE_DEBUG" = xyes; then
+ PROFILE_CFLAGS="-g"
+fi
+AC_SUBST(PROFILE_CFLAGS)
+
+if test "x$PACKAGE_VERSION_NANO" = "x1"; then
+ dnl Define _only_ during CVS (not pre-releases or releases)
+ DEPRECATED_CFLAGS="-DGST_DISABLE_DEPRECATED"
+else
+ DEPRECATED_CFLAGS=""
+fi
+AC_SUBST(DEPRECATED_CFLAGS)
+
+VISIBILITY_CFLAGS=""
+AS_COMPILER_FLAG([-fvisibility=hidden], [
+ VISIBILITY_CFLAGS="-fvisibility=hidden"
+ AC_DEFINE(GST_API_EXPORT, [extern __attribute__ ((visibility ("default")))], [public symbol export define])
+], [
+ VISIBILITY_CFLAGS=""
+ AC_DEFINE(GST_API_EXPORT, [extern], [public symbol export define])
+])
+AC_SUBST(VISIBILITY_CFLAGS)
+
+dnl disable strict aliasing
+AS_COMPILER_FLAG([-fno-strict-aliasing], [EXTRA_CFLAGS="-fno-strict-aliasing"])
+AC_SUBST(EXTRA_CFLAGS)
+
+dnl every flag in GST_OPTION_CFLAGS can be overridden
+dnl at make time with e.g. make ERROR_CFLAGS=""
+GST_OPTION_CFLAGS="\$(WARNING_CFLAGS) \$(DEBUG_CFLAGS) \$(PROFILE_CFLAGS) \$(GCOV_CFLAGS) \$(OPT_CFLAGS) \$(DEPRECATED_CFLAGS)"
+AC_SUBST(GST_OPTION_CFLAGS)
+
+dnl FIXME: do we want to rename to GST_ALL_* ?
+dnl prefer internal headers to already installed ones
+dnl also add builddir include for enumtypes and marshal
+dnl add GST_OPTION_CFLAGS, but overridable
+GST_CFLAGS="$GST_CFLAGS $EXTRA_CFLAGS \$(GST_OPTION_CFLAGS) \$(ERROR_CFLAGS) \$(VISIBILITY_CFLAGS) -DGST_USE_UNSTABLE_API"
+AC_SUBST(GST_CFLAGS)
+AC_SUBST(GST_LIBS)
+
+dnl LDFLAGS really should only contain flags, not libs - they get added before
+dnl whatevertarget_LIBS and -L flags here affect the rest of the linking
+GST_ALL_LDFLAGS="-no-undefined"
+if test "x${enable_Bsymbolic}" = "xyes"; then
+ GST_ALL_LDFLAGS="$GST_ALL_LDFLAGS -Wl,-Bsymbolic-functions"
+fi
+AC_SUBST(GST_ALL_LDFLAGS)
+
+dnl GST_LIB_LDFLAGS
+dnl linker flags shared by all libraries
+dnl LDFLAGS modifier defining exported symbols from built libraries
+GST_LIB_LDFLAGS=""
+AC_SUBST(GST_LIB_LDFLAGS)
+
+dnl GST_PLUGIN_LDFLAGS must only contain flags, not libs - they get added before
+dnl whatevertarget_LIBS and -L flags here affect the rest of the linking
+GST_PLUGIN_LDFLAGS="-module -avoid-version $GST_ALL_LDFLAGS"
+AC_SUBST(GST_PLUGIN_LDFLAGS)
+
+dnl *** output files ***
+
+dnl po/Makefile.in
+
+AC_CONFIG_FILES(
+Makefile
+ges/ges-version.h
+common/Makefile
+common/m4/Makefile
+m4/Makefile
+ges/Makefile
+tests/Makefile
+tests/check/Makefile
+tests/benchmarks/Makefile
+examples/Makefile
+examples/c/Makefile
+tests/validate/Makefile
+tests/validate/scenarios/Makefile
+tools/Makefile
+docs/Makefile
+docs/version.entities
+docs/libs/Makefile
+pkgconfig/Makefile
+pkgconfig/gst-editing-services.pc
+pkgconfig/gst-editing-services-uninstalled.pc
+plugins/Makefile
+plugins/ges/Makefile
+plugins/nle/Makefile
+bindings/Makefile
+bindings/python/Makefile
+bindings/python/gi/Makefile
+bindings/python/gi/overrides/Makefile
+)
+AC_OUTPUT
--- /dev/null
+# GStreamer
+# Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+#
+# bash/zsh completion support for ges-launch
+#
+# 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.
+
+HELPERDIR="${BASH_SOURCE[0]%/*}/../helpers"
+
+if [[ ! -d "$HELPERDIR" ]]; then
+ HELPERDIR="$(pkg-config --variable=bashhelpersdir gstreamer-1.0)"
+else
+ HELPERDIR=`cd "$HELPERDIR"; pwd`
+fi
+
+# Common definitions
+. "$HELPERDIR"/gst
+
+HELPER="$_GST_HELPER"
+
+_list_commands ()
+{
+ ges-launch-1.0 help | grep '^ +' | cut -d' ' -f3
+}
+
+_ges___inspect_action_type ()
+{
+ COMPREPLY=( $(compgen -W "$(ges-launch-1.0 --inspect-action-type | grep '^[^ ]' | cut -d':' -f2)" -- $cur) )
+}
+
+_ges___track_types ()
+{
+ COMPREPLY=( $(compgen -W "audio video audio+video" -- $cur) )
+}
+
+_ges___set_scenario () {
+ COMPREPLY=( $(compgen -W "*.scenario $(gst-validate-1.0 -l | awk '$0=$2' FS=[ RS=])" -- $cur) )
+}
+
+_ges___load () {
+ COMPREPLY=( $(compgen -W "*.xges" -- $cur) )
+}
+
+_ges___outputuri () {
+ COMPREPLY=( $(compgen -W "file://" -- $cur) )
+}
+
+_ges___audiosink () {
+ COMPREPLY=( $(compgen -W "$($HELPER --klass=Sink --sinkcaps='audio/x-raw')" -- $cur) )
+}
+
+_ges___videosink () {
+ COMPREPLY=( $(compgen -W "$($HELPER --klass=Sink --sinkcaps='video/x-raw')" -- $cur) )
+}
+
+_ges_clip () {
+ if [[ "$prev" == "$command" ]];
+ then
+ _gst_mandatory_argument
+ else
+ COMPREPLY=( $(compgen -W "duration= inpoint= start= layer= $(_list_commands)" -- $cur) )
+ fi
+}
+
+_ges_test_clip () {
+ if [[ "$prev" == "$command" ]];
+ then
+ _gst_mandatory_argument
+ else
+ COMPREPLY=( $(compgen -W "duration= inpoint= start= layer= $(_list_commands)" -- $cur) )
+ fi
+}
+
+_ges_effect () {
+ if [[ "$prev" == "$command" ]];
+ then
+ _gst_mandatory_argument
+ else
+ COMPREPLY=( $(compgen -W "duration= start= layer= $(_list_commands)" -- $cur) )
+ fi
+}
+
+_ges_list_options () {
+ _gst_all_arguments ges-launch-1.0
+}
+
+_ges_list_commands () {
+ COMPREPLY=( $(compgen -W "$(_list_commands)" -- $cur) )
+}
+
+_ges_list_properties () {
+ local props
+
+ if [[ "$real_command" == "" ]]
+ then
+ _gst_mandatory_argument
+ elif [[ "$real_command" == "+clip" ]]
+ then
+ COMPREPLY=( $(compgen -W "set-alpha set-posx set-posy set-width set-height set-volume set-mute" -- $cur) )
+ elif [[ "$real_command" == "+test-clip" ]]
+ then
+ COMPREPLY=( $(compgen -W "set-alpha set-posx set-posy set-width set-height set-volume set-mute" -- $cur) )
+ elif [[ "$real_command" == "+effect" ]]
+ then
+ COMPREPLY=()
+ effect_bin_description="${effect_bin_description//\"/ }"
+ array=(${effect_bin_description//!/ })
+ for i in "${array[@]}"; do
+ props=("$($HELPER --element-properties $i)")
+ for j in $props; do
+ j="${j//=/ }"
+ COMPREPLY+=( $(compgen -W "set-$j" -- $cur) )
+ done
+ done
+ else
+ _gst_mandatory_argument
+ fi
+}
+
+_ges___exclude_ () { _gst_mandatory_argument; }
+_ges___encoding_profile () { _gst_mandatory_argument; }
+_ges___ges_sample_path () { _gst_mandatory_argument; }
+_ges___ges_sample_path_recurse () { _gst_mandatory_argument; }
+_ges___thumbnail () { _gst_mandatory_argument; }
+_ges___repeat () { _gst_mandatory_argument; }
+_ges___save () { _gst_mandatory_argument; }
+
+containsElement () {
+ local e
+ for e in "${@:2}";
+ do
+ [[ "$e" == "$1" ]] && return 0;
+ done
+ return 1
+}
+
+__ges_main ()
+{
+ local i=1 c=1 command function_exists completion_func commands real_command effect_bin_description
+
+ commands=($(_list_commands))
+ real_command=""
+ effect_bin_description=""
+
+ if [[ "$cur" == "=" ]]; then
+ _gst_mandatory_argument
+ return
+ fi
+
+ while [[ $i -ne $COMP_CWORD ]];
+ do
+ local var
+ var="${COMP_WORDS[i]}"
+ if [[ "$var" == "--"* ]]
+ then
+ command="$var"
+ elif containsElement "$var" "${commands[@]}";
+ then
+ real_command="$var"
+ command="$var"
+ if [[ "$var" == "+effect" ]]
+ then
+ effect_bin_description="${COMP_WORDS[i+1]}"
+ fi
+ fi
+ i=$[$i+1]
+ done
+
+ if [[ "$command" == "--gst"* ]]; then
+ completion_func="_${command//-/_}"
+ else
+ completion_func="_ges_${command//-/_}"
+ completion_func="${completion_func//+/}"
+ fi
+
+ declare -f $completion_func >/dev/null 2>&1
+
+ function_exists=$?
+
+ if [[ "$cur" == "-"* ]]; then
+ _ges_list_options
+ elif [[ "$cur" == "+"* ]]; then
+ _ges_list_commands
+ elif [[ "$cur" == "="* ]]
+ then
+ _gst_mandatory_argument
+ elif [[ "$cur" == "set-"* ]]
+ then
+ _ges_list_properties
+ elif [ $function_exists -eq 0 ]
+ then
+ $completion_func
+ else
+ _ges_list_commands
+ fi
+}
+
+__ges_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.
+__ges_complete ()
+{
+ local wrapper="__ges_wrap${2}"
+ eval "$wrapper () { __ges_func_wrap $2 ; }"
+ complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \
+ || complete -o default -o nospace -F $wrapper $1
+}
+
+_ges ()
+{
+ __ges_wrap__ges_main
+}
+
+__ges_complete ges-launch-1.0 __ges_main
--- /dev/null
+if ENABLE_GTK_DOC
+DOCS_SUBDIRS = libs
+else
+DOCS_SUBDIRS =
+endif
+
+SUBDIRS = $(DOCS_SUBDIRS)
+DIST_SUBDIRS = libs
+
+EXTRA_DIST = \
+ version.entities.in
+
+upload:
+ @if test "x$(SUBDIRS)" != x; then for a in $(SUBDIRS); do cd $$a; make upload; cd ..; done; fi
--- /dev/null
+Assets
+~~~~~~~~~
+
+This draft document describes a possible design for asset objects.
+
+The assets should be used in order to instantiate objects of differents
+types.
+
+Terminology: A asset is an object from which objects can be extracted.
+
+Summary
+~~~~~~~~~
+
+1. Basic ideas
+2. Problems
+3. Propositions to solve those problems
+4. Use-cases
+5. API draft
+ A. Asset API draft
+ B. Source asset API draft
+ C. Project asset API draft
+ D. Extractable/Asset Interface API draft
+ E. Methods that should be added to other classes
+
+1. Basic ideas
+~~~~~~~~~~~~~~~
+
+Basically, asset is a way of avoiding duplicating data between object and avoid
+processing when the same processing would happen several times for an 2 different
+objects of a same type.
+
+ * There will be a listing of avalaible, ready to use assets
+ * Asset allow to create some particular types of object that implement the GESExtractable
+ interface
+ * Assets will hold metadatas
+ * Assets can be either, created by the user, or will be created by GES itself
+ when initializing, there should be a way to disable that feature on demand.
+
+Some ideas of asset(especially for TimelineSource objects) can be found in docs/random/design.
+
+2. Problems (Not in any particular order)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1) We have avoid to various times the same file on the system
+ 2) We must be able to query assets by some criteria
+ a. By type of TimelineObject that it can produce
+ b. By type of supported tracks
+ c. Should we have filters by some specific properties of source asset
+ - like duration, width, height, etc?
+ 3) We must be able to get reference to origin asset of any extracted object
+ 4) We need a way to describe projects
+ 5) GESAssets can be instantiated asynchronously
+ 6) The instantiation of a asset can fail
+ 7) Users need to get informations about the instantiation failures
+ 8) User should be able to cancel the creation of a GESAsset (especially
+ in case of asynchronous Asset creation)
+
+3. Propositions to solve those problems
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ 1) We should have an interface that needs to be implemented by classes that need to be extractable.
+ We can call it GESExtractable. It should be responsible for:
+ * letting the user get the Asset from which an extracted object comes from
+ * Making it possible to instantiate a GESAsset only from a GType which means that:
+ - It needs to contain a reference to a GES_TYPE_ASSET (or subclass) so the proper GESAsset type will be instantiated.
+ - It need to contain some mapping between the ID (string) of the asset, and the property of the object that is used as its ID.
+ For a property to be usable as an ID for its asset, each objects extracted from a same asset must have the same value for the property
+ Examples:
+ GESTimelineFileSource -> URI
+ GESTrackParseLaunchEffect -> bin_description
+ GESProject -> project name / uri of the stored serialized
+
+ 2) A list of all available, ready to be used assets should be cached and
+ reused whenever it is possible.
+ Basically it will look like:
+ GESAsset.id -> asset
+ (the ID is computed thanks to the mapping)
+
+ 4) To allow users to implement some sort of library (media, effects, transitions...)
+ we must be able to query assets by using some criteria,
+ e.g. GType of the extractable object, URI, supported track types, etc...
+
+ 5) We can instantiate a GESAsset only from a GType, the appropriate checks need
+ to be done and it can return subclasses of GESAsset thanks to the
+ information included in the GESExtractable interface.
+
+ 6) Instanciation can happen asyncronously in some cases. For example, a
+ asset that needs to discover a file to be properly filled needs.
+
+4. Use cases
+~~~~~~~~~~~~~
+ UC-1. Define media files and discover them
+ UC-2. Define project - reference all assets
+ UC-3. Define titles
+ UC-4. Define operations
+ - Transitions - 1 asset per transition type
+ - Effects - 1 asset per effects type
+ - TextOverlay
+ UC-5. Handle metadata
+ UC-6. Add operations (only effects?) to a GESTimelineObject
+ UC-7. User want to 'invent' a new operation, we need to be able
+ to let him define it
+ UC-8. The user want to make an object from a GESAsset
+
+
+5. API Draft
+~~~~~~~~~~~~
+
+A. GESExtractable API
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+GESExtractable is responsible for telling what GESAsset subclass need to
+be instantiated.
+
+/**
+ * ges_extractable_object_get_asset:
+ * @object: Target object
+ * Method to get asset which was used to instaniate specified object
+ * Returns: origin asset
+ */
+GESAsset *
+ges_extractable_get_asset(GESExtractable *extractable);
+
+/**
+ * ges_extractable_object_set_asset:
+ * @object: Target object
+ * @asset: (transfer none): The #GESAsset to set
+ *
+ * Method to set asset which was used to instaniate specified object
+ */
+void
+ges_extractable_set_asset (GESExtractable * self, GESAsset * asset)
+
+/**
+ * ges_extractable_get_asset_type:
+ * @class: Get the #GType of the GESAsset that should be used to extract
+ * the object that implements that #GESExtractable interface
+ *
+ * Lets user know the type of GESAsset that should be used to extract the
+ * object that implement that interface.
+ */
+GType
+ges_extractable_get_asset_type (GESExtractableClass *class)
+
+/**
+ * ges_extractable_get_id:
+ * @self: The #GESExtractable
+ *
+ * Returns: The #id of the associated #GESAsset
+ */
+const gchar *
+ges_extractable_get_id (GESExtractable * self)
+
+/**
+ * ges_extractable_type_get_parameters_for_id:
+ * @type: The #GType implementing #GESExtractable
+ * @id: The ID of the Extractable
+ * @n_params: (out): Return location for the returned array
+ *
+ * Returns: (transfer full) (array length=n_params): an array of #GParameter
+ * needed to extract the #GESExtractable from a #GESAsset of @id
+ */
+GParameter *
+ges_extractable_type_get_parameters_from_id (GType type, const gchar * id,
+ guint * n_params)
+
+/**
+ * ges_extractable_type_get_asset_type:
+ * @type: The #GType implementing #GESExtractable
+ *
+ * Get the #GType, subclass of #GES_TYPE_ASSET to instanciate
+ * to be able to extract a @type
+ *
+ * Returns: the #GType to use to create a asset to extract @type
+ */
+GType
+ges_extractable_type_get_asset_type (GType type)
+
+/**
+ * ges_extractable_type_check_id:
+ * @type: The #GType implementing #GESExtractable
+ * @id: The ID to check
+ *
+ * Check if @id is valid for @type
+ *
+ * Returns: Return %TRUE if @id is valid, %FALSE otherwise
+ */
+gchar *
+ges_extractable_type_check_id (GType type, const gchar * id)
+
+
+A. Asset And subclasses API draft
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+a) GESAsset
+| ~~~~~~~~~~~
+|
+| Will implement GESMetdata
+|
+| Virtual method type:
+| -------------------
+|
+| /**
+| * GESAssetCreatedCallback:
+| * @asset: the #newly created #GESAsset or %NULL if something went wrong
+| * @error: The #GError filled if previsouly provided in the constructor or %NULL
+| * @user_data: The user data pointer
+| *
+| * A function that will be called when a #GESAsset is ready to be used.
+| */
+| typedef void (*GESAssetCreatedCallback)(GESAsset *asset, GError *error, gpointer user_data);
+|
+|
+| Methods prototypes:
+| -------------------
+| /**
+| * ges_asset_request:
+| * @extractable_type: The #GType of the object that can be extracted from the new asset.
+| * The class must implement the #GESExtractable interface.
+| * @callback: a #GAsyncReadyCallback to call when the initialization is finished
+| * @id: The Identifier of the asset we want to create. This identifier depends of the extractable
+| * type you want. By default it is the name of the class itself (or %NULL), but for example for a
+| * GESTrackParseLaunchEffect, it will be the pipeline description, for a GESTimelineFileSource it
+| * will be the name of the file, etc... You should refer to the documentation of the #GESExtractable
+| * type you want to create a #GESAsset for.
+| *
+| * Creates a new #GESAsset asyncronously, @callback will be called when the materail is loaded
+| *
+| * Returns: %TRUE if the asset could be loaded to load %FALSE otherwize
+| */
+| gboolean
+| ges_asset_request (GType extractable_type, GESAssetCreatedCallback callback,
+| gpointer user_data, const gchar *id);
+|
+|->b) GESAssetTimelineObject
+| | ~~~~~~~~~~~~~~~~~~~~~~~~~
+| | /**
+| | * ges_asset_timeline_object_get_track_types:
+| | * @asset: a #GESAssetTimelineObject
+| | *
+| | * Method that returns track types that are supported by given asset
+| | *
+| | * Returns: Track types that are supported by asset
+| | */
+| | GESTrackType
+| | ges_asset_timeline_object_get_track_types (GESAssetTimelineObject *asset);
+| |
+| |
+| |-> c) GESAssetFileSource
+| ~~~~~~~~~~~~~~~~~~~~~
+| /**
+| * ges_asset_file_source_get_stream_info:
+| * @asset: a #GESAsset of extractable_type GES_TIMELINE_FILE_SOURCE
+| * Method that returns discoverer data of specified asset so user could work with
+| * it directly
+| * Returns: discover info of asset
+| */
+| GstDiscovererStreamInfo *
+| ges_asset_file_source_get_stream_info (GESAssetFileSource *asset);
+|
+|
+|-> d) GESProjectAsset asset API
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ A project is a GESAsset that has GES_TYPE_TIMELINE or subclasses as extractable_type
+
+ FIXME: This special case that should be thought thoroughly.
+
+ /**
+ * ges_asset_project_list_assets:
+ * @asset: Project asset
+ * @type: Type of asset to list
+ * Method for listing assets of specified type that are available in
+ * particular project.
+ *
+ * Returns: list of available assets of given type in project
+ */
+ ges_asset_project_list_assets (GESAsset *project,
+ GType type)
+
+E. Methods that should be added to other classes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * ges_timeline_layer_add_asset:
+ *
+ * Creates TimelineObject from asset, adds it to layer and
+ * returns reference to it.
+ *
+ * Returns: Created #GESTimelineObject
+ */
+GESTimelineObject * ges_timeline_layer_add_asset (GESTimelineLayer *layer,
+ GESAssetTimelineObject *asset,
+ GstClockTime start,
+ GstClockTime inpoint,
+ GstClockTime duration,
+
+/**
+ * ges_timeline_remove_extracted_from_asset:
+ * @timeline: A #GESTimeline from which to remove objects
+ * @asset: The #GESAssetTimelineObject to remove from @timeline
+ *
+ * Removes all asset in @timeline that have been extracted from @asset
+ *
+ * Returns: %TRUE if everything could be done properly %FALSE otherwize
+ */
+gboolean ges_timeline_layer_add_asset (GESTimeline *timeline, GESAsset *asset);
+
+/**
+ * ges_timeline_object_add_asset:
+ * @object: Target #GESTimelineObject
+ * @asset: a #GESAsset that must have a GES_TYPE_TRACK_OPERATION as extractable_type
+ * @priority: The priority of the new #GESTrackObject
+ *
+ * Adds an operation (GESTrackObject(s)) to a GESTimelineObject
+ *
+ * Returns: (transfer full):The newly created #GESTrackObject.
+ */
+GESTrackObject
+ges_timeline_object_add_asset (GESTimelineObject *object,
+ GESAsset *asset,
+ guint32 priority);
--- /dev/null
+Effects
+-------
+
+Summary
+-------
+1. Basic ideas
+2. Problems
+3. Propositions to solve those problems
+ A. The registry
+ B. Effects configurability
+ C. Keyframes
+4. Use-cases
+5. API draft
+
+The goal of this proposal is to design a simple way to handle effects through an
+API which would allow developers to handle any use-cases.
+
+1. Basic ideas
+----------------
+
+ * GESTrackEffects are subclasses of GESTrackOperation
+
+ * You can add effects on any clip or layer
+
+ * You can add effects over several clips and control them as a unique effect.
+
+ * Effects are configurable and those properties can change during time
+
+ * We must be able to handle third-party effect providers, like the
+ gnome-video-effects standard.
+
+ * We must be able to implement complex effects. This means effects that are
+ more than adding GstElement-s to the timeline. It can also mean effects
+ that apply both video and audio changes.
+
+2. Problems
+----------
+ * We must be able to provide a list of effects available on the system at
+ runtime.
+
+ * We must be able to configure effects through an API in GES
+ withtout having to access the GstElements properties directly.
+
+ * We should also expose the GstElement-s contained in an effect so
+ it is possible for people to control their properties as they wish.
+
+ * We must be able to implement and handle complexe effects directly in GES
+
+ * We must be able to configure effects through time -> Keyframes without
+ duplicating code from GStreamer
+
+
+3. Propositions to solve those problems
+---------------------------------------
+
+A. The registry => Still to design
+
+ We could implement a GESRegistry which would actually
+ retrieve elements (effects) from the GSTRegistry and any other mean
+ such as gnome-video-effects to let us get all the effects that are present
+ on the system....
+ This way the developers could have the list of all the effects
+ that are installed on the system pretty easily.
+
+B. Effects configurability
+
+ The idea to be able to configure effects through a simple API in GES would
+ be to add an API in GESTrackObject to access the gst-object properties that
+ user would like to configure.
+ We would also have a method to set those properties easily.
+
+ We should also find a way to handle that in the case of systems such as
+ gnome-effects
+
+C. Keyframes
+
+ We may want to handle this use-case directly in GES and for any kind of
+ time related configuration? FIXME
+ => Special specifications for that?
+
+4. Use-cases
+-----------
+
+ UC-1. The user wants to add an effect to an entire clip => GESTimelineObject
+ new API
+
+ UC-2. The developer wants to allow users to configure effects => New
+ GESTrackOperation API
+
+ UC-3. The user wants to add an effect on a specific portion of a clip, we
+ should allow him to specify a portion of the clip where the effect should be
+ applied.
+
+ UC-4. We want to implement an effect which isn't only composed by a bin, but
+ is more complexe than that (ex: "effect '24'") => we have the
+ GESTrackOperation which is the base class (abstract) for this kind of
+ implementation. This class should implement vmethods to get/set configurable
+ properties.
+
+ UC-5. A developer wants to implement effect which handle music and video at
+ the same time, Would the solution be to implement a GESTimelineEffect
+ to handle this special usecase? FIXME
+
+ UC-6. The developers wants to configure each elements of an effect the way
+ he wants
+ with a full control over it.
+
+ UC-7. Developers want to expose all effects present on the system to the
+ end-user
+
+5. API draft
+------------
+
+
+ A. GESTrackObject new API
+
+ signals:
+ -------
+ * deep-notify: emited when a usefull property of a GstElement
+ contained in the GESTrackObject changes
+ => DONE
+
+ /**
+ * ges_track_object_list_children_properties:
+ *
+ * @object: The origin #GESTrackObject
+ *
+ * A convenience method that lists all the usefull configurable properties
+ * of the GstElement-s contained in @object.
+ *
+ * Returns: an array of GParamSpec of the configurable properties of the
+ * GstElement-s contained in @object or %NULL if a problem occurred.
+ */
+ GParamSpec **
+ ges_track_object_list_children_properties (GESTrackObject *object);
+
+ -> Usecases: Let user know all the property he can configure.
+ => Waiting for GESMaterial
+
+ /**
+ * ges_track_object_set_child_property:
+ *
+ * @object: The origin #GESTrackObject
+ * @property_name: The name of the property
+ * @value: the value
+ *
+ * Sets a property of a GstElement contained in @object.
+ *
+ */
+ void ges_track_object_set_child_property (GESTrackObject *object,
+ const gchar *property_name,
+ GValue * value);
+ -> Usecases:
+ + Let user configure effects easily (UC-3)
+ => DONE
+
+ /**
+ * ges_track_object_get_child_property:
+ *
+ * @object: The origin #GESTrackObject
+ * @property_name: The name of the property
+ * @value: return location for the property value
+ *
+ * Gets a property of a GstElement contained in @object.
+ */
+ void ges_track_object_get_child_property (GESTrackObject *object,
+ const gchar *property_name,
+ GValue * value);
+ => DONE
+
+ /**
+ * ges_track_object_get_material:
+ *
+ * @object: The origin #GESTrackObject
+ *
+ * This is a convenience method to get the #GESMaterial
+ * from which @object has been made.
+ *
+ * Returns: The material from which @object has been made or %NULL
+ * if @object has been made by another mean
+ */
+ GESMaterial *ges_track_object_get_material (GESTrackObject *object);
+ => Waiting for GESMaterial
+
+ B. GESTimelineObject new API
+
+ signals:
+ -------
+ * effect-added: emited when an effect is added
+ * effect-removed: emited when an effect is removed
+ => DONE
+
+ /**
+ * ges_timeline_object_add_effect:
+ *
+ * @object: The origin #GESTimelineObject
+ * @effect_material: The #GESEffect from which to create the effect
+ * @position: The top position you want to give to the effect,
+ * -1 if you want it to be added at the end of effects.
+ *
+ * Adds a new effect corresponding to @effect_material to the
+ * #GESTimelineObject
+ *
+ * Returns: The newly created #GESTrackEffect, or %NULL if there was an
+ * error.
+ */
+ GESTrackEffect *ges_timeline_object_add_effect (GESTimelineObject *object,
+ GESEffect *effect_material,
+ gint position);
+ => Waiting for GESMaterial
+
+ /**
+ * ges_timeline_object_get_effects:
+ *
+ * @object: The origin #GESTimelineObject
+ *
+ * Returns: a #GList of the #GESTrackEffect that are applied on
+ * @object order by ascendant priorities.
+ * The refcount of the objects will be increased. The user will have to
+ * unref each #GESTrackOperation and free the #GList.
+ */
+ GList *
+ ges_timeline_object_get_effects (GESTimelineObject *object);
+ -> Usecases:
+ + First step to allow the configuration of effects (UC-3)
+ => DONE
+
+ /**
+ * ges_timeline_object_set_top_effect_position:
+ *
+ * @object: The origin #GESTimelineObject
+ * @effect: The #GESTrackEffect to move
+ * @newposition: the new position at which to move the @effect
+ *
+ * Returns: %TRUE if @effect was successfuly moved, %FALSE otherwize.
+ */
+ gboolean
+ ges_timeline_object_set_top_effect_position (GESTimelineObject *object,
+ GESTrackEffect *effect, guint newposition);
+ => DONE
+
+ /**
+ * ges_timeline_object_get_top_effect_position:
+ *
+ * @object: The origin #GESTimelineObject
+ * @effect: The #GESTrackEffect we want to get the top position from
+ *
+ * Gets the top position of an effect.
+ *
+ * Returns: The top position of the effect, -1 if something went wrong.
+ */
+ gint
+ ges_timeline_object_get_top_effect_position (GESTimelineObject *object,
+ GESTrackEffect *effect);
+ => DONE
+
+ C - The GESTrackEffect API:
+ -> This is an empty abstract class
+ => DONE
+
+ D - The GESTrackParseLaunchEffect API:
+ This is a parse-launch based implementation of TrackEffect.
+
+ /**
+ * ges_track_parse_launch_effect_new:
+ *
+ * @bin_dec: The gst-launch like bin description of the effect
+ *
+ * Creates a new #GESTrackEffect from the description of the bin. This is
+ * a convenience method for testing puposes.
+ *
+ * Returns: a newly created #GESTrackEffect, or %NULL if something went
+ * wrong.
+ */
+ GESTrackEffect *ges_track_parse_launch_effect_new (GESTrackEffect *effect,
+ const gchar *bin_desc);
+ => DONE
+
+ E - The GESTrackMaterialEffect API:
+ /**
+ * ges_track_material_effect:
+ *
+ * @effect_material: The #GESEffect from which to create this
+ * #GESTrackEffect
+ *
+ * Creates a new #GESTrackEffect from a #GESEffect
+ *
+ * Returns: a newly created #GESTrackEffect, or %NULL if something went
+ * wrong.
+ */
+ GESTrackEffect *ges_track_material_effect_new (GESTrackEffect *effect,
+ GESEffect *effect_material);
+ => Waiting for GESMaterial
+
+ F - The GESTimelineEffect API:
+ -> This is an empty abstract class
+ => DONE
+
+ -> Usecases: The user wants to control multiple effects in sync. The user
+ wants to add an effect to the whole timeline. The user wants
+ to had an effect to a segment of the timeline without caring
+ bout what clip it is applied on.
+
+ G - The GESTimelineParseLaunchEffect API:
+ This is a parse-launch based implementation of TimelineEffect.
+
+ /**
+ * ges_timeline_parse_launch_effect_new_from_bin_desc:
+ * @video_bin_description: The gst-launch like bin description of the effect
+ * @audio_bin_description: The gst-launch like bin description of the effect
+ *
+ * Creates a new #GESTimelineParseLaunchEffect from the description of the bin.
+ *
+ * Returns: a newly created #GESTimelineParseLaunchEffect, or %NULL if something went
+ * wrong.
+ */
+ GESTimelineParseLaunchEffect *
+ ges_timeline_parse_launch_effect_new (const gchar * video_bin_description,
+ const gchar * audio_bin_description)
+
+ => DONE
+
+
+ H - The GESEffect:
+
+ The GESEffect class is a subclass of GESMaterial, it is used to describe
+ effects independently of the usage which is made of it in the timeline.
+
+ A GESEffect can specify a GESTrackOperation class to use in a
+ TimelineObject.
+
+ All important properties are inherited from GESMaterial such as:
+ * Name
+ * Description
+ * Tags
+ * ...
+
+ We should also be able to list properties of the effect from the GESMaterial.
+
+ => Waiting for GESMaterial
+
+=================
+ TODO GESRegistry API:
+ This should be a singleton since we don't want an app to instanciate more
+ than one registry. It must be able to get effects from various sources.
+ We should also make sure any custom effect is detected.
+
+ /**
+ * ges_registry_get_default:
+ *
+ * Returns a newly created #GESEffectRegistry or the existing one
+ * increasing
+ * its refcount
+ */
+ GESEffectRegistry *
+ ges_registry_get_default (void);
+ -> Usecases:
+ + Have a registry of all effects that are on the system (UC-8)
+
+ /**
+ * ges_effect_registry_get_effect_list:
+ *
+ * @self: The origin #GESEffectRegistry
+ *
+ * Returns a #GList of #GESEffectDescriptors. The
+ */
+ GList *
+ ges_registry_get_effect_list (GESEffectRegistry *self);
+ -> Usecases:
+ + Get all effects descriptors that are on the system (UC-8)
--- /dev/null
+GStreamer: Research into encoding and muxing
+--------------------------------------------
+
+Use Cases
+---------
+
+ This is a list of various use-cases where encoding/muxing is being
+ used.
+
+* Transcoding
+
+ The goal is to convert with as minimal loss of quality any input
+ file for a target use.
+ A specific variant of this is transmuxing (see below).
+
+ Example applications: Arista, Transmageddon
+
+* Rendering timelines
+
+ The incoming streams are a collection of various segments that need
+ to be rendered.
+ Those segments can vary in nature (i.e. the video width/height can
+ change).
+ This requires the use of identiy with the single-segment property
+ activated to transform the incoming collection of segments to a
+ single continuous segment.
+
+ Example applications: Pitivi, Jokosher
+
+* Encoding of live sources
+
+ The major risk to take into account is the encoder not encoding the
+ incoming stream fast enough. This is outside of the scope of
+ encodebin, and should be solved by using queues between the sources
+ and encodebin, as well as implementing QoS in encoders and sources
+ (the encoders emitting QoS events, and the upstream elements
+ adapting themselves accordingly).
+
+ Example applications: camerabin, cheese
+
+* Screencasting applications
+
+ This is similar to encoding of live sources.
+ The difference being that due to the nature of the source (size and
+ amount/frequency of updates) one might want to do the encoding in
+ two parts:
+ * The actual live capture is encoded with a 'almost-lossless' codec
+ (such as huffyuv)
+ * Once the capture is done, the file created in the first step is
+ then rendered to the desired target format.
+
+ Fixing sources to only emit region-updates and having encoders
+ capable of encoding those streams would fix the need for the first
+ step but is outside of the scope of encodebin.
+
+ Example applications: Istanbul, gnome-shell, recordmydesktop
+
+* Live transcoding
+
+ This is the case of an incoming live stream which will be
+ broadcasted/transmitted live.
+ One issue to take into account is to reduce the encoding latency to
+ a minimum. This should mostly be done by picking low-latency
+ encoders.
+
+ Example applications: Rygel, Coherence
+
+* Transmuxing
+
+ Given a certain file, the aim is to remux the contents WITHOUT
+ decoding into either a different container format or the same
+ container format.
+ Remuxing into the same container format is useful when the file was
+ not created properly (for example, the index is missing).
+ Whenever available, parsers should be applied on the encoded streams
+ to validate and/or fix the streams before muxing them.
+
+ Metadata from the original file must be kept in the newly created
+ file.
+
+ Example applications: Arista, Transmaggedon
+
+* Loss-less cutting
+
+ Given a certain file, the aim is to extract a certain part of the
+ file without going through the process of decoding and re-encoding
+ that file.
+ This is similar to the transmuxing use-case.
+
+ Example applications: Pitivi, Transmageddon, Arista, ...
+
+* Multi-pass encoding
+
+ Some encoders allow doing a multi-pass encoding.
+ The initial pass(es) are only used to collect encoding estimates and
+ are not actually muxed and outputted.
+ The final pass uses previously collected information, and the output
+ is then muxed and outputted.
+
+* Archiving and intermediary format
+
+ The requirement is to have lossless
+
+* CD ripping
+
+ Example applications: Sound-juicer
+
+* DVD ripping
+
+ Example application: Thoggen
--- /dev/null
+Encoding and Muxing
+-------------------
+
+Summary
+-------
+ A. Problems
+ B. Goals
+ 1. EncodeBin
+ 2. Encoding Profile System
+ 3. Helper Library for Profiles
+
+
+
+A. Problems this proposal attempts to solve
+-------------------------------------------
+
+* Duplication of pipeline code for gstreamer-based applications
+ wishing to encode and or mux streams, leading to subtle differences
+ and inconsistencies accross those applications.
+
+* No unified system for describing encoding targets for applications
+ in a user-friendly way.
+
+* No unified system for creating encoding targets for applications,
+ resulting in duplication of code accross all applications,
+ differences and inconsistencies that come with that duplication,
+ and applications hardcoding element names and settings resulting in
+ poor portability.
+
+
+
+B. Goals
+--------
+
+1. Convenience encoding element
+
+ Create a convenience GstBin for encoding and muxing several streams,
+ hereafter called 'EncodeBin'.
+
+ This element will only contain one single property, which is a
+ profile.
+
+2. Define a encoding profile system
+
+2. Encoding profile helper library
+
+ Create a helper library to:
+ * create EncodeBin instances based on profiles, and
+ * help applications to create/load/save/browse those profiles.
+
+
+
+
+1. EncodeBin
+------------
+
+1.1 Proposed API
+----------------
+
+ EncodeBin is a GstBin subclass.
+
+ It implements the GstTagSetter interface, by which it will proxy the
+ calls to the muxer.
+
+ Only two introspectable property (i.e. usable without extra API):
+ * A GstEncodingProfile*
+ * The name of the profile to use
+
+ When a profile is selected, encodebin will:
+ * Add REQUEST sinkpads for all the GstStreamProfile
+ * Create the muxer and expose the source pad
+
+ Whenever a request pad is created, encodebin will:
+ * Create the chain of elements for that pad
+ * Ghost the sink pad
+ * Return that ghost pad
+
+ This allows reducing the code to the minimum for applications
+ wishing to encode a source for a given profile:
+
+ ...
+
+ encbin = gst_element_factory_make("encodebin, NULL);
+ g_object_set (encbin, "profile", "N900/H264 HQ", NULL);
+ gst_element_link (encbin, filesink);
+
+ ...
+
+ vsrcpad = gst_element_get_src_pad(source, "src1");
+ vsinkpad = gst_element_get_request_pad (encbin, "video_%d");
+ gst_pad_link(vsrcpad, vsinkpad);
+
+ ...
+
+
+1.2 Explanation of the Various stages in EncodeBin
+--------------------------------------------------
+
+ This describes the various stages which can happen in order to end
+ up with a multiplexed stream that can then be stored or streamed.
+
+1.2.1 Incoming streams
+
+ The streams fed to EncodeBin can be of various types:
+
+ * Video
+ * Uncompressed (but maybe subsampled)
+ * Compressed
+ * Audio
+ * Uncompressed (audio/x-raw-{int|float})
+ * Compressed
+ * Timed text
+ * Private streams
+
+
+1.2.2 Steps involved for raw video encoding
+
+(0) Incoming Stream
+
+(1) Transform raw video feed (optional)
+
+ Here we modify the various fundamental properties of a raw video
+ stream to be compatible with the intersection of:
+ * The encoder GstCaps and
+ * The specified "Stream Restriction" of the profile/target
+
+ The fundamental properties that can be modified are:
+ * width/height
+ This is done with a video scaler.
+ The DAR (Display Aspect Ratio) MUST be respected.
+ If needed, black borders can be added to comply with the target DAR.
+ * framerate
+ * format/colorspace/depth
+ All of this is done with a colorspace converter
+
+(2) Actual encoding (optional for raw streams)
+
+ An encoder (with some optional settings) is used.
+
+(3) Muxing
+
+ A muxer (with some optional settings) is used.
+
+(4) Outgoing encoded and muxed stream
+
+
+1.2.3 Steps involved for raw audio encoding
+
+ This is roughly the same as for raw video, expect for (1)
+
+(1) Transform raw audo feed (optional)
+
+ We modify the various fundamental properties of a raw audio stream to
+ be compatible with the intersection of:
+ * The encoder GstCaps and
+ * The specified "Stream Restriction" of the profile/target
+
+ The fundamental properties that can be modifier are:
+ * Number of channels
+ * Type of raw audio (integer or floating point)
+ * Depth (number of bits required to encode one sample)
+
+
+1.2.4 Steps involved for encoded audio/video streams
+
+ Steps (1) and (2) are replaced by a parser if a parser is available
+ for the given format.
+
+
+1.2.5 Steps involved for other streams
+
+ Other streams will just be forwarded as-is to the muxer, provided the
+ muxer accepts the stream type.
+
+
+
+
+2. Encoding Profile System
+--------------------------
+
+ This work is based on:
+ * The existing GstPreset system for elements [0]
+ * The gnome-media GConf audio profile system [1]
+ * The investigation done into device profiles by Arista and
+ Transmageddon [2 and 3]
+
+2.2 Terminology
+---------------
+
+* Encoding Target Category
+ A Target Category is a classification of devices/systems/use-cases
+ for encoding.
+
+ Such a classification is required in order for:
+ * Applications with a very-specific use-case to limit the number of
+ profiles they can offer the user. A screencasting application has
+ no use with the online services targets for example.
+ * Offering the user some initial classification in the case of a
+ more generic encoding application (like a video editor or a
+ transcoder).
+
+ Ex:
+ Consumer devices
+ Online service
+ Intermediate Editing Format
+ Screencast
+ Capture
+ Computer
+
+* Encoding Profile Target
+ A Profile Target describes a specific entity for which we wish to
+ encode.
+ A Profile Target must belong to at least one Target Category.
+ It will define at least one Encoding Profile.
+
+ Ex (with category):
+ Nokia N900 (Consumer device)
+ Sony PlayStation 3 (Consumer device)
+ Youtube (Online service)
+ DNxHD (Intermediate editing format)
+ HuffYUV (Screencast)
+ Theora (Computer)
+
+* Encoding Profile
+ A specific combination of muxer, encoders, presets and limitations.
+
+ Ex:
+ Nokia N900/H264 HQ
+ Ipod/High Quality
+ DVD/Pal
+ Youtube/High Quality
+ HTML5/Low Bandwith
+ DNxHD
+
+2.3 Encoding Profile
+--------------------
+
+An encoding profile requires the following information:
+
+ * Name
+ This string is not translatable and must be unique.
+ A recommendation to guarantee uniqueness of the naming could be:
+ <target>/<name>
+ * Description
+ This is a translatable string describing the profile
+ * Muxing format
+ This is a string containing the GStreamer media-type of the
+ container format.
+ * Muxing preset
+ This is an optional string describing the preset(s) to use on the
+ muxer.
+ * Multipass setting
+ This is a boolean describing whether the profile requires several
+ passes.
+ * List of Stream Profile
+
+2.3.1 Stream Profiles
+
+A Stream Profile consists of:
+
+ * Type
+ The type of stream profile (audio, video, text, private-data)
+ * Encoding Format
+ This is a string containing the GStreamer media-type of the encoding
+ format to be used. If encoding is not to be applied, the raw audio
+ media type will be used.
+ * Encoding preset
+ This is an optional string describing the preset(s) to use on the
+ encoder.
+ * Restriction
+ This is an optional GstCaps containing the restriction of the
+ stream that can be fed to the encoder.
+ This will generally containing restrictions in video
+ width/heigh/framerate or audio depth.
+ * presence
+ This is an integer specifying how many streams can be used in the
+ containing profile. 0 means that any number of streams can be
+ used.
+ * pass
+ This is an integer which is only meaningful if the multipass flag
+ has been set in the profile. If it has been set it indicates which
+ pass this Stream Profile corresponds to.
+
+2.4 Example profile
+-------------------
+
+The representation used here is XML only as an example. No decision is
+made as to which formatting to use for storing targets and profiles.
+
+<gst-encoding-target>
+ <name>Nokia N900</name>
+ <category>Consumer Device</category>
+ <profiles>
+ <profile>Nokia N900/H264 HQ</profile>
+ <profile>Nokia N900/MP3</profile>
+ <profile>Nokia N900/AAC</profile>
+ </profiles>
+</gst-encoding-target>
+
+<gst-encoding-profile>
+ <name>Nokia N900/H264 HQ</name>
+ <description>
+ High Quality H264/AAC for the Nokia N900
+ </description>
+ <format>video/quicktime,variant=iso</format>
+ <streams>
+ <stream-profile>
+ <type>audio</type>
+ <format>audio/mpeg,mpegversion=4</format>
+ <preset>Quality High/Main</preset>
+ <restriction>audio/x-raw-int,channels=[1,2]</restriction>
+ <presence>1</presence>
+ </stream-profile>
+ <stream-profile>
+ <type>video</type>
+ <format>video/x-h264</format>
+ <preset>Profile Baseline/Quality High</preset>
+ <restriction>
+ video/x-raw-yuv,width=[16, 800],\
+ height=[16, 480],framerate=[1/1, 30000/1001]
+ </restriction>
+ <presence>1</presence>
+ </stream-profile>
+ </streams>
+
+</gst-encoding-profile>
+
+2.5 API
+-------
+ A proposed C API is contained in the gstprofile.h file in this directory.
+
+
+2.6 Modifications required in the existing GstPreset system
+-----------------------------------------------------------
+
+2.6.1. Temporary preset.
+
+ Currently a preset needs to be saved on disk in order to be
+ used.
+
+ This makes it impossible to have temporary presets (that exist only
+ during the lifetime of a process), which might be required in the
+ new proposed profile system
+
+2.6.2 Categorisation of presets.
+
+ Currently presets are just aliases of a group of property/value
+ without any meanings or explanation as to how they exclude each
+ other.
+
+ Take for example the H264 encoder. It can have presets for:
+ * passes (1,2 or 3 passes)
+ * profiles (Baseline, Main, ...)
+ * quality (Low, medium, High)
+
+ In order to programmatically know which presets exclude each other,
+ we here propose the categorisation of these presets.
+
+ This can be done in one of two ways
+ 1. in the name (by making the name be [<category>:]<name>)
+ This would give for example: "Quality:High", "Profile:Baseline"
+ 2. by adding a new _meta key
+ This would give for example: _meta/category:quality
+
+2.6.3 Aggregation of presets.
+
+ There can be more than one choice of presets to be done for an
+ element (quality, profile, pass).
+
+ This means that one can not currently describe the full
+ configuration of an element with a single string but with many.
+
+ The proposal here is to extend the GstPreset API to be able to set
+ all presets using one string and a well-known separator ('/').
+
+ This change only requires changes in the core preset handling code.
+
+ This would allow doing the following:
+ gst_preset_load_preset (h264enc,
+ "pass:1/profile:baseline/quality:high");
+
+2.7 Points to be determined
+---------------------------
+
+ This document hasn't determined yet how to solve the following
+ problems:
+
+2.7.1 Storage of profiles
+
+ One proposal for storage would be to use a system wide directory
+ (like $prefix/share/gstreamer-0.10/profiles) and store XML files for
+ every individual profiles.
+
+ Users could then add their own profiles in ~/.gstreamer-0.10/profiles
+
+ This poses some limitations as to what to do if some applications
+ want to have some profiles limited to their own usage.
+
+
+3. Helper library for profiles
+------------------------------
+
+ These helper methods could also be added to existing libraries (like
+ GstPreset, GstPbUtils, ..).
+
+ The various API proposed are in the accompanying gstprofile.h file.
+
+3.1 Getting user-readable names for formats
+
+ This is already provided by GstPbUtils.
+
+3.2 Hierarchy of profiles
+
+ The goal is for applications to be able to present to the user a list
+ of combo-boxes for choosing their output profile:
+
+ [ Category ] # optional, depends on the application
+ [ Device/Site/.. ] # optional, depends on the application
+ [ Profile ]
+
+ Convenience methods are offered to easily get lists of categories,
+ devices, and profiles.
+
+3.3 Creating Profiles
+
+ The goal is for applications to be able to easily create profiles.
+
+ The applications needs to be able to have a fast/efficient way to:
+ * select a container format and see all compatible streams he can use
+ with it.
+ * select a codec format and see which container formats he can use
+ with it.
+
+ The remaining parts concern the restrictions to encoder
+ input.
+
+3.4 Ensuring availability of plugins for Profiles
+
+ When an application wishes to use a Profile, it should be able to
+ query whether it has all the needed plugins to use it.
+
+ This part will use GstPbUtils to query, and if needed install the
+ missing plugins through the installed distribution plugin installer.
+
+
+
+
+* Research links
+
+ Some of these are still active documents, some other not
+
+[0] GstPreset API documentation
+ http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstPreset.html
+
+[1] gnome-media GConf profiles
+ http://www.gnome.org/~bmsmith/gconf-docs/C/gnome-media.html
+
+[2] Research on a Device Profile API
+ http://gstreamer.freedesktop.org/wiki/DeviceProfile
+
+[3] Research on defining presets usage
+ http://gstreamer.freedesktop.org/wiki/PresetDesign
+
--- /dev/null
+/* GStreamer encoding bin
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * (C) 2009 Nokia Corporation
+ *
+ * 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_ENCODEBIN_H__
+#define __GST_ENCODEBIN_H__
+
+#include <gst/gst.h>
+#include <gst/gstprofile.h>
+
+#define GST_TYPE_ENCODE_BIN (gst_encode_bin_get_type())
+#define GST_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODE_BIN,GstPlayBin))
+#define GST_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ENCODE_BIN,GstPlayBinClass))
+#define GST_IS_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODE_BIN))
+#define GST_IS_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ENCODE_BIN))
+
+typedef struct _GstEncodebin GstEncodeBin;
+
+struct _GstEncodeBin {
+ GstBin parent;
+
+ GstProfile *profile;
+};
+
+GType gst_encode_bin_get_type(void);
+
+GstElement *gst_encode_bin_new (GstProfile *profile, gchar *name);
+gboolean gst_encode_bin_set_profile (GstEncodeBin *ebin, GstProfile *profile);
+
+#endif __GST_ENCODEBIN_H__
--- /dev/null
+/* GStreamer encoding profiles library
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * (C) 2009 Nokia Corporation
+ *
+ * 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_PROFILE_H__
+#define __GST_PROFILE_H__
+
+#include <gst/gst.h>
+
+typedef enum {
+ GST_ENCODING_PROFILE_UNKNOWN,
+ GST_ENCODING_PROFILE_VIDEO,
+ GST_ENCODING_PROFILE_AUDIO,
+ GST_ENCODING_PROFILE_TEXT
+ /* Room for extenstion */
+} GstEncodingProfileType;
+
+typedef struct _GstEncodingTarget GstEncodingTarget;
+typedef struct _GstEncodingProfile GstEncodingProfile;
+typedef struct _GstStreamEncodingProfile GstStreamEncodingProfile;
+typedef struct _GstVideoEncodingProfile GstVideoEncodingProfile;
+
+/* FIXME/UNKNOWNS
+ *
+ * Should encoding categories be well-known strings/quarks ?
+ *
+ */
+
+/**
+ * GstEncodingTarget:
+ * @name: The name of the target profile.
+ * @category: The target category (device, service, use-case).
+ * @profiles: A list of #GstProfile this device supports.
+ *
+ */
+struct _GstEncodingTarget {
+ gchar *name;
+ gchar *category;
+ GList *profiles;
+}
+
+/**
+ * GstEncodingProfile:
+ * @name: The name of the profile
+ * @format: The GStreamer mime type corresponding to the muxing format.
+ * @preset: The name of the #GstPreset(s) to be used on the muxer. This is optional.
+ * @multipass: Whether this profile is a multi-pass profile or not.
+ * @encodingprofiles: A list of #GstStreamEncodingProfile for the various streams.
+ *
+ */
+
+struct _GstEncodingProfile {
+ gchar *name;
+ gchar *format;
+ gchar *preset;
+ gboolean multipass;
+ GList *encodingprofiles;
+};
+
+/**
+ * GstStreamEncodingProfile:
+ * @type: Type of profile
+ * @format: The GStreamer mime type corresponding to the encoding format.
+ * @preset: The name of the #GstPreset to be used on the encoder. This is optional.
+ * @restriction: The #GstCaps restricting the input. This is optional.
+ * @presence: The number of streams that can be created. 0 => any.
+ */
+struct _GstStreamEncodingProfile {
+ GstEncodingProfileType type;
+ gchar *format;
+ gchar *preset;
+ GstCaps *restriction;
+ guint presence;
+};
+
+/**
+ * GstVideoEncodingProfile:
+ * @profile: common #GstEncodingProfile part.
+ * @pass: The pass number if this is part of a multi-pass profile. Starts at 1
+ * for multi-pass. Set to 0 if this is not part of a multi-pass profile.
+ * @variable_framerate: Do not enforce framerate on incoming raw stream. Default
+ * is FALSE.
+ */
+struct _GstVideoEncodingProfile {
+ GstStreamEncodingProfile profile;
+ guint pass;
+ gboolean variable_framerate;
+};
+
+/* Generic helper API */
+/**
+ * gst_encoding_category_list_target:
+ * @category: a profile target category name. Can be NULL.
+ *
+ * Returns the list of all available #GstProfileTarget for the given @category.
+ * If @category is #NULL, then all available #GstProfileTarget are returned.
+ */
+GList *gst_encoding_category_list_target (gchar *category);
+
+/**
+ * list available profile target categories
+ */
+GList *gst_profile_list_target_categories ();
+
+gboolean gst_profile_target_save (GstProfileTarget *target);
+
+/**
+ * gst_encoding_profile_get_input_caps:
+ * @profile: a #GstEncodingProfile
+ *
+ * Returns: the list of all caps the profile can accept. Caller must call
+ * gst_cap_unref on all unwanted caps once it is done with the list.
+ */
+GList * gst_profile_get_input_caps (GstEncodingProfile *profile);
+
+/*
+ * Application convenience methods (possibly to be added in gst-pb-utils)
+ */
+
+/**
+ * gst_pb_utils_create_encoder:
+ * @caps: The #GstCaps corresponding to a codec format
+ * @preset: The name of a preset
+ * @name: The name to give to the returned instance, can be #NULL.
+ *
+ * Creates an encoder which can output the given @caps. If several encoders can
+ * output the given @caps, then the one with the highest rank will be picked.
+ * If a @preset is specified, it will be applied to the created encoder before
+ * returning it.
+ * If a @preset is specified, then the highest-ranked encoder that can accept
+ * the givein preset will be returned.
+ *
+ * Returns: The encoder instance with the preset applied if it is available.
+ * #NULL if no encoder is available.
+ */
+GstElement *gst_pb_utils_create_encoder(GstCaps *caps, gchar *preset, gchar *name);
+/**
+ * gst_pb_utils_create_encoder_format:
+ *
+ * Convenience version of @gst_pb_utils_create_encoder except one does not need
+ * to create a #GstCaps.
+ */
+GstElement *gst_pb_utils_create_encoder_format(gchar *format, gchar *preset,
+ gchar *name);
+
+/**
+ * gst_pb_utils_create_muxer:
+ * @caps: The #GstCaps corresponding to a codec format
+ * @preset: The name of a preset
+ *
+ * Creates an muxer which can output the given @caps. If several muxers can
+ * output the given @caps, then the one with the highest rank will be picked.
+ * If a @preset is specified, it will be applied to the created muxer before
+ * returning it.
+ * If a @preset is specified, then the highest-ranked muxer that can accept
+ * the givein preset will be returned.
+ *
+ * Returns: The muxer instance with the preset applied if it is available.
+ * #NULL if no muxer is available.
+ */
+GstElement *gst_pb_utils_create_muxer(GstCaps *caps, gchar *preset);
+/**
+ * gst_pb_utils_create_muxer_format:
+ *
+ * Convenience version of @gst_pb_utils_create_muxer except one does not need
+ * to create a #GstCaps.
+ */
+GstElement *gst_pb_utils_create_muxer_format(gchar *format, gchar *preset,
+ gchar *name);
+
+/**
+ * gst_pb_utils_encoders_compatible_with_muxer:
+ * @muxer: a muxer instance
+ *
+ * Finds a list of available encoders whose output can be fed to the given
+ * @muxer.
+ *
+ * Returns: A list of compatible encoders, or #NULL if none can be found.
+ */
+GList *gst_pb_utils_encoders_compatible_with_muxer(GstElement *muxer);
+
+GList *gst_pb_utils_muxers_compatible_with_encoder(GstElement *encoder);
+
+
+/*
+ * GstPreset modifications
+ */
+
+/**
+ * gst_preset_create:
+ * @preset: The #GstPreset on which to create the preset
+ * @name: A name for the preset
+ * @properties: The properties
+ *
+ * Creates a new preset with the given properties. This preset will only
+ * exist during the lifetime of the process.
+ * If you wish to use it after the lifetime of the process, you must call
+ * @gst_preset_save_preset.
+ *
+ * Returns: #TRUE if the preset could be created, else #FALSE.
+ */
+gboolean gst_preset_create (GstPreset *preset, gchar *name,
+ GstStructure *properties);
+
+/**
+ * gst_preset_reset:
+ * @preset: a #GstPreset
+ *
+ * Sets all the properties of the element back to their default values.
+ */
+/* FIXME : This could actually be put at the GstObject level, or maybe even
+ * at the GObject level. */
+void gst_preset_reset (GstPreset *preset);
+
+#endif /* __GST_PROFILE_H__ */
--- /dev/null
+Metadata
+~~~~~~~~
+
+Summary
+~~~~~~~
+
+1. Basic ideas
+2. Problems
+3. Ways of solving problems
+4. Use-cases
+5. API draft
+
+1. Basic ideas
+~~~~~~~~~~~~~~
+
+If we look at entities that are present in GES we can see that almost all of
+them need some sort of metadata:
+ * GESTimeline
+ * GESTimelineLayer
+ * GESTimelineObject
+ * GESTrackObject
+ * Yet to be implemented GESProject
+
+For all those classes to be able to contain metadatas and to avoid code
+duplication as much as possible, we should have an interface to handle Metadata.
+Let's call the interface GESMetaContainer for now (name to be defined).
+
+2. Problems
+~~~~~~~~~~~
+
+ 1) We must be able to discover all metadata items that are
+ attached to object
+ 2) We must be able to hold metadata of any type user wants
+ 3) Some metadatas are read only, others are writable
+ 4) User should be able to query metadata easily using various criteria
+ 5) Metadatas should be serializable
+ 6) User should be able to define read only metadatas with a default value
+ 7) User should be able to define metadatas that have a specific type which can not
+ be changed when setting a new value
+
+
+3. Possible solution
+~~~~~~~~~~~~~~~~~~~~~
+
+ 1) To implement metadata GstStructure will be used. It allows to get list of
+ all available tags in specified list by calling "gst_structure_foreach".
+ 2) We will have methods to register metas
+
+4. Use-cases
+~~~~~~~~~~~~
+
+ UC-1. Hold tag information about file source asset.
+ - TS: I think some of them are TrackObject specific... so we should be
+ able to get them from the 2 types of objects
+ UC-2. Hold descriptions of operations
+ UC-3. Hold information about projects (title, author, description)
+ UC-4. Hold user comments about any of TimelineLayer/Timeline/Project/TimelineObjects
+ UC-5. Hold application specific settings (i.e. layer height, folding state
+ in Pitivi)
+ UC-6. Serialize a timeline, project and keep metadatas
+
+5. API
+~~~~~~
+
+We have a GESMetdata class that controls metadata.
+
+gboolean
+ges_meta_container_set_boolean (GESMetaContainer *container,
+ const gchar* meta_item,
+ gboolean value);
+
+gboolean
+ges_meta_container_set_int (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint value);
+
+gboolean
+ges_meta_container_set_uint (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint value);
+
+gboolean
+ges_meta_container_set_int64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint64 value);
+
+gboolean
+ges_meta_container_set_uint64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint64 value);
+
+gboolean
+ges_meta_container_set_float (GESMetaContainer *container,
+ const gchar* meta_item,
+ gfloat value);
+
+gboolean
+ges_meta_container_set_double (GESMetaContainer *container,
+ const gchar* meta_item,
+ gdouble value);
+
+gboolean
+ges_meta_container_set_date (GESMetaContainer *container,
+ const gchar* meta_item,
+ const GDate* value);
+
+gboolean
+ges_meta_container_set_date_time (GESMetaContainer *container,
+ const gchar* meta_item,
+ const GstDateTime* value);
+
+gboolean
+ges_meta_container_set_string (GESMetaContainer *container,
+ const gchar* meta_item,
+ const gchar* value);
+
+gboolean
+ges_meta_container_set_meta (GESMetaContainer * container,
+ const gchar* meta_item,
+ const GValue *value);
+
+gboolean
+ges_meta_container_register_meta_boolean (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gboolean value);
+
+gboolean
+ges_meta_container_register_meta_int (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gint value);
+
+gboolean
+ges_meta_container_register_meta_uint (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ guint value);
+
+gboolean
+ges_meta_container_register_meta_int64 (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gint64 value);
+
+gboolean
+ges_meta_container_register_meta_uint64 (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ guint64 value);
+
+gboolean
+ges_meta_container_register_meta_float (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gfloat value);
+
+gboolean
+ges_meta_container_register_meta_double (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gdouble value);
+
+gboolean
+ges_meta_container_register_meta_date (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const GDate* value);
+
+gboolean
+ges_meta_container_register_meta_date_time (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const GstDateTime* value);
+
+gboolean
+ges_meta_container_register_meta_string (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const gchar* value);
+
+gboolean
+ges_meta_container_register_meta (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const GValue * value);
+
+gboolean
+ges_meta_container_check_meta_registered (GESMetaContainer *container,
+ const gchar * meta_item,
+ GESMetaFlag * flags,
+ GType * type);
+
+gboolean
+ges_meta_container_get_boolean (GESMetaContainer *container,
+ const gchar* meta_item,
+ gboolean* dest);
+
+gboolean
+ges_meta_container_get_int (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint* dest);
+
+gboolean
+ges_meta_container_get_uint (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint* dest);
+
+gboolean
+ges_meta_container_get_int64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint64* dest);
+
+gboolean
+ges_meta_container_get_uint64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint64* dest);
+
+gboolean
+ges_meta_container_get_float (GESMetaContainer *container,
+ const gchar* meta_item,
+ gfloat* dest);
+
+gboolean
+ges_meta_container_get_double (GESMetaContainer *container,
+ const gchar* meta_item,
+ gdouble* dest);
+
+gboolean
+ges_meta_container_get_date (GESMetaContainer *container,
+ const gchar* meta_item,
+ GDate** dest);
+
+gboolean
+ges_meta_container_get_date_time (GESMetaContainer *container,
+ const gchar* meta_item,
+ GstDateTime** dest);
+
+const gchar *
+ges_meta_container_get_string (GESMetaContainer * container,
+ const gchar * meta_item);
+
+const GValue *
+ges_meta_container_get_meta (GESMetaContainer * container,
+ const gchar * key);
+
+typedef void
+(*GESMetaForeachFunc) (const GESMetaContainer *container,
+ const gchar *key,
+ const GValue *value,
+ gpointer user_data);
+
+void
+ges_meta_container_foreach (GESMetaContainer *container,
+ GESMetaForeachFunc func,
+ gpointer user_data);
+
+gchar *
+ges_meta_container_metas_to_string (GESMetaContainer *container);
+
+gboolean
+ges_meta_container_add_metas_from_string (GESMetaContainer *container,
+ const gchar *str);
--- /dev/null
+ges-decl-list.txt
+ges-decl.txt
+ges-overrides.txt
+ges-undeclared.txt
+ges-undocumented.txt
+ges-unused.txt
+ges.args
+ges.hierarchy
+ges.interfaces
+ges.prerequisites
+ges.signals
+*.stamp
+
+html/
+tmpl/
+xml/
+doc-registry.xml
--- /dev/null
+GST_DOC_SCANOBJ = $(top_srcdir)/common/gstdoc-scangobj
+
+## Process this file with automake to produce Makefile.in
+
+# The name of the module, e.g. 'glib'.
+MODULE=ges
+DOC_MODULE=$(MODULE)
+
+# for upload-doc.mak
+DOC=gstreamer-editing-services
+FORMATS=html
+html: html-build.stamp
+include $(top_srcdir)/common/upload-doc.mak
+
+# The top-level SGML file. Change it if you want.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
+
+# The directory containing the source code.
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting functions and macros.
+DOC_SOURCE_DIR = $(top_srcdir)/ges
+
+SCAN_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkdb.
+MKDB_OPTIONS=--sgml-mode --source-suffixes=c,h,cc,m
+
+# Extra options to supply to gtkdoc-fixref.
+FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
+ --extra-dir=$(GST_PREFIX)/share/gtk-doc/html \
+ --extra-dir=$(GSTPB_PREFIX)/share/gtk-doc/html
+
+# Used for dependencies.
+HFILE_GLOB=$(top_srcdir)/ges/ges-*.h
+CFILE_GLOB=$(top_srcdir)/ges/ges-*.c
+
+# Extra options to supply to gtkdoc-scan.
+SCANOBJ_OPTIONS=--type-init-func="g_type_init();gst_init(&argc,&argv)"
+
+# Header files to ignore when scanning.
+IGNORE_HFILES = \
+ gesmarshal.h \
+ ges-internal.h \
+ ges-auto-transition.h \
+ ges-structured-interface.h \
+ ges-structure-parser.h \
+ ges-smart-video-mixer.h \
+ gstframepositioner.h
+IGNORE_CFILES =
+
+# we add all .h files of elements that have signals/args we want
+# sadly this also pulls in the private methods - maybe we should
+# move those around in the source ?
+# also, we should add some stuff here conditionally based on whether
+# or not the plugin will actually build
+# but I'm not sure about that - it might be this Just Works given that
+# the registry won't have the element
+
+EXTRA_HFILES = \
+ $(top_srcdir)/ges/ges-types.h
+
+# Images to copy into HTML directory.
+HTML_IMAGES = layer_track_overview.png
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+content_files = architecture.xml
+
+# Other files to distribute.
+extra_files =
+
+# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
+# contains GtkObjects/GObjects and you want to document signals and properties.
+GTKDOC_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_BASE_CFLAGS) \
+ $(GST_CFLAGS) $(GIO_CFLAGS) $(GCOV_CFLAGS)
+GTKDOC_LIBS = \
+ $(top_builddir)/ges/libges-@GST_API_VERSION@.la \
+ $(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS) $(GCOV_LIBS)
+
+GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC)
+GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC)
+
+# If you need to override some of the declarations, place them in this file
+# and uncomment this line.
+DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt
+
+include $(top_srcdir)/common/gtk-doc.mak
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+]>
+<refentry id="ges-architecture" revision="25 mar 2009">
+ <refmeta>
+ <refentrytitle>Overview and architecture</refentrytitle>
+
+ <manvolnum>1</manvolnum>
+
+ <refmiscinfo>GStreamer Editing Services</refmiscinfo>
+ </refmeta>
+
+ <!-- <refnamediv> -->
+
+ <!-- <refname>Overview</refname> -->
+
+ <!-- <refpurpose> -->
+
+ <!-- Goals of the GStreamer Editing Services. -->
+
+ <!-- </refpurpose> -->
+
+ <!-- </refnamediv> -->
+
+ <refsect1>
+ <title>Goals of GStreamer Editing Services</title>
+
+ <para>The GStreamer multimedia framework and the accompanying GNonLin set
+ of plugins for non-linear editing offer all the building blocks for:
+ <itemizedlist>
+ <listitem>
+ <para>Decoding and encoding to a wide variety of formats, through
+ all the available GStreamer plugins.</para>
+ </listitem>
+
+ <listitem>
+ <para>Easily choosing segments of streams and arranging them through
+ time through the GNonLin set of plugins.</para>
+ </listitem>
+ </itemizedlist></para>
+
+ <para>But all those building blocks only offer stream-level access, which
+ results in developers who want to write non-linear editors to write a
+ consequent amount of code to get to the level of <emphasis>non-linear
+ editing</emphasis> notions which are closer and more meaningful for the
+ end-user (and therefore the application).</para>
+
+ <para>The GStreamer Editing Services <remark>(hereafter GES)</remark> aims
+ to fill the gap between GStreamer/GNonLin and the application developer by
+ offering a series of classes to simplify the creation of many kind of
+ editing-related applications.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Architecture</title>
+
+ <refsect2>
+ <title>Timeline and TimelinePipeline</title>
+
+ <para>The most top-level object encapsulating every other object is the
+ <link linkend="GESTimeline">GESTimeline</link>. It is the central object
+ for any editing project.</para>
+
+ <para>The <classname>GESTimeline</classname> is a
+ <classname>GstElement</classname>. It can therefore be used in any
+ GStreamer pipeline like any other object.</para>
+ </refsect2>
+
+
+ <refsect2>
+ <title>Tracks and Layers</title>
+
+ <para>The GESTimeline can contain two types of objects (seen in <xref
+ linkend="layer_tracks_diagram" />): <itemizedlist>
+ <listitem>
+ <para>Layers - Corresponds to the user-visible arrangement of clips,
+ and what you primarily interact with as an application developer.
+ A minimalistic timeline would only have one layer,
+ but a more complex editing application could use as many as needed.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>Tracks - Corresponds to the output streams in GStreamer.
+ A typical GESTimeline, aimed at a video editing application, would
+ have an audio track and a video track.
+ A GESTimeline for an audio editing application would only require
+ an audio track. Multiple layers can be related to each track.</para>
+ </listitem>
+ </itemizedlist></para>
+
+ <figure float="0" id="layer_tracks_diagram">
+ <title>Layers and Tracks</title>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="layer_track_overview.png" scale="75" />
+ </imageobject>
+ </mediaobject>
+ </figure>
+
+ <para>In order to reduce even more the amount of GStreamer interaction
+ the application developer has to deal with, a convenience GstPipeline
+ has been made available specifically for Timelines : <link
+ linkend="GESPipeline">GESPipeline</link>.</para>
+ </refsect2>
+
+ </refsect1>
+</refentry>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY % version-entities SYSTEM "version.entities">
+%version-entities;
+]>
+
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>GStreamer Editing Services &GES_VERSION; Reference Manual</title>
+ <releaseinfo>
+ for GStreamer Editing Services &GST_API_VERSION; (&GES_VERSION;)
+ </releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>GStreamer Editing Services Overview</title>
+ <para>
+ The "GStreamer Editing Services" is a library to simplify the creation
+of multimedia editing applications. Based on the GStreamer multimedia framework
+and the GNonLin set of plugins, its goals are to suit all types of editing-related
+applications.
+ </para>
+
+ <para>
+ The GStreamer Editing Services are cross-platform and work on most UNIX-like
+platform as well as Windows. It is released under the GNU Library General Public License
+(GNU LGPL).
+ </para>
+ <xi:include href="architecture.xml"/>
+ <xi:include href="xml/ges-common.xml"/>
+ <xi:include href="xml/ges-enums.xml"/>
+ <xi:include href="xml/ges-gerror.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Base Classes</title>
+ <xi:include href="xml/gestimeline.xml"/>
+ <xi:include href="xml/geslayer.xml"/>
+ <xi:include href="xml/gestimelineelement.xml"/>
+ <xi:include href="xml/gescontainer.xml"/>
+ <xi:include href="xml/gesclip.xml"/>
+ <xi:include href="xml/gessourceclip.xml"/>
+ <xi:include href="xml/gesoperationclip.xml"/>
+ <xi:include href="xml/gesoverlayclip.xml"/>
+ <xi:include href="xml/gesbaseeffectclip.xml"/>
+ <xi:include href="xml/gestrack.xml"/>
+ <xi:include href="xml/gestrackelement.xml"/>
+ <xi:include href="xml/gessource.xml"/>
+ <xi:include href="xml/gesvideosource.xml"/>
+ <xi:include href="xml/gesaudiosource.xml"/>
+ <xi:include href="xml/gesbaseeffect.xml"/>
+ <xi:include href="xml/gesoperation.xml"/>
+ <xi:include href="xml/gesbasetransitionclip.xml"/>
+ <xi:include href="xml/gesasset.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Timeline objects</title>
+ <xi:include href="xml/gesuriclip.xml"/>
+ <xi:include href="xml/gestitleclip.xml"/>
+ <xi:include href="xml/gestestclip.xml"/>
+ <xi:include href="xml/gestextoverlayclip.xml"/>
+ <xi:include href="xml/gestransitionclip.xml"/>
+ <xi:include href="xml/geseffectclip.xml"/>
+ <xi:include href="xml/gesgroup.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Track objects</title>
+ <xi:include href="xml/gesaudiourisource.xml"/>
+ <xi:include href="xml/gesvideourisource.xml"/>
+ <xi:include href="xml/gestitlesource.xml"/>
+ <xi:include href="xml/gesaudiotestsource.xml"/>
+ <xi:include href="xml/gesvideotestsource.xml"/>
+ <xi:include href="xml/gestextoverlay.xml"/>
+ <xi:include href="xml/gestransition.xml"/>
+ <xi:include href="xml/gesvideotransition.xml"/>
+ <xi:include href="xml/gesaudiotransition.xml"/>
+ <xi:include href="xml/gesimagesource.xml"/>
+ <xi:include href="xml/gesmultifilesource.xml"/>
+ <xi:include href="xml/geseffect.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Convenience classes</title>
+ <xi:include href="xml/gespipeline.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Serialization Classes</title>
+ <xi:include href="xml/gesformatter.xml"/>
+ <xi:include href="xml/gespitiviformatter.xml"/>
+ <xi:include href="xml/gesbasexmlformatter.xml"/>
+ <xi:include href="xml/gesxmlformatter.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Interfaces</title>
+ <xi:include href="xml/gesmetacontainer.xml"/>
+ <xi:include href="xml/gesextractable.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Assets</title>
+ <xi:include href="xml/gesclipasset.xml"/>
+ <xi:include href="xml/gestrackelementasset.xml"/>
+ <xi:include href="xml/gesuriclipasset.xml"/>
+ <xi:include href="xml/gesurisourceasset.xml"/>
+ <xi:include href="xml/gesproject.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Tracks</title>
+ <xi:include href="xml/gesvideotrack.xml"/>
+ <xi:include href="xml/gesaudiotrack.xml"/>
+ </chapter>
+
+ <chapter id="ges-hierarchy">
+ <title>Object Hierarchy</title>
+ <xi:include href="xml/tree_index.sgml"/>
+ </chapter>
+
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
--- /dev/null
+<INCLUDE>ges/ges.h</INCLUDE>
+
+<SECTION>
+<FILE>ges-common</FILE>
+<TITLE>Initialization</TITLE>
+ges_init
+ges_is_initialized
+ges_init_check
+ges_deinit
+ges_version
+ges_init_get_option_group
+GES_VERSION_MAJOR
+GES_VERSION_MICRO
+GES_VERSION_MINOR
+GES_VERSION_NANO
+<SUBSECTION Standard>
+GES_PADDING
+GES_PADDING_LARGE
+GESAssetLoadingReturn
+</SECTION>
+
+<SECTION>
+<FILE>ges-utils</FILE>
+<TITLE>Utilities</TITLE>
+ges_add_missing_uri_relocation_uri
+
+ges_pspec_equal
+ges_pspec_hash
+
+</SECTION>
+
+<SECTION>
+<FILE>ges-gerror</FILE>
+<TITLE>GES GErrors</TITLE>
+GES_ERROR
+GESError
+</SECTION>
+
+<SECTION>
+<FILE>ges-enums</FILE>
+<TITLE>GES Enums</TITLE>
+GESTrackType
+GESVideoStandardTransitionType
+GESTextHAlign
+DEFAULT_HALIGNMENT
+GESTextVAlign
+DEFAULT_VALIGNMENT
+GESVideoTestPattern
+GESPipelineFlags
+GESEdge
+GESEditMode
+GESMetaFlag
+<SUBSECTION Standard>
+GES_TYPE_TRACK_TYPE
+ges_track_type_get_type
+GES_META_FLAG_TYPE
+ges_meta_flag_get_type
+GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE
+ges_video_standard_transition_type_get_type
+GES_TEXT_HALIGN_TYPE
+ges_text_halign_get_type
+GES_TEXT_VALIGN_TYPE
+ges_text_valign_get_type
+GES_VIDEO_TEST_PATTERN_TYPE
+ges_video_test_pattern_get_type
+GES_TYPE_PIPELINE_FLAGS
+ges_pipeline_flags_get_type
+GES_TYPE_EDGE
+ges_edge_get_type
+GES_TYPE_EDIT_MODE
+ges_edit_mode_get_type
+ges_track_type_name
+</SECTION>
+
+<SECTION>
+<FILE>gestrack</FILE>
+<TITLE>GESTrack</TITLE>
+GESTrack
+GESCreateElementForGapFunc
+ges_track_new
+ges_track_add_element
+ges_track_set_restriction_caps
+ges_track_update_restriction_caps
+ges_track_remove_element
+ges_track_set_caps
+ges_track_get_caps
+ges_track_enable_update
+ges_track_get_elements
+ges_track_is_updating
+ges_track_commit
+ges_track_get_mixing
+ges_track_set_mixing
+<SUBSECTION Standard>
+GESTrackClass
+GESTrackPrivate
+ges_track_set_timeline
+ges_track_get_timeline
+ges_track_get_type
+GES_IS_TRACK
+GES_IS_TRACK_CLASS
+GES_TRACK
+GES_TRACK_CLASS
+GES_TRACK_GET_CLASS
+GES_TYPE_TRACK
+</SECTION>
+
+<SECTION>
+<FILE>gesaudiotrack</FILE>
+<TITLE>GESAudioTrack</TITLE>
+GESAudioTrack
+ges_audio_track_new
+<SUBSECTION Standard>
+GESAudioTrackClass
+GESAudioTrackPrivate
+GES_IS_AUDIO_TRACK
+GES_IS_AUDIO_TRACK_CLASS
+GES_AUDIO_TRACK
+GES_AUDIO_TRACK_CLASS
+GES_AUDIO_TRACK_GET_CLASS
+GES_TYPE_AUDIO_TRACK
+ges_audio_track_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesvideotrack</FILE>
+<TITLE>GESVideoTrack</TITLE>
+GESVideoTrack
+ges_video_track_new
+<SUBSECTION Standard>
+GESVideoTrackClass
+GESVideoTrackPrivate
+GES_IS_VIDEO_TRACK
+GES_IS_VIDEO_TRACK_CLASS
+GES_VIDEO_TRACK
+GES_VIDEO_TRACK_CLASS
+GES_VIDEO_TRACK_GET_CLASS
+GES_TYPE_VIDEO_TRACK
+ges_video_track_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gestrackelement</FILE>
+<TITLE>GESTrackElement</TITLE>
+GESTrackElement
+GESTrackElementClass
+ges_track_element_set_active
+ges_track_element_get_track
+ges_track_element_get_nleobject
+ges_track_element_get_gnlobject
+ges_track_element_get_element
+ges_track_element_is_active
+ges_track_element_lookup_child
+ges_track_element_list_children_properties
+ges_track_element_set_child_property
+ges_track_element_set_child_properties
+ges_track_element_set_child_property_valist
+ges_track_element_set_child_property_by_pspec
+ges_track_element_get_child_property
+ges_track_element_get_child_properties
+ges_track_element_get_child_property_valist
+ges_track_element_get_child_property_by_pspec
+ges_track_element_edit
+ges_track_element_set_control_source
+ges_track_element_get_control_binding
+ges_track_element_get_all_control_bindings
+ges_track_element_remove_control_binding
+<SUBSECTION Standard>
+GESTrackElementPrivate
+ges_track_element_set_track
+ges_track_element_get_type
+GES_IS_TRACK_ELEMENT
+GES_IS_TRACK_ELEMENT_CLASS
+GES_TRACK_ELEMENT
+GES_TRACK_ELEMENT_CLASS
+GES_TRACK_ELEMENT_GET_CLASS
+GES_TYPE_TRACK_ELEMENT
+</SECTION>
+
+<SECTION>
+<FILE>gessource</FILE>
+<TITLE>GESSource</TITLE>
+GESSource
+GESSourceClass
+<SUBSECTION Standard>
+GESSourcePrivate
+GES_SOURCE
+GES_SOURCE_CLASS
+GES_SOURCE_GET_CLASS
+GES_TYPE_SOURCE
+GES_IS_SOURCE
+GES_IS_SOURCE_CLASS
+ges_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesoperation</FILE>
+<TITLE>GESOperation</TITLE>
+GESOperation
+GESOperationClass
+<SUBSECTION Standard>
+GESOperationPrivate
+ges_operation_get_type
+GES_IS_OPERATION
+GES_IS_OPERATION_CLASS
+GES_OPERATION
+GES_OPERATION_CLASS
+GES_OPERATION_GET_CLASS
+GES_TYPE_OPERATION
+</SECTION>
+
+<SECTION>
+<FILE>gesvideosource</FILE>
+<TITLE>GESVideoSource</TITLE>
+GESVideoSource
+<SUBSECTION Standard>
+GESVideoSourceClass
+GESVideoSourcePrivate
+GES_VIDEO_SOURCE
+GES_VIDEO_SOURCE_CLASS
+GES_VIDEO_SOURCE_GET_CLASS
+GES_TYPE_VIDEO_SOURCE
+GES_IS_VIDEO_SOURCE
+GES_IS_VIDEO_SOURCE_CLASS
+ges_video_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesaudiosource</FILE>
+<TITLE>GESAudioSource</TITLE>
+GESAudioSource
+<SUBSECTION Standard>
+GESAudioSourceClass
+GESAudioSourcePrivate
+GES_AUDIO_SOURCE
+GES_AUDIO_SOURCE_CLASS
+GES_AUDIO_SOURCE_GET_CLASS
+GES_TYPE_AUDIO_SOURCE
+GES_IS_AUDIO_SOURCE
+GES_IS_AUDIO_SOURCE_CLASS
+ges_audio_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesvideourisource</FILE>
+<TITLE>GESVideoUriSource</TITLE>
+GESVideoUriSource
+<SUBSECTION Standard>
+GESVideoUriSourceClass
+GESVideoUriSourcePrivate
+GES_VIDEO_URI_SOURCE
+GES_VIDEO_URI_SOURCE_CLASS
+GES_VIDEO_URI_SOURCE_GET_CLASS
+GES_TYPE_VIDEO_URI_SOURCE
+GES_IS_VIDEO_URI_SOURCE
+GES_IS_VIDEO_URI_SOURCE_CLASS
+ges_video_uri_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesaudiourisource</FILE>
+<TITLE>GESAudioUriSource</TITLE>
+GESAudioUriSource
+<SUBSECTION Standard>
+GESAudioUriSourceClass
+GESAudioUriSourcePrivate
+GES_AUDIO_URI_SOURCE
+GES_AUDIO_URI_SOURCE_CLASS
+GES_AUDIO_URI_SOURCE_GET_CLASS
+GES_TYPE_AUDIO_URI_SOURCE
+GES_IS_AUDIO_URI_SOURCE
+GES_IS_AUDIO_URI_SOURCE_CLASS
+ges_audio_uri_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesimagesource</FILE>
+<TITLE>GESImageSource</TITLE>
+GESImageSource
+<SUBSECTION Standard>
+GESImageSourcePrivate
+GES_IS_IMAGE_SOURCE
+GES_IS_IMAGE_SOURCE_CLASS
+GES_IMAGE_SOURCE
+GES_IMAGE_SOURCE_CLASS
+GES_IMAGE_SOURCE_GET_CLASS
+GES_TYPE_IMAGE_SOURCE
+GESImageSourceClass
+ges_image_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesmultifilesource</FILE>
+<TITLE>GESMultiFileSource</TITLE>
+GESMultiFileSource
+ges_multi_file_source_new
+<SUBSECTION Standard>
+GESMultiFileSourcePrivate
+GES_IS_MULTI_FILE_SOURCE
+GES_IS_MULTI_FILE_SOURCE_CLASS
+GES_MULTI_FILE_SOURCE
+GES_MULTI_FILE_SOURCE_CLASS
+GES_MULTI_FILE_SOURCE_GET_CLASS
+GES_TYPE_MULTI_FILE_SOURCE
+GESMultiFileSourceClass
+ges_multi_file_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gestransition</FILE>
+<TITLE>GESTransition</TITLE>
+GESTransition
+GESTransitionClass
+<SUBSECTION Standard>
+GESTransitionPrivate
+GES_IS_TRANSITION
+GES_IS_TRANSITION_CLASS
+GES_TRANSITION
+GES_TRANSITION_CLASS
+GES_TRANSITION_GET_CLASS
+GES_TYPE_TRANSITION
+ges_transition_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesaudiotransition</FILE>
+<TITLE>GESAudioTransition</TITLE>
+GESAudioTransition
+ges_audio_transition_new
+<SUBSECTION Standard>
+GESAudioTransitionClass
+GESAudioTransitionPrivate
+GES_IS_AUDIO_TRANSITION
+ges_audio_transition_get_type
+GES_IS_AUDIO_TRANSITION_CLASS
+GES_AUDIO_TRANSITION
+GES_AUDIO_TRANSITION_CLASS
+GES_AUDIO_TRANSITION_GET_CLASS
+GES_TYPE_AUDIO_TRANSITION
+</SECTION>
+
+<SECTION>
+<FILE>gesvideotransition</FILE>
+<TITLE>GESVideoTransition</TITLE>
+GESVideoTransition
+ges_video_transition_new
+ges_video_transition_set_transition_type
+ges_video_transition_get_transition_type
+ges_video_transition_set_border
+ges_video_transition_get_border
+ges_video_transition_set_inverted
+ges_video_transition_is_inverted
+<SUBSECTION Standard>
+GESVideoTransitionClass
+GESVideoTransitionPrivate
+ges_video_transition_get_type
+GES_IS_VIDEO_TRANSITION
+GES_IS_VIDEO_TRANSITION_CLASS
+GES_VIDEO_TRANSITION
+GES_VIDEO_TRANSITION_CLASS
+GES_VIDEO_TRANSITION_GET_CLASS
+GES_TYPE_VIDEO_TRANSITION
+</SECTION>
+
+<SECTION>
+<FILE>gestimeline</FILE>
+<TITLE>GESTimeline</TITLE>
+GESTimeline
+ges_timeline_new
+ges_timeline_new_audio_video
+ges_timeline_new_from_uri
+ges_timeline_add_layer
+ges_timeline_append_layer
+ges_timeline_remove_layer
+ges_timeline_add_track
+ges_timeline_remove_track
+ges_timeline_load_from_uri
+ges_timeline_save_to_uri
+ges_timeline_enable_update
+ges_timeline_is_updating
+ges_timeline_commit
+ges_timeline_commit_sync
+ges_timeline_move_layer
+<SUBSECTION usage>
+ges_timeline_get_tracks
+ges_timeline_get_layer
+ges_timeline_get_layers
+ges_timeline_get_track_for_pad
+ges_timeline_get_pad_for_track
+ges_timeline_get_duration
+ges_timeline_get_project
+ges_timeline_get_auto_transition
+ges_timeline_set_auto_transition
+ges_timeline_get_snapping_distance
+ges_timeline_set_snapping_distance
+ges_timeline_get_element
+ges_timeline_is_empty
+GES_TIMELINE_GET_LAYERS
+GES_TIMELINE_GET_TRACKS
+<SUBSECTION Standard>
+GESTimelinePrivate
+GESTimelineClass
+ges_timeline_get_type
+GES_IS_TIMELINE
+GES_IS_TIMELINE_CLASS
+GES_TIMELINE
+GES_TIMELINE_CLASS
+GES_TIMELINE_GET_CLASS
+GES_TYPE_TIMELINE
+</SECTION>
+
+
+<SECTION>
+<FILE>geslayer</FILE>
+<TITLE>GESLayer</TITLE>
+GESLayer
+GESLayerClass
+ges_layer_add_clip
+ges_layer_add_asset
+ges_layer_new
+ges_layer_remove_clip
+ges_layer_set_priority
+ges_layer_get_priority
+ges_layer_get_clips
+ges_layer_get_timeline
+ges_layer_get_auto_transition
+ges_layer_set_auto_transition
+ges_layer_is_empty
+ges_layer_get_duration
+<SUBSECTION Standard>
+GESLayerPrivate
+ges_layer_set_timeline
+ges_layer_get_type
+GES_IS_LAYER
+GES_IS_LAYER_CLASS
+GES_LAYER
+GES_LAYER_CLASS
+GES_LAYER_GET_CLASS
+GES_TYPE_LAYER
+</SECTION>
+
+<SECTION>
+<FILE>gestimelineelement</FILE>
+<TITLE>GESTimelineElement</TITLE>
+GESTimelineElement
+GESTimelineElementClass
+ges_timeline_element_set_parent
+ges_timeline_element_get_parent
+ges_timeline_element_set_timeline
+ges_timeline_element_get_timeline
+ges_timeline_element_set_start
+ges_timeline_element_set_inpoint
+ges_timeline_element_set_duration
+ges_timeline_element_set_max_duration
+ges_timeline_element_set_priority
+ges_timeline_element_get_start
+ges_timeline_element_get_inpoint
+ges_timeline_element_get_duration
+ges_timeline_element_get_max_duration
+ges_timeline_element_get_priority
+ges_timeline_element_ripple
+ges_timeline_element_ripple_end
+ges_timeline_element_roll_start
+ges_timeline_element_roll_end
+ges_timeline_element_trim
+ges_timeline_element_get_toplevel_parent
+ges_timeline_element_copy
+ges_timeline_element_paste
+ges_timeline_element_get_name
+ges_timeline_element_set_name
+ges_timeline_element_list_children_properties
+ges_timeline_element_lookup_child
+ges_timeline_element_get_child_property_by_pspec
+ges_timeline_element_get_child_property_valist
+ges_timeline_element_get_child_properties
+ges_timeline_element_set_child_property_valist
+ges_timeline_element_set_child_property_by_pspec
+ges_timeline_element_set_child_properties
+ges_timeline_element_set_child_property
+ges_timeline_element_get_child_property
+ges_timeline_element_add_child_property
+ges_timeline_element_remove_child_property
+ges_timeline_element_get_track_types
+GES_TIMELINE_ELEMENT_PARENT
+GES_TIMELINE_ELEMENT_TIMELINE
+GES_TIMELINE_ELEMENT_START
+GES_TIMELINE_ELEMENT_END
+GES_TIMELINE_ELEMENT_INPOINT
+GES_TIMELINE_ELEMENT_DURATION
+GES_TIMELINE_ELEMENT_MAX_DURATION
+GES_TIMELINE_ELEMENT_PRIORITY
+GES_TIMELINE_ELEMENT_NAME
+<SUBSECTION Standard>
+GESTimelineElementPrivate
+ges_timeline_element_get_type
+GES_TYPE_TIMELINE_ELEMENT
+GES_TIMELINE_ELEMENT
+GES_TIMELINE_ELEMENT_CLASS
+GES_IS_TIMELINE_ELEMENT
+GES_IS_TIMELINE_ELEMENT_CLASS
+GES_TIMELINE_ELEMENT_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gescontainer</FILE>
+<TITLE>GESContainer</TITLE>
+GESContainer
+GESContainerClass
+GES_CONTAINER_CHILDREN
+GES_CONTAINER_HEIGHT
+ges_container_get_children
+ges_container_add
+ges_container_remove
+ges_container_ungroup
+ges_container_group
+ges_container_edit
+<SUBSECTION Standard>
+GESContainerPrivate
+ges_container_get_type
+GES_TYPE_CONTAINER
+GES_CONTAINER
+GES_CONTAINER_CLASS
+GES_IS_CONTAINER
+GES_IS_CONTAINER_CLASS
+GES_CONTAINER_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesclip</FILE>
+<TITLE>GESClip</TITLE>
+GESClip
+GESClipClass
+GESCreateTrackElementFunc
+GESCreateTrackElementsFunc
+GESFillTrackElementFunc
+ges_clip_get_layer
+ges_clip_find_track_element
+ges_clip_find_track_elements
+ges_clip_add_asset
+ges_clip_get_top_effects
+ges_clip_get_top_effect_index
+ges_clip_set_top_effect_index
+ges_clip_get_top_effect_position
+ges_clip_set_top_effect_priority
+ges_clip_move_to_layer
+ges_clip_set_supported_formats
+ges_clip_get_supported_formats
+ges_clip_split
+ges_clip_edit
+GES_CLIP_HEIGHT
+<SUBSECTION Standard>
+GESClipPrivate
+GES_IS_CLIP
+GES_IS_CLIP_CLASS
+GES_CLIP
+GES_CLIP_CLASS
+GES_CLIP_GET_CLASS
+GES_TYPE_CLIP
+ges_clip_get_type
+</SECTION>
+
+
+<SECTION>
+<FILE>gespipeline</FILE>
+<TITLE>GESPipeline</TITLE>
+GESPipeline
+ges_pipeline_new
+ges_pipeline_set_timeline
+ges_pipeline_set_mode
+ges_pipeline_set_render_settings
+ges_pipeline_preview_get_audio_sink
+ges_pipeline_preview_get_video_sink
+ges_pipeline_preview_set_audio_sink
+ges_pipeline_preview_set_video_sink
+ges_pipeline_get_mode
+ges_pipeline_get_thumbnail
+ges_pipeline_get_thumbnail_rgb24
+ges_pipeline_save_thumbnail
+<SUBSECTION Standard>
+GESPipelineClass
+GESPipelinePrivate
+ges_play_sink_convert_frame
+ges_pipeline_get_type
+GES_PIPELINE
+GES_PIPELINE_CLASS
+GES_PIPELINE_GET_CLASS
+GES_IS_PIPELINE
+GES_IS_PIPELINE_CLASS
+GES_TYPE_PIPELINE
+</SECTION>
+
+
+<SECTION>
+<FILE>gessourceclip</FILE>
+<TITLE>GESSourceClip</TITLE>
+GESSourceClip
+GESSourceClipClass
+<SUBSECTION Standard>
+GESSourceClipPrivate
+ges_source_clip_get_type
+GES_IS_SOURCE_CLIP
+GES_IS_SOURCE_CLIP_CLASS
+GES_SOURCE_CLIP
+GES_SOURCE_CLIP_CLASS
+GES_SOURCE_CLIP_GET_CLASS
+GES_TYPE_SOURCE_CLIP
+</SECTION>
+
+<SECTION>
+<FILE>gesuriclip</FILE>
+<TITLE>GESUriClip</TITLE>
+GESUriClip
+ges_uri_clip_new
+ges_uri_clip_get_uri
+ges_uri_clip_is_image
+ges_uri_clip_is_muted
+ges_uri_clip_set_is_image
+ges_uri_clip_set_mute
+<SUBSECTION Standard>
+GESUriClipClass
+GESUriClipPrivate
+ges_uri_clip_get_type
+GES_IS_URI_CLIP
+GES_IS_URI_CLIP_CLASS
+GES_URI_CLIP
+GES_URI_CLIP_CLASS
+GES_URI_CLIP_GET_CLASS
+GES_TYPE_URI_CLIP
+</SECTION>
+
+<SECTION>
+<FILE>gesoperationclip</FILE>
+<TITLE>GESOperationClip</TITLE>
+GESOperationClip
+<SUBSECTION Standard>
+GESOperationClipClass
+GESOperationClipPrivate
+GES_OPERATION_CLIP
+GES_IS_OPERATION_CLIP
+GES_TYPE_OPERATION_CLIP
+ges_operation_clip_get_type
+GES_OPERATION_CLIP_CLASS
+GES_IS_OPERATION_CLIP_CLASS
+GES_OPERATION_CLIP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesoverlayclip</FILE>
+<TITLE>GESOverlayClip</TITLE>
+GESOverlayClip
+GESOverlayClipClass
+<SUBSECTION Standard>
+GESOverlayClipPrivate
+ges_overlay_clip_get_type
+GES_IS_OVERLAY_CLIP
+GES_IS_OVERLAY_CLIP_CLASS
+GES_OVERLAY_CLIP
+GES_OVERLAY_CLIP_CLASS
+GES_OVERLAY_CLIP_GET_CLASS
+GES_TYPE_OVERLAY_CLIP
+</SECTION>
+
+<SECTION>
+<FILE>gesbasetransitionclip</FILE>
+<TITLE>GESBaseTransitionClip</TITLE>
+GESBaseTransitionClip
+<SUBSECTION Standard>
+GESBaseTransitionClipClass
+GESBaseTransitionClipPrivate
+GES_BASE_TRANSITION_CLIP
+GES_IS_BASE_TRANSITION_CLIP
+GES_TYPE_BASE_TRANSITION_CLIP
+ges_base_transition_clip_get_type
+GES_BASE_TRANSITION_CLIP_CLASS
+GES_IS_BASE_TRANSITION_CLIP_CLASS
+GES_BASE_TRANSITION_CLIP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesbaseeffectclip</FILE>
+<TITLE>GESBaseEffectClip</TITLE>
+GESBaseEffectClip
+<SUBSECTION Standard>
+GESBaseEffectClipClass
+GESBaseEffectClipPrivate
+GES_BASE_EFFECT_CLIP
+GES_IS_BASE_EFFECT_CLIP
+GES_TYPE_BASE_EFFECT_CLIP
+ges_base_effect_clip_get_type
+GES_BASE_EFFECT_CLIP_CLASS
+GES_IS_BASE_EFFECT_CLIP_CLASS
+GES_BASE_EFFECT_CLIP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>geseffectclip</FILE>
+<TITLE>GESEffectClip</TITLE>
+GESEffectClip
+ges_effect_clip_new
+<SUBSECTION Standard>
+GESEffectClipClass
+GESEffectClipPrivate
+GES_EFFECT_CLIP_CLASS
+GES_EFFECT_CLIP
+GES_IS_EFFECT_CLIP
+GES_TYPE_EFFECT_CLIP
+ges_effect_clip_get_type
+GES_EFFECT_CLIP_CLASS_
+GES_IS_EFFECT_CLIP_CLASS
+GES_EFFECT_CLIP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gestransitionclip</FILE>
+<TITLE>GESTransitionClip</TITLE>
+GESTransitionClip
+ges_transition_clip_new
+ges_transition_clip_new_for_nick
+<SUBSECTION Standard>
+GESTransitionClipClass
+GESTransitionClipPrivate
+GES_IS_TRANSITION_CLIP
+GES_IS_TRANSITION_CLIP_CLASS
+GES_TRANSITION_CLIP
+GES_TRANSITION_CLIP_CLASS
+GES_TRANSITION_CLIP_GET_CLASS
+GES_TYPE_TRANSITION_CLIP
+ges_transition_clip_get_type
+</SECTION>
+
+
+<SECTION>
+<FILE>gestestclip</FILE>
+<TITLE>GESTestClip</TITLE>
+GESTestClip
+GESTestClipClass
+ges_test_clip_new
+ges_test_clip_new_for_nick
+ges_test_clip_get_vpattern
+ges_test_clip_get_frequency
+ges_test_clip_get_volume
+ges_test_clip_is_muted
+ges_test_clip_set_vpattern
+ges_test_clip_set_frequency
+ges_test_clip_set_mute
+ges_test_clip_set_volume
+<SUBSECTION Standard>
+GESTestClipPrivate
+ges_test_clip_get_type
+GES_TYPE_TEST_CLIP
+GES_IS_TEST_CLIP
+GES_IS_TEST_CLIP_CLASS
+GES_TEST_CLIP
+GES_TEST_CLIP_CLASS
+GES_TEST_CLIP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gestitleclip</FILE>
+<TITLE>GESTitleClip</TITLE>
+GESTitleClip
+ges_title_clip_new
+ges_title_clip_set_text
+ges_title_clip_set_font_desc
+ges_title_clip_set_halignment
+ges_title_clip_set_valignment
+ges_title_clip_set_color
+ges_title_clip_set_background
+ges_title_clip_set_xpos
+ges_title_clip_set_ypos
+ges_title_clip_get_text
+ges_title_clip_get_font_desc
+ges_title_clip_get_valignment
+ges_title_clip_get_halignment
+ges_title_clip_get_text_color
+ges_title_clip_get_background_color
+ges_title_clip_get_xpos
+ges_title_clip_get_ypos
+<SUBSECTION Standard>
+GESTitleClipClass
+GESTitleClipPrivate
+ges_title_clip_get_type
+GES_IS_TITLE_CLIP
+GES_IS_TITLE_CLIP_CLASS
+GES_TITLE_CLIP
+GES_TITLE_CLIP_CLASS
+GES_TITLE_CLIP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gestextoverlayclip</FILE>
+<TITLE>GESTextOverlayClip</TITLE>
+GESTextOverlayClip
+ges_text_overlay_clip_new
+ges_text_overlay_clip_set_text
+ges_text_overlay_clip_set_font_desc
+ges_text_overlay_clip_set_valign
+ges_text_overlay_clip_set_halign
+ges_text_overlay_clip_set_color
+ges_text_overlay_clip_set_xpos
+ges_text_overlay_clip_set_ypos
+ges_text_overlay_clip_get_text
+ges_text_overlay_clip_get_font_desc
+ges_text_overlay_clip_get_valignment
+ges_text_overlay_clip_get_halignment
+ges_text_overlay_clip_get_color
+ges_text_overlay_clip_get_xpos
+ges_text_overlay_clip_get_ypos
+<SUBSECTION Standard>
+GESTextOverlayClipClass
+GESTextOverlayClipPrivate
+ges_text_overlay_clip_get_type
+GES_IS_OVERLAY_TEXT_CLIP
+GES_IS_OVERLAY_TEXT_CLIP_CLASS
+GES_OVERLAY_TEXT_CLIP
+GES_OVERLAY_TEXT_CLIP_CLASS
+GES_OVERLAY_TEXT_CLIP_GET_CLASS
+GES_TYPE_OVERLAY_TEXT_CLIP
+</SECTION>
+
+<SECTION>
+<FILE>gesvideotestsource</FILE>
+<TITLE>GESVideoTestSource</TITLE>
+GESVideoTestSource
+ges_video_test_source_set_pattern
+ges_video_test_source_get_pattern
+<SUBSECTION Standard>
+GESVideoTestSourceClass
+GESVideoTestSourcePrivate
+ges_video_test_source_get_type
+GES_IS_VIDEO_TEST_SOURCE
+GES_IS_VIDEO_TEST_SOURCE_CLASS
+GES_VIDEO_TEST_SOURCE
+GES_VIDEO_TEST_SOURCE_CLASS
+GES_VIDEO_TEST_SOURCE_GET_CLASS
+GES_TYPE_VIDEO_TEST_SOURCE
+</SECTION>
+
+<SECTION>
+<FILE>gesaudiotestsource</FILE>
+<TITLE>GESAudioTestSource</TITLE>
+GESAudioTestSource
+ges_audio_test_source_set_freq
+ges_audio_test_source_set_volume
+ges_audio_test_source_get_freq
+ges_audio_test_source_get_volume
+<SUBSECTION Standard>
+GESAudioTestSourceClass
+GESAudioTestSourcePrivate
+ges_audio_test_source_get_type
+GES_AUDIO_TEST_SOURCE
+GES_AUDIO_TEST_SOURCE_CLASS
+GES_AUDIO_TEST_SOURCE_GET_CLASS
+GES_TYPE_AUDIO_TEST_SOURCE
+GES_IS_AUDIO_TEST_SOURCE
+GES_IS_AUDIO_TEST_SOURCE_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gestitlesource</FILE>
+<TITLE>GESTitleSource</TITLE>
+GESTitleSource
+ges_title_source_set_text
+ges_title_source_set_font_desc
+ges_title_source_set_halignment
+ges_title_source_set_valignment
+ges_title_source_set_text_color
+ges_title_source_set_background_color
+ges_title_source_set_xpos
+ges_title_source_set_ypos
+ges_title_source_get_text
+ges_title_source_get_font_desc
+ges_title_source_get_halignment
+ges_title_source_get_valignment
+ges_title_source_get_text_color
+ges_title_source_get_background_color
+ges_title_source_get_xpos
+ges_title_source_get_ypos
+<SUBSECTION Standard>
+GESTitleSourceClass
+GESTitleSourcePrivate
+ges_title_source_get_type
+GES_TITLE_SOURCE
+GES_TITLE_SOURCE_CLASS
+GES_TITLE_SOURCE_GET_CLASS
+GES_TYPE_TITLE_CLIP
+GES_TYPE_TITLE_SOURCE
+GES_IS_TITLE_SOURCE
+GES_IS_TITLE_SOURCE_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gestextoverlay</FILE>
+<TITLE>GESTextOverlay</TITLE>
+GESTextOverlay
+ges_text_overlay_new
+ges_text_overlay_set_text
+ges_text_overlay_set_font_desc
+ges_text_overlay_set_halignment
+ges_text_overlay_set_valignment
+ges_text_overlay_set_color
+ges_text_overlay_set_xpos
+ges_text_overlay_set_ypos
+ges_text_overlay_get_text
+ges_text_overlay_get_font_desc
+ges_text_overlay_get_halignment
+ges_text_overlay_get_valignment
+ges_text_overlay_get_color
+ges_text_overlay_get_xpos
+ges_text_overlay_get_ypos
+<SUBSECTION Standard>
+GESTextOverlayClass
+GESTextOverlayPrivate
+ges_text_overlay_get_type
+GES_IS_TEXT_OVERLAY
+GES_IS_TEXT_OVERLAY_CLASS
+GES_TEXT_OVERLAY
+GES_TEXT_OVERLAY_CLASS
+GES_TEXT_OVERLAY_GET_CLASS
+GES_TYPE_TEXT_OVERLAY
+</SECTION>
+
+<SECTION>
+<FILE>gesformatter</FILE>
+<TITLE>GESFormatter</TITLE>
+GESFormatter
+GESFormatterClass
+
+GESFormatterLoadFromURIMethod
+GESFormatterSaveToURIMethod
+GESFormatterCanLoadURIMethod
+GESFormatterCanSaveURIMethod
+
+ges_formatter_class_register_metas
+
+ges_formatter_load_from_uri
+ges_formatter_save_to_uri
+ges_formatter_can_load_uri
+ges_formatter_can_save_uri
+ges_formatter_get_default
+
+<SUBSECTION Standard>
+GES_FORMATTER
+GES_FORMATTER_CLASS
+GES_FORMATTER_GET_CLASS
+GES_IS_FORMATTER
+GES_IS_FORMATTER_CLASS
+GES_TYPE_FORMATTER
+<SUBSECTION Private>
+ges_formatter_get_type
+GESFormatterPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gespitiviformatter</FILE>
+<TITLE>GESPitiviFormatter</TITLE>
+GESPitiviFormatter
+ges_pitivi_formatter_new
+<SUBSECTION Standard>
+GESPitiviFormatterClass
+GESPitiviFormatterPrivate
+GES_TYPE_PITIVI_FORMATTER
+GES_IS_PITIVI_FORMATTER
+GES_IS_PITIVI_FORMATTER_CLASS
+GES_PITIVI_FORMATTER
+GES_PITIVI_FORMATTER_CLASS
+GES_PITIVI_FORMATTER_GET_CLASS
+GES_TYPE_PITIVIFORMATTER
+ges_pitivi_formatter_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesbaseeffect</FILE>
+<TITLE>GESBaseEffect</TITLE>
+GESBaseEffect
+<SUBSECTION Standard>
+GESBaseEffectClass
+GESBaseEffectPrivate
+GES_IS_BASE_EFFECT
+GES_IS_BASE_EFFECT_CLASS
+GES_BASE_EFFECT
+GES_BASE_EFFECT_CLASS
+GES_BASE_EFFECT_GET_CLASS
+GES_TYPE_BASE_EFFECT
+ges_base_effect_get_type
+</SECTION>
+
+<SECTION>
+<FILE>geseffect</FILE>
+<TITLE>GESEffect</TITLE>
+GESEffect
+ges_effect_new
+<SUBSECTION Standard>
+GESEffectClass
+GESEffectPrivate
+GES_IS_EFFECT
+GES_IS_EFFECT_CLASS
+GES_EFFECT
+GES_EFFECT_CLASS
+GES_EFFECT_GET_CLASS
+GES_TYPE_EFFECT
+ges_effect_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesmetacontainer</FILE>
+<TITLE>GESMetaContainer</TITLE>
+GESMetaContainer
+GESMetaForeachFunc
+ges_meta_container_foreach
+ges_meta_container_get_meta
+ges_meta_container_get_boolean
+ges_meta_container_get_date
+ges_meta_container_get_date_time
+ges_meta_container_get_double
+ges_meta_container_get_float
+ges_meta_container_get_int
+ges_meta_container_get_int64
+ges_meta_container_get_string
+ges_meta_container_get_uint
+ges_meta_container_get_uint64
+ges_meta_container_set_boolean
+ges_meta_container_set_date
+ges_meta_container_set_date_time
+ges_meta_container_set_double
+ges_meta_container_set_float
+ges_meta_container_set_int
+ges_meta_container_set_int64
+ges_meta_container_set_string
+ges_meta_container_set_uint
+ges_meta_container_set_uint64
+ges_meta_container_set_meta
+ges_meta_container_register_meta_boolean
+ges_meta_container_register_meta_int
+ges_meta_container_register_meta_uint
+ges_meta_container_register_meta_int64
+ges_meta_container_register_meta_uint64
+ges_meta_container_register_meta_float
+ges_meta_container_register_meta_double
+ges_meta_container_register_meta_date
+ges_meta_container_register_meta_date_time
+ges_meta_container_register_meta_string
+ges_meta_container_register_meta
+ges_meta_container_metas_to_string
+ges_meta_container_add_metas_from_string
+ges_meta_container_get_type
+ges_meta_container_check_meta_registered
+
+GES_META_FORMATTER_NAME
+GES_META_FORMATTER_MIMETYPE
+GES_META_FORMATTER_EXTENSION
+GES_META_FORMATTER_VERSION
+GES_META_FORMATTER_RANK
+GES_META_DESCRIPTION
+
+GES_META_FORMAT_VERSION
+
+<SUBSECTION Standard>
+GESMetaContainerInterface
+GES_IS_META_CONTAINER
+GES_META_CONTAINER
+GES_META_CONTAINER_GET_INTERFACE
+GES_TYPE_META_CONTAINER
+ges_meta_container_get_ype
+</SECTION>
+
+<SECTION>
+<FILE>gesextractable</FILE>
+<TITLE>GESExtractableInterface</TITLE>
+GESExtractable
+GESExtractableInterface
+GESExtractableCheckId
+ges_extractable_get_asset
+ges_extractable_set_asset
+ges_extractable_get_id
+<SUBSECTION Standard>
+GES_IS_EXTRACTABLE
+GES_EXTRACTABLE
+GES_EXTRACTABLE_GET_INTERFACE
+GES_TYPE_EXTRACTABLE
+ges_extractable_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesasset</FILE>
+<TITLE>GESAsset</TITLE>
+GESAsset
+ges_asset_get_extractable_type
+ges_asset_get_id
+ges_asset_request
+ges_asset_request_async
+ges_asset_request_finish
+ges_asset_extract
+ges_asset_get_error
+ges_list_assets
+ges_asset_set_proxy
+ges_asset_list_proxies
+ges_asset_get_proxy_target
+ges_asset_get_proxy
+ges_asset_needs_reload
+<SUBSECTION Standard>
+GESAssetPrivate
+GES_ASSET
+GES_TYPE_ASSET
+GES_ASSET_CLASS
+GES_IS_ASSET
+GES_IS_ASSET_CLASS
+GES_ASSET_GET_CLASS
+ges_asset_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gesclipasset</FILE>
+<TITLE>GESClipAsset</TITLE>
+GESClipAsset
+ges_clip_asset_get_type
+ges_clip_asset_set_supported_formats
+ges_clip_asset_get_supported_formats
+<SUBSECTION Standard>
+GESClipAssetPrivate
+GES_CLIP_ASSET
+GES_TYPE_CLIP_ASSET
+GES_CLIP_ASSET_CLASS
+GES_IS_CLIP_ASSET
+GES_IS_CLIP_ASSET_CLASS
+GES_CLIP_ASSET_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gestrackelementasset</FILE>
+<TITLE>GESTrackElementAsset</TITLE>
+GESTrackElementAsset
+ges_track_element_asset_get_type
+ges_track_element_asset_get_track_type
+ges_track_element_asset_set_track_type
+ges_track_element_add_children_props
+<SUBSECTION Standard>
+GESTrackElementAssetPrivate
+GES_TRACK_ELEMENT_ASSET
+GES_TYPE_TRACK_ELEMENT_ASSET
+GES_TRACK_ELEMENT_ASSET_CLASS
+GES_IS_TRACK_ELEMENT_ASSET
+GES_IS_TRACK_ELEMENT_ASSET_CLASS
+GES_TRACK_ELEMENT_ASSET_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesuriclipasset</FILE>
+<TITLE>GESUriClipAsset</TITLE>
+GESUriClipAsset
+ges_uri_clip_asset_get_type
+ges_uri_clip_asset_get_duration
+ges_uri_clip_asset_is_image
+ges_uri_clip_asset_get_info
+ges_uri_clip_asset_new
+ges_uri_clip_asset_request_sync
+ges_uri_clip_asset_get_stream_assets
+ges_uri_clip_asset_class_set_timeout
+<SUBSECTION Standard>
+GESUriClipAssetPrivate
+GES_URI_CLIP_ASSET
+GES_TYPE_URI_CLIP_ASSET
+GES_URI_CLIP_ASSET_CLASS
+GES_IS_URI_CLIP_ASSET
+GES_IS_URI_CLIP_ASSET_CLASS
+GES_URI_CLIP_ASSET_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesgroup</FILE>
+<TITLE>GESGroup</TITLE>
+GESGroup
+ges_group_new
+<SUBSECTION Standard>
+GESGroupClass
+GESGroupPrivate
+GES_GROUP
+GES_IS_GROUP
+GES_TYPE_GROUP
+ges_group_get_type
+GES_GROUP_CLASS
+GES_IS_GROUP_CLASS
+GES_GROUP_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesurisourceasset</FILE>
+<TITLE>GESUriSourceAsset</TITLE>
+GESUriSourceAsset
+ges_uri_source_asset_get_type
+ges_uri_source_asset_get_filesource_asset
+ges_uri_source_asset_get_stream_info
+ges_uri_source_asset_get_stream_uri
+<SUBSECTION Standard>
+GESUriSourceAssetPrivate
+GES_URI_SOURCE_ASSET
+GES_TYPE_URI_SOURCE_ASSET
+GES_URI_SOURCE_ASSET_CLASS
+GES_IS_URI_SOURCE_ASSET
+GES_IS_URI_SOURCE_ASSET_CLASS
+GES_URI_SOURCE_ASSET_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gesproject</FILE>
+<TITLE>GESProject</TITLE>
+GESProject
+ges_project_load
+ges_project_add_asset
+ges_project_remove_asset
+ges_project_list_assets
+ges_project_get_asset
+ges_project_save
+ges_project_create_asset
+ges_project_create_asset_sync
+ges_project_get_type
+ges_project_get_uri
+ges_project_new
+ges_project_add_encoding_profile
+ges_project_list_encoding_profiles
+ges_project_get_loading_assets
+<SUBSECTION Standard>
+GESProjectPrivate
+GES_PROJECT
+GES_PROJECT_CLASS
+GES_IS_PROJECT
+GES_IS_PROJECT_CLASS
+GES_PROJECT_GET_CLASS
+GES_TYPE_PROJECT
+</SECTION>
+
+<SECTION>
+<FILE>gesbasexmlformatter</FILE>
+<TITLE>GESBaseXmlFormatter</TITLE>
+GESBaseXmlFormatter
+ges_base_xml_formatter_get_type
+<SUBSECTION Standard>
+GESBaseXmlFormatterPrivate
+GES_BASE_XML_FORMATTER
+GES_BASE_XML_FORMATTER_CLASS
+GES_IS_BASE_XML_FORMATTER
+GES_IS_BASE_XML_FORMATTER_CLASS
+GES_BASE_XML_FORMATTER_GET_CLASS
+GES_TYPE_BASE_XML_FORMATTER
+</SECTION>
+
+<SECTION>
+<FILE>gesxmlformatter</FILE>
+<TITLE>GESXmlFormatter</TITLE>
+ges_xml_formatter_get_type
+<SUBSECTION Standard>
+GESXmlFormatterPrivate
+GES_XML_FORMATTER
+GES_TYPE_XML_FORMATTER
+GES_XML_FORMATTER_CLASS
+GES_XML_FORMATTER_GET_CLASS
+GES_IS_XML_FORMATTER
+GES_IS_XML_FORMATTER_CLASS
+</SECTION>
--- /dev/null
+#include <gst/gst.h>
+#include <ges/ges.h>
+
+ges_formatter_get_type
+%ges_text_halign_get_type
+%ges_text_valign_get_type
+ges_timeline_get_type
+ges_layer_get_type
+ges_clip_get_type
+ges_operation_clip_get_type
+ges_overlay_clip_get_type
+ges_pipeline_get_type
+ges_source_clip_get_type
+ges_test_clip_get_type
+ges_base_transition_clip_get_type
+ges_transition_clip_get_type
+ges_base_effect_clip_get_type
+ges_effect_clip_get_type
+ges_uri_clip_get_type
+ges_text_overlay_clip_get_type
+ges_title_clip_get_type
+ges_audio_test_source_get_type
+ges_audio_transition_get_type
+ges_video_uri_source_get_type
+ges_audio_uri_source_get_type
+ges_track_get_type
+ges_image_source_get_type
+ges_multi_file_source_get_type
+ges_track_element_get_type
+ges_base_effect_get_type
+ges_effect_get_type
+ges_operation_get_type
+ges_source_get_type
+ges_text_overlay_get_type
+ges_title_source_get_type
+ges_transition_get_type
+%ges_track_type_get_type
+ges_video_test_source_get_type
+ges_video_transition_get_type
+ges_project_get_type
+%ges_video_test_pattern_get_type
+%ges_video_standard_transition_type_get_type
+ges_meta_container_get_type
--- /dev/null
+types = configure_file(input : 'ges.types',
+ output : 'ges.types',
+ copy: true)
+
+doc_deps_names = ['glib-2.0',
+ 'gstreamer-@0@'.format(apiversion),
+ 'gstreamer-plugins-base-@0@'.format(apiversion)]
+
+doc_deps = []
+foreach doc_dep : doc_deps_names
+ # TODO: Use get_pkgconfig_variable()
+ runcmd = run_command('pkg-config', '--variable=prefix', doc_dep)
+ if runcmd.returncode() == 0
+ tmp = '--extra-dir=' + runcmd.stdout().strip() + '/share/gtk-doc/html/'
+ tmp.strip()
+ doc_deps = doc_deps + [tmp]
+ endif
+endforeach
+
+gnome.gtkdoc('ges',
+ main_sgml : 'ges-docs.sgml',
+ src_dir : '@0@/../../ges'.format(meson.current_source_dir()),
+ scan_args : ['--deprecated-guards=GST_DISABLE_DEPRECATED',
+ '--ignore-decorators=GES_API',
+ '--ignore-headers=gesmarshal.h ges-internal.h ges-auto-transition.h ges-structured-interface.h ges-structure-parser.h ges-smart-video-mixer.h gstframepositioner.h'
+ ],
+ scanobjs_args : ['--type-init-func="gst_init(NULL,NULL)"'],
+ gobject_typesfile : types,
+ dependencies : [ges_dep],
+ fixxref_args: doc_deps + ['--html-dir=' + get_option('prefix') + '/share/gtk-doc/html/'],
+ content_files : ['architecture.xml', 'ges-sections.txt', version_entities],
+ html_assets : 'layer_track_overview.png',
+ install : true,
+ install_dir : 'gstreamer-editing-services',
+)
--- /dev/null
+docconf = configuration_data()
+
+docconf.set('GST_API_VERSION', apiversion)
+docconf.set('VERSION', gst_version)
+docconf.set('PLUGINDIR', '@0@/lib/gstreamer-1.0'.format(get_option('prefix')))
+
+version_entities = configure_file(input : 'version.entities.in',
+ output : 'version.entities',
+ configuration : docconf)
+
+subdir('libs')
--- /dev/null
+GStreamer Editing Services
+--------------------------
+
+ This is a list of features and goals for the GStreamer Editing
+ Services.
+
+ Some features are already implemented, and some others not. When the
+ status is not specified, this means it is still not implemented but
+ might be investigated.
+
+
+FUNDAMENTAL GOALS:
+
+ 1) API must be easy to use for simple use-cases. Use and abuse
+ convenience methods.
+ 2) API must allow as many use-cases as possible, not just the simple
+ ones.
+
+
+FEATURES
+
+Index of features:
+
+ * Project file load/save support (GESFormatter)
+ * Grouping/Linking of Multiple TrackObjects
+ * Selection support (Extension from Grouping/Linking)
+ * Effects support
+ * Source Material object
+ * Proxy support
+ * Editing modes (Ripple/Roll/Slip/Slide)
+ * Coherent handling of Content in different formats
+ * Video compositing and audio mixing
+ * Handling of alpha video (i.e. transparency)
+ * Faster/Tigher interaction with GNonLin elements
+ * Media Asset Management integration
+ * Templates
+ * Plugin system
+
+
+
+* Project file load/save support (GESFormatter)
+
+ Status:
+ Implemented, requires API addition for all use-cases.
+
+ Problems:
+ Timelines can be stored in many different formats, we need to
+ ensure it is as easy/trivial as possible for users to load/save
+ those timelines.
+ Some timeline formats might have format-specific
+ sources/objects/effects which need to be handled in certain ways
+ and therefore provide their own classes.
+
+ The object that can save/load GESTimeline are Formatters.
+
+ Formatters can offer support for load-only/save-only formats.
+
+ There must be a list of well-known GES classes that all formatters
+ must be able to cope with. If a subclass of one of those classes is
+ present in a timeline, the formatter will do its best to store a
+ compatible information.
+
+ A Formatter can ask a pre-render of classes that it doesn't
+ understand (See Proxy section).
+
+ Formatters can provide subclasses of well-known GES classes when
+ filling in the timeline to offer format-specific features.
+
+
+
+* Grouping/Linking of Multiple TrackObjects
+
+ Status:
+ Implemented, but doesn't have public API for controlling the
+ tracked objects or creating groups from TimelineObject(s)
+
+ Problems:
+ In order to make the usage of timelines at the Layer level as easy
+ as possible, we must be able to group any TrackObject together as
+ one TimelineObject.
+
+ The base GESTimelineObject keeps a reference to all the
+ GESTrackObjects it is controlling. It contains a mapping of the
+ position of those track objects relatively to the timeline objects.
+
+ TrackObjects will move and be modified synchronously with the
+ TimelineObject, and vice-versa.
+
+ TrackObjects can be 'unlocked' from the changes of its controlling
+ TimelineObject. In this case, it will not move and be modified
+ synchronously with the TimelineObject.
+
+
+
+* Selection support (Extension from Grouping/Linking)
+
+ Problems:
+ In order to make user-interface faster to write, we must have a way
+ to create selections of user-selected TimelineObject(s) or
+ TrackObject(s) to move them together.
+
+ This should be able to do by creating a non-visible (maybe not even
+ inserted in the layer?) TimelineObject.
+
+
+
+* Effects support
+
+ Status:
+ Partially Implemented, requires API addition for all use-cases.
+
+ Problems:
+ In order for users to apply multimedia effects in their timelines,
+ we need an API to search, add and control those effects.
+
+ We must be able to provide a list of effects available on the system
+ at runtime.
+ We must be able to configure effects through an API in GES without
+ having to access the GstElements properties directly.
+
+ We should also expose the GstElements contained in an effect so it
+ is possible for people to control their properties as they wish.
+
+ We must be able to implement and handle complex effects directly in
+ GES.
+
+ We must be able to configure effects through time -> Keyframes
+ without duplicating code from GStreamer.
+
+
+
+* Source Material object
+
+ Problems:
+ Several TimelineSource for a same uri actually share a lot
+ in common. That information will mostly come from GstDiscoverer,
+ but could also contain extra information provided by 3rd party
+ modules.
+
+ The information regarding the various streams (and obtained through
+ optionally running GstDiscoverer) is not stored and has to be
+ re-analyzed else where.
+
+ Definition:
+ Material: n, The substance or substances out of which a thing is or
+ can be made.
+
+ In order to avoid duplicating that information in every single
+ TimelineSource, a 'Material' object needs to be made available.
+
+ A Material object contains all the information which is independent
+ of the usage of that material in a timeline.
+
+ A Material contains the list of 'streams' that can be provided with
+ as much information as possible (ex: contains audio and video
+ streams with full caps information, or better yet the output of
+ GstDiscoverer).
+
+ A Material contains the various Metadata (author, title, origin,
+ copyright ,....).
+
+ A Material object can specify the TimelineSource class to use in a
+ Layer.
+
+
+
+* Proxy support
+
+ Problems:
+ A certain content might be impossible to edit on a certain setup
+ due to many reasons (too complex to decode in realtime, not in
+ digital format, not available locally, ...).
+
+ In order to be able to store/export timelines to some formats, one
+ might need to have to create a pre-render of some items of the
+ timeline, while retaining as much information as possible.
+
+ Content here is not limited to single materials, it could very well
+ be a complex combination of materials/effects like a timeline or a
+ collection of images.
+
+ To solve this problem, we need a notion of ProxyMaterial.
+
+ It is a subclass of Material and as such provides all the same
+ features as Material.
+
+ It should be made easy to create one from an existing TimelineSource
+ (and it's associated Material(s)), with specifiable rendering
+ settings and output location.
+
+ The user should have the possibility to switch from Proxy materials
+ to original (in order to use the lower
+ resolution/quality/... version for the editing phase and the
+ original material for final rendering phase).
+
+ Requires:
+ GESMaterial
+
+
+
+* Editing modes (Ripple/Roll/Slip/Slide)
+
+ Status:
+ Not implemented.
+
+ Problems:
+ Most editing relies on heavy usage of 4 editing tools which editors
+ will require. Ripple/Roll happen on edit points (between two clips)
+ and Slip/Slide happen on a clip.
+
+ The Ripple tool allows you to modify the beginning/end of a clip
+ and move the neighbour accordingly. This will change the overall
+ timeline duration.
+
+ The Roll tool allows you to modify the position of an editing point
+ between two clips without modifying the inpoint of the first clip
+ nor the out-point of the second clip. This will not change the
+ overall timeline duration.
+
+ The Slip tool allows you to modify the in-point of a clip without
+ modifying it's duration or position in the timeline.
+
+ The Slide tool allows you to modify the position of a clip in a
+ timeline without modifying it's duration or it's in-point, but will
+ modify the out-point of the previous clip and in-point of the
+ following clip so as not to modify the overall timeline duration.
+
+ These tools can be used both on TimelineObjects and on
+ TrackObjects, we need to make sure that changes are propagated
+ properly.
+
+
+
+* Coherent handling of Content in different formats
+
+ Problems:
+ When mixing content in different format (Aspect-Ratio, Size, color
+ depth, number of audio channels, ...), decisions need to be made on
+ whether to conform the material to a common format or not, and on
+ how to conform that material.
+
+ Conforming the material here means bringing it to a common format.
+
+ All the information regarding the contents we are handling are
+ stored in the various GESMaterial. The target format is also known
+ through the caps of the various GESTracks involved. The Material and
+ track output caps will allow us to make decisions on what course of
+ action to take.
+
+ By default content should be conformed to a good balance of speed
+ and avoid loss of information.
+ Ex: If mixing a 4:3 video and a 16:9 video with a target track
+ aspect ratio of 4:3, we will make the width of the two videos
+ be equal without distorting their respective aspect-ratios.
+
+ Requires:
+ GESMaterial
+
+ See also:
+ Video compositing and audio mixing
+
+
+
+* Video compositing and audio mixing
+
+ Status:
+ Not implemented. The bare minimum to implement are the static
+ absolute property handling. Relative/variable properties and group
+ handling can be done once we know how to handle object grouping.
+
+ Problems:
+ Editing requires not only a linear combination of cuts and
+ sequences, but also mixing various content/effect at the same
+ time.
+
+ Audio and Video compositing/mixing requires having a set of base
+ properties for all sources that indicate their positioning in the
+ final composition.
+
+ Audio properties
+ * Volume
+ * Panning (or more generally positioning and up-/down-mixing for
+ multi-channel).
+
+ Video properties
+ * Z-layer (implicit through priority property)
+ * X,Y position
+ * Vertical and Horizontal scaling
+ * Global Alpha (see note below about alpha).
+
+ A big problem with compositing/mixing is handling positioning that
+ could change due to different input/output format AND avoiding any
+ quality loss.
+
+ Example 1 (video position and scale/aspect-ratio changes):
+ A user puts a 32x24 logo video at position 10,10 on a 1280x720
+ video. Later on the user decides to render the timeline to a
+ different resolution (like 1920x1080) or aspect ratio (4:3 instead
+ of 16:9).
+ The overlayed logo should stay at the same relative position
+ regardless of the output format.
+
+ Example 2 (video scaling):
+ A user decides to overlay a video logo which is originally a
+ 320x240 video by scaling it down to 32x24 on top of a 1280x720
+ video. Later on the user decides to render a 1920x1080 version of
+ the timeline.
+ The resulting rendered 1920x1080 video shall have the overlay
+ video located at the exact relative position and using a 64x48
+ downscale of the original overlay video (i.e. avoiding a
+ 640x480=>32x24=>64x48 double-scaling).
+
+ Example 3 (audio volume):
+ A user adjusts the commentary audio track and the soundtrack audio
+ track based on the volume of the various videos playing. Later on
+ the user wants to adjust the overall audio volume in order for the
+ final output to conform to a target RMS/peak volume.
+ The resulting relative volumes of each track should be the same
+ WITHOUT any extra loss of audio quality (i.e. avoiding a
+ downscale/upscale lossy volume conversion cycle).
+
+ Example 4 (audio positioning):
+ A user adjusts the relative panning/positioning of the commentary,
+ soundtrack and sequence for a 5.1 mixing. Later on he decides to
+ make a 7.1 and a stereo rendering.
+ The resulting relative positioning should be kept as much as
+ possible (left/right downmix and re-positioning for extra 2
+ channels in the case of 7.1 upmixing) WITHOUT any extra loss in
+ quality.
+
+
+ Create a new 'CompositingProperties' object for audio and video
+ which is an extensible set of properties for media-specific
+ positioning. This contains the properties mentionned above.
+
+ Add the CompositingProperties object to the base GESTrackObject
+ which points to the audio or video CompositingProperties
+ object (depending on what format that object is handling).
+
+ Provide convenience functions to retrieve and set the audio or video
+ compositing properties of a GESTrackObject. Do the same for the
+ GESTimelineObject, which proxies it to the relevant GESTrackObject.
+
+ Create a new GESTrack{Audio|Video}Compositing GstElement which will
+ be put in each track as a priority 0 expandable NleOperation.
+ That object will be able to figure out which
+ mixing/scaling/conversion elements to use at any given time by
+ inspecting:
+ * The various GESTrackObject Compositing Properties
+ * The various GESTrackObject GESMaterial stream properties
+ * The GESTrack target output GstCaps
+
+ The properties values could be both set/stored as 'relative' values
+ and as 'absolute' values in order to handle any input/output formats
+ or setting.
+
+ Objects that are linked/grouped with others have their properties
+ move in sync with each other. (Ex: If an overlay logo is locked to a
+ video, it will scale/move/be-transparent in sync with the video on
+ which it is overlayed).
+
+ Objects that are not linked/grouped to other objects have their
+ properties move in sync with the target format. If the target format
+ changes, all object positioning will change relatively to that
+ format.
+
+ Requires:
+ GESMaterial
+
+ See also:
+ Coherent handling of Content in different formats
+
+
+
+* Handling of alpha video (i.e. transparency)
+
+ Problem:
+ Some streams will contain partial transparency (overlay
+ logos/videos, bluescreen, ...).
+
+ Those streams need to be handle-able by the user just like
+ non-alpha videos without losing the transparency regions (i.e. it
+ should be properly blended with the underlying regions).
+
+
+
+* Faster/Tighter interaction with GNonLin elements
+
+ Problems:
+ A lot of properties/concepts need to be duplicated at the GES level
+ since the only way to communicate with the GNonLin elements is
+ through publically available APIs (GObject and GStreamer APIs).
+
+ The GESTrackObject for example has to duplicate exactly the same
+ properties as NleObject for no reason.
+
+ Other properties are also expensive to re-compute and also become
+ non-MT-safe (like computing the exact 'tree' of objects at a
+ certain position in a NleComposition).
+
+ Merge the GES and GNonLin modules together into one single module,
+ and keep the same previous API for both for backward compatibility.
+
+ Add additional APIs to GNonLin which GES can use.
+
+
+
+* Media Asset Management integration
+
+ (Track, Search, Browse, Push content) TBD
+
+
+
+* Templates
+
+ Problem:
+ In order to create as quickly as possible professional-looking
+ timelines, we need to provide a way to create 'templates' which
+ users can select and have an automatic timeline 'look' used.
+
+ This will allow users to be able to quickly add their clips, set
+ titles and have a timeline with a professional look. This is
+ similar to the same feature that iMovie offers both on desktop and
+ iOS.
+
+
+
+* Plugin system
+
+ Problem:
+ All of GES classes are made in such a way that creating new
+ sources, effects, templates, formatters, etc... can be easily added
+ either to the GES codebase itself or to applications.
+
+ But in order to provide more features without depending on GES
+ releases, limit those features to a single application, and in
+ order to provide 'closed'/3rd party features, we need to implement
+ a plugin system so one can add new features.
+
+ Use a registry system similar to GStreamer.
--- /dev/null
+Lifecycle of a Timeline/Track Object
+
+* Adding a TimelineObject to a Layer
+
+(tlobj:timelineobject, trobj:trackobject)
+
+ges_timeline_layer_add_object(layer, tlobj)
+ signal_emit "object-added", layer, tlobj
+ GESTimeline receives signal
+ for each TRACK {
+ ges_timeline_object_create_track_objects(tlobj, TRACK)
+ trobj = GESTimelineObject::create_track_objects
+ ges_track_add_object(TRACK, trobj)
+ ges_track_object_set_track(troj, TRACK)
+ nleobj = GESTrackObject::create_gnl_object
+ ges_timeline_object_fill_track_object(tlobj, trobj, nleobj)
+ GESTimelineObject::fill_track_object
+
--- /dev/null
+Mapping Timeline position to Track position
+-------------------------------------------
+
+TrackObject/TimelineObject basic properties (hereafter position):
+ start
+ duration
+ in-point
+ priority
+
+
+Use Cases:
+
+ A TimelineObject tracks one or many TrackObject(s).
+
+ When the TimelineObject position is modified we might need
+ to cascade those changes to the controlled TrackObject(s) if those
+ TrackObject(s) are 'locked' to the TimelineObject.
+
+ If we modify the positions of a TrackObject that TrackObject is
+ 'locked' to the TimelineObject, we need to ensure all the other
+ co-related TrackObject belong to the same TimelineObject are moved in
+ the same way.
+
+ A TrackObject can be temporarily 'unlocked' from its TimelineObject,
+ so as to move it independently, and then 'locked' back to it. This
+ can allow moves, like shifting audio trackobject in relation to the
+ video trackobject (to fix sync issues) and then 'lock' them back so
+ as to be able to move them as one entity thereafter.
+
+ When adding TimelineOverlay(s) or TimelineEffect(s) on a
+ TimelineObject, we need to ensure the TrackObject(s) that those extra
+ effects will create can be added with specific priority offsets, in
+ such a way that they always end up "on top" of the TimelineObject's
+ existing tracked TrackObject(s).
+
+ When a controlled TrackObject is being moved when 'unlocked', we need
+ to make sure the duration/height of the TimelineObject is updated
+ accordingly. Ex : moving a TrackObject down by one priority should
+ increase the TimelineObject "heigh" property by 1.
+
+ A TimelineObject might want to have a tighter control over which
+ Track(s) each of the TrackObjects it is controlling are going. This
+ is more obvious in the case of timeline with multiple Tracks of the
+ same kind, or if a TimelineObject can produce multiple TrackObjects
+ of the same media type (ex: file with multiple audio tracks).
+
+
+Main Problem:
+
+ There needs to be a mapping between the TimelineObject basic
+ properties and its controlled TrackObject(s) position.
+
+Design:
+
+ The TimelineObject listen to TrackObject 'notify' signals
+
+ When it sets a property on its trackobjects, it 'ignores' all
+ notifications that happen while setting them.
+
+ Setting a property on a TrackObject will see its property changed,
+ and then it emits a notify with the modified property.
+
+ TrackObject::locked
+ ges_track_object_set_locked()
+ ges_track_object_is_locked()
+
+ Mapping {
+ GESTrackObject *object;
+ gint64 start_offset;
+ gint64 duration_offset;
+ gint64 inpoint_offset;
+ gint32 priority_offset;
+ /* Track ??? */
+ }
+
+ P : property
+ V : value
+
+ TimelineObject set_property(P,V)
+ ignore_notifies = TRUE
+ parent.P = V
+ foreach child in trackobjects:
+ if child.is_locked():
+ child.set_property(P, parent.P + mapping(child).P_offset)
+ ignore_notifies = FALSE
+
+ TimelineObject child 'notify::P' handler:
+ if ignore_notifies:
+ return
+ if not child.is_locked():
+ mapping(child).P_offset = timeline.P - child.P
+ else:
+ TimelineObject.set_property(P, child value + mapping(child).P_offset)
+
+ TrackObject set_property(P, V)
+ update the property locally (P = V)
+ emit 'notify::P' signal
+
+ TODO : When do we resync the parent values to have minimal offsets ?
--- /dev/null
+<!DOCTYPE html>
+<html>
+<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" >
+<title> Rework the GStreamer Editing Services class hierarchy </title>
+<xmp theme="cerulean" style="display:none;">
+
+Reasoning:
+----------
+
+All the time (position) related concepts are shared between GESTimelineObject and GESTrackObject
+and currently are repeated at the 2 levels.
+Moreover, if we want to add the concept of Group we end up with something quite similare to the current
+GESTimelineObject but that contains GESTimelineObject-s instead of GESTrackObject-s so we could share
+those informations creating a new class aiming at containing the objects that have that
+notion of timing.
+
+At the same time, we want to clarify namings. First we should remove the word Object in class names,
+we have been told various times that it sounds just "wrong" for people as Objects are instances and there
+we are talking about Classes.
+
+Class Hierarchy:
+-------------
+
+<pre><code>
+<table>
+<tr>
+ <td>
+
+Before:
+-------
+
+GESTimelineObject
+ GESTimelineSource
+ GESCustomTimelineSource
+ GESTimelineTestSource
+ GESTimelineFileSource
+ GESTimelineTitleSource
+ GESTimelineOperation
+ GESTimelineOverlay
+ GESTimelineTextOverlay
+ GESTimelineTransition
+ GESTimelineTransition
+ GESTimelineEffect
+ GESTimelineParseLaunchEffect
+GESTimelineLayer
+ GESSimpleTimelineLayer
+GESTrackObject
+ GESTrackSource
+ GESTrackAudioTestSource
+ GESTrackFileSource
+ GESTrackImageSource
+ GESTrackTitleSource
+ GESTrackVideoTestSource
+ GESTrackOperation
+ GESTrackTransition
+ GESTrackAudioTransition
+ GESTrackVideoTransition
+ GESTrackEffect
+ GESTrackParseLaunchEffect
+ GESTrackTextOverlay
+ </td>
+ <td>
+
+After:
+-------
+
+GESTimelineElement
+ GESContainer
+ GESClip
+ GESSourceClip
+ GESCustomSourceClip
+ GESTestClip
+ GESUriClip
+ GESTitleClip
+ GESOperationClip
+ GESOverlayClip
+ GESTextOverlayClip
+ GESBaseTransitionClip
+ GESTransitionClip
+ GESBaseEffectClip
+ GESEffectClip
+ GESClipGroup
+ GESTrackElement
+ GESSource
+ GESAudioTestSource
+ GESUriSource
+ GESImageSource
+ GESTitleSource
+ GESVideoTestSource
+ GESOperation
+ GESTransition
+ GESAudioTransition
+ GESVideoTransition
+ GESBaseEffect
+ GESEffect
+ GESTextOverlay
+ </td>
+ </tr>
+</table>
+</code></pre>
+</xmp>
+<script src="http://strapdownjs.com/v/0.1/strapdown.js"></script>
+</html>
--- /dev/null
+SCENARIOS
+
+* Adding a TimelineObject to a TimelineLayer
+--------------------------------------------
+
+ * Create a Timeline
+
+ * Create a Track
+
+ * Add the track to the Timeline (==> ges_timeline_add_track (track);)
+ The Timeline adds the Track to itself (i.e. gst_bin_add())
+ 'track-added' is emitted
+
+ * Create a TimelineLayer
+
+ * Add the TimelineLayer to the Timeline (ges_timeline_add_layer (layer);)
+ The Timeline takes a reference on the layer and stores it
+ The Timeline tells the TimelineLayer that it now belongs to the given Timeline (weak reference)
+ ==> ges_timeline_layer_set_timeline ();
+ 'layer-added' is emitted
+
+ * Create a TimelineObject
+
+ * Add the TimelineObject to the TimelineLayer (ges_timeline_layer_add_object (object);)
+ The TimelineLayer takes a reference on the TimelineObject and stores it
+ The timelineLayer tells the TimelineObject that it now belongs to the given layer (weak reference)
+ ==> ges_timeline_object_set_layer ();
+ 'object-added' is emitted by TimelineLayer
+ The Timeline requests a new TrackObject from the new TimelineObject for each Track
+ ==> ges_timeline_object_create_track_object (track)
+ The TimelineObject calls the 'create_track_object' virtual method with the given track
+ Example implementation
+ Create a GESTrackSource
+ (GESTimelineObject is a constructor property of track objects)
+ A GESTrackObject CAN NOT EXIST WITHOUT A GESTimelineObject !
+ The Timeline adds the newly created TrackObject to the Track
+ ==> ges_track_add_object (track, trackobject);
+ Set the track on the TrackObject
+ ==> ges_track_object_set_track (track)
+ The GESTrackObject can create the NleObject
+
+
+
+Methods
+-------
+
+[ GESTimeline ]
+
+* gboolean
+ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track);
+
+ * The Timeline adds the track to itself (gst_bin_add ()) # reference implicitely taken
+ * The Timeline adds the track to its list of tracked tracks
+ * The Timeline sets the Timeline on the track
+ => ges_track_set_timeline (GESTrack * track, GESTimeline * timeline);
+ Just sets the timeline field of the track.
+ * emits 'track-added'
+
+
+* gboolean
+ ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer);
+
+ * The Timeline takes a reference on the layer and stores it
+ * The Timeline tells the Layer that it now belongs to the given Timeline
+ => ges_timeline_layer_set_timeline (GESTimelineLayer * layer, GESTimeline * timeline);
+ Just sets the timeline field of the layer.
+ * Connect to the layer's 'object-added' signal
+ * emits 'layer-added'
+
+
+* GESTimeline's
+ callback for GESTimelineLayer::object-added (GESTimelineLayer * layer, GESTimelineObject * object);
+
+ * For each GESTrack in the Timeline:
+ * The timeline requests a new TrackObject for the new TimelineObject for each Track
+ trackobj = ges_timeline_object_create_track_object (timelineobj, track);
+ * The timeline adds the newly created TrackObject to the track
+ ges_track_add_object (track, trackobj);
+
+[ GESTimelineLayer ]
+
+* gboolean
+ ges_timeline_layer_add_object (GESTimelineLayer * layer, GESTimelineObject * object);
+
+ * The TimelineLayer takes a reference on the TimelineObject and stores it
+ * The TimelineLayer tells the TimelineObject it now belongs to the given Layer
+ => ges_timeline_object_set_layer (GESTimelineObject * object, GESTimelineLayer * layer);
+ Just sets the layer field of the timeline object.
+ * emits 'object-added'
+
+
+[ GESTimelineObject ]
+
+* GESTrackObject *
+ ges_timeline_object_create_track_object (GESTimelineObject * object, GESTrack * track);
+
+ * The TimelineObject calls the 'create_track_object' virtual method
+ * The TimelineObject sets the TimelineObject on the new TrackObject
+ => ges_track_object_set_timeline_object (track_object, timeline_object);
+ Just sets the timeline-object field of the TrackObject
+ * Return the newly created GESTrackObject
+
+
+* Virtual-method for GESTimelineObject::create_track_object (GESTimelineObject * object, GESTrack * track);
+
+ * Create a track object of the proper type
+ Ex (for a source) :
+ return ges_track_source_new();
+
+* gboolean
+ ges_timeline_object_fill_track_object (GESTimelineObject *tlo, GESTrackObject *tro, GstElement *nleobj);
+
+ * up to the implementation :)
+
+
+[ GESTrack ]
+
+* gboolean
+ ges_track_add_object (GESTrack * track, GESTrackObject * object);
+
+ * Set the track on the track_object
+ ges_track_object_set_track (object, track);
+ * Add the NleObject of the TrackObject to the composition
+ gst_bin_add (track->composition, object->nleobject);
+
+
+[ GESTrackObject ]
+
+* gboolean
+ ges_track_object_set_track (GESTrackObject * object, GESTrack * track);
+
+ * Set the track field of the TrackObject
+ * if no NleObject is available yet:
+ * Call the 'create_gnl_object' virtual method
+
+
+* Virtual-method for GESTrackObject::create_gnl_object
+
+ * Create a NleObject of the proper type
+ Ex : nleobject = gst_element_factory_make("nlesource", NULL);
+ * Ask the TimelineObject to fill in the NleObject
+ => ges_timeline_object_fill_track_object (GESTimelineObject * tlo, GESTrackObject * tro, GstElement * nleobj);
+
--- /dev/null
+<!ENTITY GST_API_VERSION "@GST_API_VERSION@">
+<!ENTITY GES_VERSION "@VERSION@">
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1052.3622"
+ height="744.09448"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="working-diagrams.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3227"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3209"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3206"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4142136"
+ inkscape:cx="358.45699"
+ inkscape:cy="275.3079"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:snap-global="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2383"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ style="opacity:0.5;fill:#aaeeff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2385"
+ width="339.37469"
+ height="310.25867"
+ x="700.35718"
+ y="493.7818"
+ rx="9.689147"
+ ry="12.085826"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <text
+ xml:space="preserve"
+ style="font-size:18px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="710"
+ y="514.09448"
+ id="text2391"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575"><tspan
+ sodipodi:role="line"
+ id="tspan2393"
+ x="710"
+ y="514.09448">GESTimeline</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot3165"
+ style="font-size:18px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"><flowRegion
+ id="flowRegion3167"><rect
+ id="rect3169"
+ width="288"
+ height="208"
+ x="50"
+ y="129.36218" /></flowRegion><flowPara
+ id="flowPara3171" /></flowRoot> <g
+ id="g5556"
+ transform="translate(690,9.5885)"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575"
+ style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none">
+ <rect
+ y="532.77368"
+ x="29.296322"
+ height="49.58847"
+ width="300.70367"
+ id="rect3179"
+ style="opacity:1;fill:#aa87de;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <text
+ id="text3181"
+ y="564.86188"
+ x="139.24094"
+ style="font-size:28px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="564.86188"
+ x="139.24094"
+ id="tspan3183"
+ sodipodi:role="line">Layer</tspan></text>
+ </g>
+ <rect
+ style="fill:#8dd35f;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3185"
+ width="299.99881"
+ height="49.589645"
+ x="719.99939"
+ y="652.36157"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <text
+ xml:space="preserve"
+ style="font-size:28px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="833.7522"
+ y="687.59491"
+ id="text3187"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575"><tspan
+ sodipodi:role="line"
+ id="tspan3189"
+ x="833.7522"
+ y="687.59491">Track</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1019.8707,676.85631 L 1079.4001,676.85631"
+ id="path3201"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <rect
+ style="fill:#8dd35f;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3195"
+ width="299.99881"
+ height="49.589645"
+ x="719.99939"
+ y="732.36157"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <text
+ xml:space="preserve"
+ style="font-size:28px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="833.7522"
+ y="767.59491"
+ id="text3197"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575"><tspan
+ sodipodi:role="line"
+ id="tspan3199"
+ x="833.7522"
+ y="767.59491">Track</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:18px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="-566.94543"
+ y="659.29633"
+ id="text5573"
+ transform="matrix(0,-1,1,0,0,0)"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575"><tspan
+ sodipodi:role="line"
+ id="tspan5575"
+ x="-566.94543"
+ y="659.29633">User</tspan><tspan
+ sodipodi:role="line"
+ x="-566.94543"
+ y="681.79633"
+ id="tspan5577">visible</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:18px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="-710.63367"
+ y="670"
+ id="text5579"
+ transform="matrix(0,-1,1,0,0,0)"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575"><tspan
+ sodipodi:role="line"
+ x="-710.63367"
+ y="670"
+ id="tspan5583">Medias</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1019.6067,757.52077 L 1079.1361,757.52077"
+ id="path5615"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <path
+ style="opacity:0.5;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.91023302;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.73069881, 2.91023294;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 700,619.09448 L 1039.0223,619.09448"
+ id="path5619"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <rect
+ style="fill:#aa87de;fill-opacity:1;stroke:#000000;stroke-width:1.48319197;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2507"
+ width="489.8678"
+ height="119.87508"
+ x="40.067669"
+ y="83.888557" />
+ <rect
+ style="fill:#8dd35f;fill-opacity:1;stroke:#000000;stroke-width:1.35438466;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3304"
+ width="490.13684"
+ height="99.903282"
+ x="39.870644"
+ y="434.19138" />
+ <rect
+ style="fill:#8dd35f;fill-opacity:1;stroke:#000000;stroke-width:1.65954936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3306"
+ width="489.94894"
+ height="150.05229"
+ x="40.026497"
+ y="254.01666" />
+ <rect
+ style="fill:#00c4ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3308"
+ width="160"
+ height="80"
+ x="60.08099"
+ y="113.9472"
+ rx="10" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.11796093;stroke-linecap:butt;stroke-linejoin:miter;marker-start:none;marker-end:url(#Arrow2Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 40.384902,60.094197 L 527.99322,60.094197"
+ id="path3312" />
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="40"
+ y="55.094482"
+ id="text4368"><tspan
+ sodipodi:role="line"
+ id="tspan4370"
+ x="40"
+ y="55.094482">Time</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.97574663;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 529.9953,484.17782 L 588.00008,484.17782"
+ id="path4372"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ <rect
+ style="fill:#00c4ff;fill-opacity:1;stroke:#000000;stroke-width:1.8037442;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4374"
+ width="129.82126"
+ height="80.196259"
+ x="220.0939"
+ y="113.84907"
+ rx="10" />
+ <rect
+ style="fill:#00c4ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4376"
+ width="160"
+ height="80"
+ x="349.95801"
+ y="113.99493"
+ rx="10" />
+ <rect
+ style="fill:#aaa800;fill-opacity:1;stroke:#000000;stroke-width:1.73008239;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4378"
+ width="160.25104"
+ height="59.76992"
+ x="59.955471"
+ y="332.76434"
+ rx="10" />
+ <rect
+ style="fill:#aaa800;fill-opacity:1;stroke:#000000;stroke-width:1.55953789;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4380"
+ width="129.84395"
+ height="59.940464"
+ x="220.08257"
+ y="332.67908"
+ rx="10" />
+ <rect
+ style="fill:#aaa800;fill-opacity:1;stroke:#000000;stroke-width:1.55953789;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4382"
+ width="129.84395"
+ height="59.940464"
+ x="220.08257"
+ y="272.64929"
+ rx="10" />
+ <rect
+ style="fill:#aaa800;fill-opacity:1;stroke:#000000;stroke-width:1.72976816;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4384"
+ width="160.19199"
+ height="59.770233"
+ x="349.862"
+ y="332.81192"
+ rx="10" />
+ <rect
+ style="fill:#aaa800;fill-opacity:1;stroke:#000000;stroke-width:1.73008239;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4386"
+ width="160.25104"
+ height="59.76992"
+ x="59.955471"
+ y="465.89206"
+ rx="10" />
+ <rect
+ style="fill:#aaa800;fill-opacity:1;stroke:#000000;stroke-width:1.55953789;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4388"
+ width="129.84395"
+ height="59.940464"
+ x="220.08257"
+ y="465.80679"
+ rx="10" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ x="140.53998"
+ y="159.21556"
+ id="text4392"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4394"
+ x="140.53998"
+ y="159.21556"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">FileSource A</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ x="429.58691"
+ y="145.30821"
+ id="text4396"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4398"
+ x="429.58691"
+ y="145.30821"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">FileSource C</tspan><tspan
+ sodipodi:role="line"
+ x="429.58691"
+ y="170.30821"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ id="tspan4410">(muted)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="283"
+ y="158.09448"
+ id="text4400"><tspan
+ sodipodi:role="line"
+ id="tspan4402"></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ x="285.38052"
+ y="144.69408"
+ id="text4404"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4406"
+ x="285.38052"
+ y="144.69408"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">FileSource B</tspan><tspan
+ sodipodi:role="line"
+ x="285.38052"
+ y="169.69408"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ id="tspan4408">(+fx)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="42.373047"
+ y="274.09448"
+ id="text4412"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4414"
+ x="42.373047"
+ y="274.09448"
+ style="font-size:20px;font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Oblique">Video Track</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="45"
+ y="454.09448"
+ id="text4416"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4418"
+ x="45"
+ y="454.09448"
+ style="font-size:20px;font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Oblique">Audio Track</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="43.398438"
+ y="102.42651"
+ id="text4420"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4422"
+ x="43.398438"
+ y="102.42651"
+ style="font-size:20px;font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Oblique">Layer</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Bold Italic"
+ x="139.73431"
+ y="370.0614"
+ id="text4424"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan4426"
+ x="139.73431"
+ y="370.0614"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">Source A</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Bold Italic"
+ x="139.73431"
+ y="503.18912"
+ id="text4428"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan4430"
+ x="139.73431"
+ y="503.18912"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">Source A</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Bold Italic"
+ x="284.57486"
+ y="358.20789"
+ id="text4432"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan4434"
+ x="284.57486"
+ y="358.20789"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">Source</tspan><tspan
+ sodipodi:role="line"
+ x="284.57486"
+ y="382.20789"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ id="tspan4450">B</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Bold Italic"
+ x="286.56216"
+ y="302.49377"
+ id="text4436"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan4438"
+ x="286.56216"
+ y="302.49377"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">BaseEffect</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Bold Italic"
+ x="428.78125"
+ y="370.09448"
+ id="text4440"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan4442"
+ x="428.78125"
+ y="370.09448"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">Source C</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Georgia;-inkscape-font-specification:Georgia Bold Italic"
+ x="284.57486"
+ y="491.3356"
+ id="text4444"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan4446"
+ x="284.57486"
+ y="491.3356"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic">Source</tspan><tspan
+ sodipodi:role="line"
+ x="284.57486"
+ y="515.33563"
+ style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:middle;font-family:Georgia;-inkscape-font-specification:Georgia Italic"
+ id="tspan4448">B</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.97574663;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 529.9953,329.07761 L 588.00008,329.07761"
+ id="path4456"
+ inkscape:export-filename="/home/bilboed/work/devel/gst-editing-services/docs/libs/layer_track_overview.png"
+ inkscape:export-xdpi="149.87575"
+ inkscape:export-ydpi="149.87575" />
+ </g>
+</svg>
--- /dev/null
+assets
+concatenate
+ges-ui
+play_timeline_with_one_clip
+test1
+test2
+test3
+test4
+simple1
+text_properties
+transition
+thumbnails
+overlays
+multifilesrc
+c/gessrc
+
--- /dev/null
+SUBDIRS = c
+
+AM_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_CFLAGS) $(GTK_CFLAGS)
+AM_LDFLAGS = -export-dynamic
+LDADD = $(top_builddir)/ges/libges-@GST_API_VERSION@.la $(GST_PBUTILS_LIBS) $(GST_LIBS) $(GTK_LIBS)
--- /dev/null
+if HAVE_GTK_X11
+graphical=ges-ui
+else
+graphical=
+endif
+
+noinst_PROGRAMS = \
+ concatenate \
+ gessrc \
+ simple1 \
+ test1 \
+ test2 \
+ test3 \
+ test4 \
+ transition \
+ thumbnails \
+ overlays \
+ text_properties \
+ assets \
+ multifilesrc \
+ play_timeline_with_one_clip \
+ $(graphical)
+
+ERROR_CFLAGS=
+
+AM_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_CFLAGS) $(GTK_CFLAGS)
+AM_LDFLAGS = -export-dynamic
+LDADD = $(top_builddir)/ges/libges-@GST_API_VERSION@.la $(GST_PBUTILS_LIBS) $(GST_LIBS) $(GTK_LIBS)
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2012 Volodymyr Rudyi<vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <ges/ges.h>
+#include <ges/ges-uri-asset.h>
+#include <gst/pbutils/encoding-profile.h>
+#include <gst/pbutils/gstdiscoverer.h>
+
+static void
+asset_loaded_cb (GObject * source, GAsyncResult * res, GMainLoop * mainloop)
+{
+ GESUriClipAsset *mfs =
+ GES_URI_CLIP_ASSET (ges_asset_request_finish (res, NULL));
+ GstDiscovererInfo *discoverer_info = NULL;
+ discoverer_info = ges_uri_clip_asset_get_info (mfs);
+
+ GST_DEBUG ("Result is %d", gst_discoverer_info_get_result (discoverer_info));
+ GST_DEBUG ("Info type is %s", G_OBJECT_TYPE_NAME (mfs));
+ GST_DEBUG ("Duration is %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ges_uri_clip_asset_get_duration (mfs)));
+
+ gst_object_unref (mfs);
+
+ g_main_loop_quit (mainloop);
+}
+
+int
+main (int argc, gchar ** argv)
+{
+ GMainLoop *mainloop;
+
+ if (argc != 2) {
+ return 1;
+ }
+ /* Initialize GStreamer (this will parse environment variables and commandline
+ * arguments. */
+ gst_init (NULL, NULL);
+
+ /* Initialize the GStreamer Editing Services */
+ ges_init ();
+
+ /* ... and we start a GMainLoop. GES **REQUIRES** a GMainLoop to be running in
+ * order to function properly ! */
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ ges_asset_request_async (GES_TYPE_URI_CLIP, argv[1], NULL,
+ (GAsyncReadyCallback) asset_loaded_cb, mainloop);
+
+ g_main_loop_run (mainloop);
+ g_main_loop_unref (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gio/gio.h>
+#include <ges/ges.h>
+#include <gst/pbutils/gstdiscoverer.h>
+#include <gst/pbutils/encoding-profile.h>
+
+static void
+bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop);
+
+static GstEncodingProfile *make_profile_from_info (GstDiscovererInfo * info);
+
+GESPipeline *pipeline = NULL;
+gchar *output_uri = NULL;
+guint assetsCount = 0;
+guint assetsLoaded = 0;
+
+static void
+asset_loaded_cb (GObject * source_object, GAsyncResult * res,
+ GMainLoop * mainloop)
+{
+ GError *error = NULL;
+
+ GESUriClipAsset *mfs =
+ GES_URI_CLIP_ASSET (ges_asset_request_finish (res, &error));
+
+ if (error) {
+ GST_WARNING ("error creating asseti %s", error->message);
+
+ return;
+ }
+
+ assetsLoaded++;
+ /*
+ * Check if we have loaded last asset and trigger concatenating
+ */
+ if (assetsLoaded == assetsCount) {
+ GstDiscovererInfo *info = ges_uri_clip_asset_get_info (mfs);
+ GstEncodingProfile *profile = make_profile_from_info (info);
+ ges_pipeline_set_render_settings (pipeline, output_uri, profile);
+ /* We want the pipeline to render (without any preview) */
+ if (!ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_SMART_RENDER)) {
+ g_main_loop_quit (mainloop);
+ return;
+ }
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ }
+
+ gst_object_unref (mfs);
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *mainloop = NULL;
+ GESTimeline *timeline;
+ GESLayer *layer = NULL;
+ GstBus *bus = NULL;
+ guint i;
+
+
+ if (argc < 3) {
+ g_print ("Usage: %s <output uri> <list of files>\n", argv[0]);
+ return -1;
+ }
+
+ gst_init (&argc, &argv);
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ layer = (GESLayer *) ges_layer_new ();
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+
+ output_uri = argv[1];
+ assetsCount = argc - 2;
+
+ for (i = 2; i < argc; i++) {
+ ges_asset_request_async (GES_TYPE_URI_CLIP, argv[i],
+ NULL, (GAsyncReadyCallback) asset_loaded_cb, mainloop);
+ }
+
+ /* In order to view our timeline, let's grab a convenience pipeline to put
+ * our timeline in. */
+ pipeline = ges_pipeline_new ();
+
+ /* Add the timeline to that pipeline */
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
+
+ g_main_loop_run (mainloop);
+
+ return 0;
+
+}
+
+static void
+bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ g_print ("ERROR\n");
+ g_main_loop_quit (mainloop);
+ break;
+ case GST_MESSAGE_EOS:
+ g_print ("Done\n");
+ g_main_loop_quit (mainloop);
+ break;
+ default:
+ break;
+ }
+}
+
+static GstEncodingProfile *
+make_profile_from_info (GstDiscovererInfo * info)
+{
+ GstEncodingContainerProfile *profile = NULL;
+ GstDiscovererStreamInfo *sinfo = gst_discoverer_info_get_stream_info (info);
+
+ /* Get the container format */
+ if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) {
+ GList *tmp, *substreams;
+
+ profile = gst_encoding_container_profile_new ((gchar *) "concatenate", NULL,
+ gst_discoverer_stream_info_get_caps (sinfo), NULL);
+
+ substreams =
+ gst_discoverer_container_info_get_streams ((GstDiscovererContainerInfo
+ *) sinfo);
+
+ /* For each on the formats add stream profiles */
+ for (tmp = substreams; tmp; tmp = tmp->next) {
+ GstDiscovererStreamInfo *stream = GST_DISCOVERER_STREAM_INFO (tmp->data);
+ GstEncodingProfile *sprof = NULL;
+
+ if (GST_IS_DISCOVERER_VIDEO_INFO (stream)) {
+ sprof = (GstEncodingProfile *)
+ gst_encoding_video_profile_new (gst_discoverer_stream_info_get_caps
+ (stream), NULL, NULL, 1);
+ } else if (GST_IS_DISCOVERER_AUDIO_INFO (stream)) {
+ sprof = (GstEncodingProfile *)
+ gst_encoding_audio_profile_new (gst_discoverer_stream_info_get_caps
+ (stream), NULL, NULL, 1);
+ } else {
+ GST_WARNING ("Unsupported streams");
+ }
+
+ if (sprof)
+ gst_encoding_container_profile_add_profile (profile, sprof);
+ }
+ if (substreams)
+ gst_discoverer_stream_info_list_free (substreams);
+ } else {
+ GST_ERROR ("No container format !!!");
+ }
+
+ if (sinfo)
+ gst_discoverer_stream_info_unref (sinfo);
+
+ return GST_ENCODING_PROFILE (profile);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <ges/ges.h>
+
+/* Application Data ********************************************************/
+
+/**
+ * Contains most of the application data so that signal handlers
+ * and other callbacks have easy access.
+ */
+
+typedef struct App
+{
+ /* back-end objects */
+ GESTimeline *timeline;
+ GESPipeline *pipeline;
+ GESLayer *layer;
+ GESTrack *audio_track;
+ GESTrack *video_track;
+ guint audio_tracks;
+ guint video_tracks;
+
+ /* application state */
+ gchar *pending_uri;
+ int n_objects;
+
+ int n_selected;
+ GList *selected_objects;
+ GType selected_type;
+ gboolean first_selected;
+ gboolean last_selected;
+
+ gboolean ignore_input;
+ GstState state;
+
+ GtkListStore *model;
+ GtkTreeSelection *selection;
+
+ /* widgets */
+ GtkWidget *main_window;
+ GtkWidget *add_effect_dlg;
+ GtkWidget *properties;
+ GtkWidget *filesource_properties;
+ GtkWidget *text_properties;
+ GtkWidget *generic_duration;
+ GtkWidget *background_properties;
+ GtkWidget *audio_effect_entry;
+ GtkWidget *video_effect_entry;
+
+ GtkHScale *duration;
+ GtkHScale *in_point;
+ GtkHScale *volume;
+
+ GtkAction *add_file;
+ GtkAction *add_effect;
+ GtkAction *add_test;
+ GtkAction *add_title;
+ GtkAction *add_transition;
+ GtkAction *delete;
+ GtkAction *play;
+ GtkAction *stop;
+ GtkAction *move_up;
+ GtkAction *move_down;
+ GtkToggleAction *audio_track_action;
+ GtkToggleAction *video_track_action;
+
+ GtkComboBox *halign;
+ GtkComboBox *valign;
+ GtkComboBox *background_type;
+
+ GtkEntry *text;
+ GtkEntry *seconds;
+
+ GtkSpinButton *frequency;
+} App;
+
+static int n_instances = 0;
+
+/* Prototypes for auto-connected signal handlers ***************************/
+
+/**
+ * These are declared non-static for signal auto-connection
+ */
+
+gboolean window_delete_event_cb (GtkWidget * window, GdkEvent * event,
+ App * app);
+void new_activate_cb (GtkMenuItem * item, App * app);
+void open_activate_cb (GtkMenuItem * item, App * app);
+void save_as_activate_cb (GtkMenuItem * item, App * app);
+void launch_project_activate_cb (GtkMenuItem * item, App * app);
+void quit_item_activate_cb (GtkMenuItem * item, App * app);
+void delete_activate_cb (GtkAction * item, App * app);
+void play_activate_cb (GtkAction * item, App * app);
+void stop_activate_cb (GtkAction * item, App * app);
+void move_up_activate_cb (GtkAction * item, App * app);
+void move_down_activate_cb (GtkAction * item, App * app);
+void add_effect_activate_cb (GtkAction * item, App * app);
+void add_file_activate_cb (GtkAction * item, App * app);
+void add_text_activate_cb (GtkAction * item, App * app);
+void add_test_activate_cb (GtkAction * item, App * app);
+void audio_track_activate_cb (GtkToggleAction * item, App * app);
+void video_track_activate_cb (GtkToggleAction * item, App * app);
+void add_transition_activate_cb (GtkAction * item, App * app);
+void app_selection_changed_cb (GtkTreeSelection * selection, App * app);
+void halign_changed_cb (GtkComboBox * widget, App * app);
+void valign_changed_cb (GtkComboBox * widget, App * app);
+void background_type_changed_cb (GtkComboBox * widget, App * app);
+void frequency_value_changed_cb (GtkSpinButton * widget, App * app);
+void on_apply_effect_cb (GtkButton * button, App * app);
+void on_cancel_add_effect_cb (GtkButton * button, App * app);
+gboolean add_effect_dlg_delete_event_cb (GtkWidget * widget, GdkEvent * event,
+ gpointer * app);
+
+gboolean
+duration_scale_change_value_cb (GtkRange * range,
+ GtkScrollType unused, gdouble value, App * app);
+
+gboolean
+in_point_scale_change_value_cb (GtkRange * range,
+ GtkScrollType unused, gdouble value, App * app);
+
+gboolean
+volume_change_value_cb (GtkRange * range,
+ GtkScrollType unused, gdouble value, App * app);
+
+/* UI state functions *******************************************************/
+
+/**
+ * Update properties of UI elements that depend on more than one thing.
+ */
+
+static void
+update_effect_sensitivity (App * app)
+{
+ GList *i;
+ gboolean ok = TRUE;
+
+ /* effects will work for multiple FileSource */
+ for (i = app->selected_objects; i; i = i->next) {
+ if (!GES_IS_URI_CLIP (i->data)) {
+ ok = FALSE;
+ break;
+ }
+ }
+
+ gtk_action_set_sensitive (app->add_effect,
+ ok && (app->n_selected > 0) && (app->state != GST_STATE_PLAYING)
+ && (app->state != GST_STATE_PAUSED));
+}
+
+static void
+update_delete_sensitivity (App * app)
+{
+ /* delete will work for multiple items */
+ gtk_action_set_sensitive (app->delete,
+ (app->n_selected > 0) && (app->state != GST_STATE_PLAYING)
+ && (app->state != GST_STATE_PAUSED));
+}
+
+static void
+update_add_transition_sensitivity (App * app)
+{
+ gtk_action_set_sensitive (app->add_transition,
+ (app->state != GST_STATE_PLAYING) && (app->state != GST_STATE_PAUSED));
+}
+
+static void
+update_move_up_down_sensitivity (App * app)
+{
+ gboolean can_move;
+
+ can_move = (app->n_selected == 1) &&
+ (app->state != GST_STATE_PLAYING) && (app->state != GST_STATE_PAUSED);
+
+ gtk_action_set_sensitive (app->move_up, can_move && (!app->first_selected));
+ gtk_action_set_sensitive (app->move_down, can_move && (!app->last_selected));
+}
+
+static void
+update_play_sensitivity (App * app)
+{
+ gtk_action_set_sensitive (app->play, app->n_objects);
+}
+
+/* Backend callbacks ********************************************************/
+
+static void
+test_source_notify_volume_changed_cb (GESClip * clip, GParamSpec *
+ unused G_GNUC_UNUSED, App * app)
+{
+ gdouble volume;
+
+ g_object_get (G_OBJECT (clip), "volume", &volume, NULL);
+
+ gtk_range_set_value (GTK_RANGE (app->volume), volume);
+}
+
+static void
+layer_notify_valid_changed_cb (GObject * object, GParamSpec * unused
+ G_GNUC_UNUSED, App * app)
+{
+ update_play_sensitivity (app);
+}
+
+static gboolean
+find_row_for_object (GtkListStore * model, GtkTreeIter * ret, GESClip * clip)
+{
+ gtk_tree_model_get_iter_first ((GtkTreeModel *) model, ret);
+
+ while (gtk_list_store_iter_is_valid (model, ret)) {
+ GESClip *clip2;
+ gtk_tree_model_get ((GtkTreeModel *) model, ret, 2, &clip2, -1);
+ if (clip2 == clip) {
+ gst_object_unref (clip2);
+ return TRUE;
+ }
+ gst_object_unref (clip2);
+ gtk_tree_model_iter_next ((GtkTreeModel *) model, ret);
+ }
+ return FALSE;
+}
+
+/* this callback is registered for every clip, and updates the
+ * corresponding duration cell in the model */
+static void
+clip_notify_duration_cb (GESClip * clip,
+ GParamSpec * arg G_GNUC_UNUSED, App * app)
+{
+ GtkTreeIter iter;
+ guint64 duration = 0;
+
+ g_object_get (clip, "duration", &duration, NULL);
+ find_row_for_object (app->model, &iter, clip);
+ gtk_list_store_set (app->model, &iter, 1, duration, -1);
+}
+
+/* these guys are only connected to filesources that are the target of the
+ * current selection */
+
+static void
+filesource_notify_duration_cb (GESClip * clip,
+ GParamSpec * arg G_GNUC_UNUSED, App * app)
+{
+ guint64 duration, max_inpoint;
+ duration = GES_TIMELINE_ELEMENT_DURATION (clip);
+ max_inpoint = GES_TIMELINE_ELEMENT_MAX_DURATION (clip) - duration;
+
+ gtk_range_set_value (GTK_RANGE (app->duration), duration);
+ gtk_range_set_fill_level (GTK_RANGE (app->in_point), max_inpoint);
+
+ if (max_inpoint < GES_TIMELINE_ELEMENT_INPOINT (clip))
+ g_object_set (clip, "in-point", max_inpoint, NULL);
+
+}
+
+static void
+filesource_notify_max_duration_cb (GESClip * clip,
+ GParamSpec * arg G_GNUC_UNUSED, App * app)
+{
+ gtk_range_set_range (GTK_RANGE (app->duration), 0, (gdouble)
+ GES_TIMELINE_ELEMENT_MAX_DURATION (clip));
+ gtk_range_set_range (GTK_RANGE (app->in_point), 0, (gdouble)
+ GES_TIMELINE_ELEMENT_MAX_DURATION (clip));
+}
+
+static void
+filesource_notify_in_point_cb (GESClip * clip,
+ GParamSpec * arg G_GNUC_UNUSED, App * app)
+{
+ gtk_range_set_value (GTK_RANGE (app->in_point),
+ GES_TIMELINE_ELEMENT_INPOINT (clip));
+}
+
+static void
+app_update_first_last_selected (App * app)
+{
+ GtkTreePath *path;
+
+ /* keep track of whether the first or last items are selected */
+ path = gtk_tree_path_new_from_indices (0, -1);
+ app->first_selected =
+ gtk_tree_selection_path_is_selected (app->selection, path);
+ gtk_tree_path_free (path);
+
+ path = gtk_tree_path_new_from_indices (app->n_objects - 1, -1);
+ app->last_selected =
+ gtk_tree_selection_path_is_selected (app->selection, path);
+ gtk_tree_path_free (path);
+}
+
+static void
+object_count_changed (App * app)
+{
+ app_update_first_last_selected (app);
+ update_move_up_down_sensitivity (app);
+ update_play_sensitivity (app);
+}
+
+static void
+title_source_text_changed_cb (GESClip * clip,
+ GParamSpec * arg G_GNUC_UNUSED, App * app)
+{
+ GtkTreeIter iter;
+ gchar *text;
+
+ g_object_get (clip, "text", &text, NULL);
+ if (text) {
+ find_row_for_object (app->model, &iter, clip);
+ gtk_list_store_set (app->model, &iter, 0, text, -1);
+ }
+}
+
+static void
+layer_object_added_cb (GESLayer * layer, GESClip * clip, App * app)
+{
+ GtkTreeIter iter;
+ gchar *description;
+
+ GST_INFO ("layer clip added cb %p %p %p", layer, clip, app);
+
+ gtk_list_store_append (app->model, &iter);
+
+ if (GES_IS_URI_CLIP (clip)) {
+ g_object_get (G_OBJECT (clip), "uri", &description, NULL);
+ gtk_list_store_set (app->model, &iter, 0, description, 2, clip, -1);
+ }
+
+ else if (GES_IS_TITLE_CLIP (clip)) {
+ gtk_list_store_set (app->model, &iter, 2, clip, -1);
+ g_signal_connect (G_OBJECT (clip), "notify::text",
+ G_CALLBACK (title_source_text_changed_cb), app);
+ title_source_text_changed_cb (clip, NULL, app);
+ }
+
+ else if (GES_IS_TEST_CLIP (clip)) {
+ gtk_list_store_set (app->model, &iter, 2, clip, 0, "Test Source", -1);
+ }
+
+ else if (GES_IS_BASE_TRANSITION_CLIP (clip)) {
+ gtk_list_store_set (app->model, &iter, 2, clip, 0, "Transition", -1);
+ }
+
+ g_signal_connect (G_OBJECT (clip), "notify::duration",
+ G_CALLBACK (clip_notify_duration_cb), app);
+ clip_notify_duration_cb (clip, NULL, app);
+
+ app->n_objects++;
+ object_count_changed (app);
+}
+
+static void
+layer_object_removed_cb (GESLayer * layer, GESClip * clip, App * app)
+{
+ GtkTreeIter iter;
+
+ GST_INFO ("layer clip removed cb %p %p %p", layer, clip, app);
+
+ if (!find_row_for_object (GTK_LIST_STORE (app->model), &iter, clip)) {
+ g_print ("clip deleted but we don't own it");
+ return;
+ }
+ app->n_objects--;
+ object_count_changed (app);
+
+ gtk_list_store_remove (app->model, &iter);
+}
+
+static void
+layer_object_moved_cb (GESClip * layer, GESClip * clip,
+ gint old, gint new, App * app)
+{
+ GtkTreeIter a, b;
+ GtkTreePath *path;
+
+ /* we can take the old position as given, but the new position might have to
+ * be adjusted. */
+ new = new < 0 ? (app->n_objects - 1) : new;
+
+ path = gtk_tree_path_new_from_indices (old, -1);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (app->model), &a, path);
+ gtk_tree_path_free (path);
+
+ path = gtk_tree_path_new_from_indices (new, -1);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (app->model), &b, path);
+ gtk_tree_path_free (path);
+
+ gtk_list_store_swap (app->model, &a, &b);
+ app_selection_changed_cb (app->selection, app);
+ update_move_up_down_sensitivity (app);
+}
+
+static void
+pipeline_state_changed_cb (App * app)
+{
+ gboolean playing_or_paused;
+
+ if (app->state == GST_STATE_PLAYING)
+ gtk_action_set_stock_id (app->play, GTK_STOCK_MEDIA_PAUSE);
+ else
+ gtk_action_set_stock_id (app->play, GTK_STOCK_MEDIA_PLAY);
+
+ update_delete_sensitivity (app);
+ update_add_transition_sensitivity (app);
+ update_move_up_down_sensitivity (app);
+
+ playing_or_paused = (app->state == GST_STATE_PLAYING) ||
+ (app->state == GST_STATE_PAUSED);
+
+ gtk_action_set_sensitive (app->add_file, !playing_or_paused);
+ gtk_action_set_sensitive (app->add_title, !playing_or_paused);
+ gtk_action_set_sensitive (app->add_test, !playing_or_paused);
+ gtk_action_set_sensitive ((GtkAction *) app->audio_track_action,
+ !playing_or_paused);
+ gtk_action_set_sensitive ((GtkAction *) app->video_track_action,
+ !playing_or_paused);
+ gtk_widget_set_sensitive (app->properties, !playing_or_paused);
+}
+
+static void
+project_bus_message_cb (GstBus * bus, GstMessage * message,
+ GMainLoop * mainloop)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ g_printerr ("ERROR\n");
+ g_main_loop_quit (mainloop);
+ break;
+ case GST_MESSAGE_EOS:
+ g_printerr ("Done\n");
+ g_main_loop_quit (mainloop);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+bus_message_cb (GstBus * bus, GstMessage * message, App * app)
+{
+ const GstStructure *s;
+ s = gst_message_get_structure (message);
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ g_print ("ERROR\n");
+ break;
+ case GST_MESSAGE_EOS:
+ gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_READY);
+ break;
+ case GST_MESSAGE_STATE_CHANGED:
+ if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (app->pipeline)) {
+ GstState old, new, pending;
+ gst_message_parse_state_changed (message, &old, &new, &pending);
+ app->state = new;
+ pipeline_state_changed_cb (app);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* Static UI Callbacks ******************************************************/
+
+static gboolean
+check_time (const gchar * time)
+{
+ static GRegex *re = NULL;
+
+ if (!re) {
+ if (NULL == (re =
+ g_regex_new ("^[0-9][0-9]:[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?$",
+ G_REGEX_EXTENDED, 0, NULL)))
+ return FALSE;
+ }
+
+ if (g_regex_match (re, time, 0, NULL))
+ return TRUE;
+ return FALSE;
+}
+
+static guint64
+str_to_time (const gchar * str)
+{
+ guint64 ret;
+ guint64 h, m;
+ gdouble s;
+ gchar buf[15];
+
+ buf[0] = str[0];
+ buf[1] = str[1];
+ buf[2] = '\0';
+
+ h = strtoull (buf, NULL, 10);
+
+ buf[0] = str[3];
+ buf[1] = str[4];
+ buf[2] = '\0';
+
+ m = strtoull (buf, NULL, 10);
+
+ strncpy (buf, &str[6], sizeof (buf) - 1);
+ buf[sizeof (buf) - 1] = '\0';
+ s = strtod (buf, NULL);
+
+ ret = (h * 3600 * GST_SECOND) +
+ (m * 60 * GST_SECOND) + ((guint64) (s * GST_SECOND));
+
+ return ret;
+}
+
+static void
+text_notify_text_changed_cb (GtkEntry * widget, GParamSpec * unused, App * app)
+{
+ GList *tmp;
+ const gchar *text;
+
+ if (app->ignore_input)
+ return;
+
+ text = gtk_entry_get_text (widget);
+
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (G_OBJECT (tmp->data), "text", text, NULL);
+ }
+}
+
+static void
+seconds_notify_text_changed_cb (GtkEntry * widget, GParamSpec * unused,
+ App * app)
+{
+ GList *tmp;
+ const gchar *text;
+
+ if (app->ignore_input)
+ return;
+
+ text = gtk_entry_get_text (app->seconds);
+
+ if (!check_time (text)) {
+ gtk_entry_set_icon_from_stock (app->seconds,
+ GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_WARNING);
+ } else {
+ gtk_entry_set_icon_from_stock (app->seconds,
+ GTK_ENTRY_ICON_SECONDARY, NULL);
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (GES_CLIP (tmp->data), "duration",
+ (guint64) str_to_time (text), NULL);
+ }
+ }
+}
+
+static void
+duration_cell_func (GtkTreeViewColumn * column, GtkCellRenderer * renderer,
+ GtkTreeModel * model, GtkTreeIter * iter, gpointer user)
+{
+ gchar buf[30];
+ guint64 duration;
+
+ gtk_tree_model_get (model, iter, 1, &duration, -1);
+ g_snprintf (buf, sizeof (buf), "%u:%02u:%02u.%09u", GST_TIME_ARGS (duration));
+ g_object_set (renderer, "text", &buf, NULL);
+}
+
+/* UI Initialization ********************************************************/
+
+static void
+connect_to_filesource (GESClip * clip, App * app)
+{
+ g_signal_connect (G_OBJECT (clip), "notify::max-duration",
+ G_CALLBACK (filesource_notify_max_duration_cb), app);
+ filesource_notify_max_duration_cb (clip, NULL, app);
+
+ g_signal_connect (G_OBJECT (clip), "notify::duration",
+ G_CALLBACK (filesource_notify_duration_cb), app);
+ filesource_notify_duration_cb (clip, NULL, app);
+
+ g_signal_connect (G_OBJECT (clip), "notify::in-point",
+ G_CALLBACK (filesource_notify_in_point_cb), app);
+ filesource_notify_in_point_cb (clip, NULL, app);
+}
+
+static void
+disconnect_from_filesource (GESClip * clip, App * app)
+{
+ g_signal_handlers_disconnect_by_func (G_OBJECT (clip),
+ filesource_notify_duration_cb, app);
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (clip),
+ filesource_notify_max_duration_cb, app);
+}
+
+static void
+connect_to_title_source (GESClip * clip, App * app)
+{
+ GESTitleClip *titleclip;
+ titleclip = GES_TITLE_CLIP (clip);
+ gtk_combo_box_set_active (app->halign,
+ ges_title_clip_get_halignment (titleclip));
+ gtk_combo_box_set_active (app->valign,
+ ges_title_clip_get_valignment (titleclip));
+ gtk_entry_set_text (app->text, ges_title_clip_get_text (titleclip));
+}
+
+static void
+disconnect_from_title_source (GESClip * clip, App * app)
+{
+}
+
+static void
+connect_to_test_source (GESClip * clip, App * app)
+{
+ GObjectClass *klass;
+ GParamSpecDouble *pspec;
+
+ GESTestClip *testclip;
+ testclip = GES_TEST_CLIP (clip);
+ gtk_combo_box_set_active (app->background_type,
+ ges_test_clip_get_vpattern (testclip));
+
+ g_signal_connect (G_OBJECT (testclip), "notify::volume",
+ G_CALLBACK (test_source_notify_volume_changed_cb), app);
+ test_source_notify_volume_changed_cb (clip, NULL, app);
+
+ klass = G_OBJECT_GET_CLASS (G_OBJECT (testclip));
+
+ pspec = G_PARAM_SPEC_DOUBLE (g_object_class_find_property (klass, "volume"));
+ gtk_range_set_range (GTK_RANGE (app->volume), pspec->minimum, pspec->maximum);
+
+ pspec = G_PARAM_SPEC_DOUBLE (g_object_class_find_property (klass, "freq"));
+ gtk_spin_button_set_range (app->frequency, pspec->minimum, pspec->maximum);
+ gtk_spin_button_set_value (app->frequency,
+ ges_test_clip_get_frequency (GES_TEST_CLIP (clip)));
+}
+
+static void
+disconnect_from_test_source (GESClip * clip, App * app)
+{
+ g_signal_handlers_disconnect_by_func (G_OBJECT (clip),
+ test_source_notify_volume_changed_cb, app);
+}
+
+static void
+connect_to_object (GESClip * clip, App * app)
+{
+ gchar buf[30];
+ guint64 duration;
+
+ app->ignore_input = TRUE;
+
+ duration = GES_TIMELINE_ELEMENT_DURATION (clip);
+ g_snprintf (buf, sizeof (buf), "%02u:%02u:%02u.%09u",
+ GST_TIME_ARGS (duration));
+ gtk_entry_set_text (app->seconds, buf);
+
+ if (GES_IS_URI_CLIP (clip)) {
+ connect_to_filesource (clip, app);
+ } else if (GES_IS_TITLE_CLIP (clip)) {
+ connect_to_title_source (clip, app);
+ } else if (GES_IS_TEST_CLIP (clip)) {
+ connect_to_test_source (clip, app);
+ }
+
+ app->ignore_input = FALSE;
+}
+
+static void
+disconnect_from_object (GESClip * clip, App * app)
+{
+ if (GES_IS_URI_CLIP (clip)) {
+ disconnect_from_filesource (clip, app);
+ } else if (GES_IS_TITLE_CLIP (clip)) {
+ disconnect_from_title_source (clip, app);
+ } else if (GES_IS_TEST_CLIP (clip)) {
+ disconnect_from_test_source (clip, app);
+ }
+}
+
+static GtkListStore *
+get_video_patterns (void)
+{
+ GEnumClass *enum_class;
+ GESTestClip *tr;
+ GESTestClipClass *klass;
+ GParamSpec *pspec;
+ GEnumValue *v;
+ GtkListStore *m;
+ GtkTreeIter i;
+
+ m = gtk_list_store_new (1, G_TYPE_STRING);
+
+ tr = ges_test_clip_new ();
+ klass = GES_TEST_CLIP_GET_CLASS (tr);
+
+ pspec = g_object_class_find_property (G_OBJECT_CLASS (klass), "vpattern");
+
+ enum_class = G_ENUM_CLASS (g_type_class_ref (pspec->value_type));
+
+ for (v = enum_class->values; v->value_nick != NULL; v++) {
+ gtk_list_store_append (m, &i);
+ gtk_list_store_set (m, &i, 0, v->value_name, -1);
+ }
+
+ g_type_class_unref (enum_class);
+ gst_object_unref (tr);
+
+ return m;
+}
+
+#define GET_WIDGET(dest,name,type) {\
+ if (!(dest =\
+ type(gtk_builder_get_object(builder, name))))\
+ goto fail;\
+}
+
+
+static void
+layer_added_cb (GESTimeline * timeline, GESLayer * layer, App * app)
+{
+ if (!GES_IS_LAYER (layer)) {
+ GST_ERROR ("This timeline contains a layer type other than "
+ "GESLayer. Timeline editing disabled");
+ return;
+ }
+
+ if (!(app->layer)) {
+ app->layer = layer;
+ }
+
+ if (layer != app->layer) {
+ GST_ERROR ("This demo doesn't support editing timelines with multiple"
+ " layers");
+ return;
+ }
+
+ g_signal_connect (app->layer, "clip-added",
+ G_CALLBACK (layer_object_added_cb), app);
+ g_signal_connect (app->layer, "clip-removed",
+ G_CALLBACK (layer_object_removed_cb), app);
+ g_signal_connect (app->layer, "object-moved",
+ G_CALLBACK (layer_object_moved_cb), app);
+ g_signal_connect (app->layer, "notify::valid",
+ G_CALLBACK (layer_notify_valid_changed_cb), app);
+}
+
+static void
+update_track_actions (App * app)
+{
+ g_signal_handlers_disconnect_by_func (app->audio_track_action,
+ audio_track_activate_cb, app);
+ g_signal_handlers_disconnect_by_func (app->video_track_action,
+ video_track_activate_cb, app);
+ gtk_toggle_action_set_active (app->audio_track_action, app->audio_tracks);
+ gtk_toggle_action_set_active (app->video_track_action, app->video_tracks);
+ gtk_action_set_sensitive ((GtkAction *) app->audio_track_action,
+ app->audio_tracks <= 1);
+ gtk_action_set_sensitive ((GtkAction *) app->video_track_action,
+ app->video_tracks <= 1);
+ g_signal_connect (G_OBJECT (app->audio_track_action), "activate",
+ G_CALLBACK (audio_track_activate_cb), app);
+ g_signal_connect (G_OBJECT (app->video_track_action), "activate",
+ G_CALLBACK (video_track_activate_cb), app);
+}
+
+static void
+track_added_cb (GESTimeline * timeline, GESTrack * track, App * app)
+{
+ if (track->type == GES_TRACK_TYPE_AUDIO) {
+ app->audio_tracks++;
+ if (!app->audio_track)
+ app->audio_track = track;
+ }
+ if (track->type == GES_TRACK_TYPE_VIDEO) {
+ app->video_tracks++;
+ if (!app->video_track)
+ app->video_track = track;
+ }
+
+
+ update_track_actions (app);
+}
+
+static void
+track_removed_cb (GESTimeline * timeline, GESTrack * track, App * app)
+{
+ if (track->type == GES_TRACK_TYPE_AUDIO)
+ app->audio_tracks--;
+ if (track->type == GES_TRACK_TYPE_VIDEO)
+ app->video_tracks--;
+
+ update_track_actions (app);
+}
+
+static gboolean
+create_ui (App * app)
+{
+ GtkBuilder *builder;
+ GtkTreeView *timeline;
+ GtkTreeViewColumn *duration_col;
+ GtkCellRenderer *duration_renderer;
+ GtkCellRenderer *background_type_renderer;
+ GtkListStore *backgrounds;
+ GstBus *bus;
+
+ /* construct widget tree */
+
+ builder = gtk_builder_new ();
+ gtk_builder_add_from_file (builder, "ges-ui.glade", NULL);
+
+ /* get a bunch of widgets from the XML tree */
+
+ GET_WIDGET (timeline, "timeline_treeview", GTK_TREE_VIEW);
+ GET_WIDGET (app->properties, "properties", GTK_WIDGET);
+ GET_WIDGET (app->filesource_properties, "filesource_properties", GTK_WIDGET);
+ GET_WIDGET (app->text_properties, "text_properties", GTK_WIDGET);
+ GET_WIDGET (app->main_window, "window", GTK_WIDGET);
+ GET_WIDGET (app->add_effect_dlg, "add_effect_dlg", GTK_WIDGET);
+ GET_WIDGET (app->audio_effect_entry, "entry1", GTK_WIDGET);
+ GET_WIDGET (app->video_effect_entry, "entry2", GTK_WIDGET);
+ GET_WIDGET (app->duration, "duration_scale", GTK_HSCALE);
+ GET_WIDGET (app->in_point, "in_point_scale", GTK_HSCALE);
+ GET_WIDGET (app->halign, "halign", GTK_COMBO_BOX);
+ GET_WIDGET (app->valign, "valign", GTK_COMBO_BOX);
+ GET_WIDGET (app->text, "text", GTK_ENTRY);
+ GET_WIDGET (duration_col, "duration_column", GTK_TREE_VIEW_COLUMN);
+ GET_WIDGET (duration_renderer, "duration_renderer", GTK_CELL_RENDERER);
+ GET_WIDGET (app->add_file, "add_file", GTK_ACTION);
+ GET_WIDGET (app->add_effect, "add_effect", GTK_ACTION);
+ GET_WIDGET (app->add_title, "add_text", GTK_ACTION);
+ GET_WIDGET (app->add_test, "add_test", GTK_ACTION);
+ GET_WIDGET (app->add_transition, "add_transition", GTK_ACTION);
+ GET_WIDGET (app->delete, "delete", GTK_ACTION);
+ GET_WIDGET (app->play, "play", GTK_ACTION);
+ GET_WIDGET (app->stop, "stop", GTK_ACTION);
+ GET_WIDGET (app->move_up, "move_up", GTK_ACTION);
+ GET_WIDGET (app->move_down, "move_down", GTK_ACTION);
+ GET_WIDGET (app->seconds, "seconds", GTK_ENTRY);
+ GET_WIDGET (app->generic_duration, "generic_duration", GTK_WIDGET);
+ GET_WIDGET (app->background_type, "background_type", GTK_COMBO_BOX);
+ GET_WIDGET (app->background_properties, "background_properties", GTK_WIDGET);
+ GET_WIDGET (app->frequency, "frequency", GTK_SPIN_BUTTON);
+ GET_WIDGET (app->volume, "volume", GTK_HSCALE);
+ GET_WIDGET (app->audio_track_action, "audio_track", GTK_TOGGLE_ACTION);
+ GET_WIDGET (app->video_track_action, "video_track", GTK_TOGGLE_ACTION);
+
+ /* get text notifications */
+
+ g_signal_connect (app->text, "notify::text",
+ G_CALLBACK (text_notify_text_changed_cb), app);
+
+ g_signal_connect (app->seconds, "notify::text",
+ G_CALLBACK (seconds_notify_text_changed_cb), app);
+
+ /* we care when the tree selection changes */
+
+ if (!(app->selection = gtk_tree_view_get_selection (timeline)))
+ goto fail;
+
+ gtk_tree_selection_set_mode (app->selection, GTK_SELECTION_MULTIPLE);
+
+ g_signal_connect (app->selection, "changed",
+ G_CALLBACK (app_selection_changed_cb), app);
+
+ /* create the model for the treeview */
+
+ if (!(app->model =
+ gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_OBJECT)))
+ goto fail;
+
+ gtk_tree_view_set_model (timeline, GTK_TREE_MODEL (app->model));
+
+ /* register custom cell data function */
+
+ gtk_tree_view_column_set_cell_data_func (duration_col, duration_renderer,
+ duration_cell_func, NULL, NULL);
+
+ /* initialize combo boxes */
+
+ if (!(backgrounds = get_video_patterns ()))
+ goto fail;
+
+ if (!(background_type_renderer = gtk_cell_renderer_text_new ()))
+ goto fail;
+
+ gtk_combo_box_set_model (app->background_type, (GtkTreeModel *)
+ backgrounds);
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (app->background_type),
+ background_type_renderer, FALSE);
+
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (app->background_type),
+ background_type_renderer, "text", 0);
+
+ g_signal_connect (app->timeline, "layer-added", G_CALLBACK
+ (layer_added_cb), app);
+ g_signal_connect (app->timeline, "track-added", G_CALLBACK
+ (track_added_cb), app);
+ g_signal_connect (app->timeline, "track-removed", G_CALLBACK
+ (track_removed_cb), app);
+
+ /* register callbacks on GES objects */
+ bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), app);
+
+ /* success */
+ gtk_builder_connect_signals (builder, app);
+ gst_object_unref (G_OBJECT (builder));
+ return TRUE;
+
+fail:
+ gst_object_unref (G_OBJECT (builder));
+ return FALSE;
+}
+
+#undef GET_WIDGET
+
+/* application methods ******************************************************/
+
+static void selection_foreach (GtkTreeModel * model, GtkTreePath * path,
+ GtkTreeIter * iter, gpointer user);
+
+static void
+app_toggle_playpause (App * app)
+{
+ if (app->state != GST_STATE_PLAYING) {
+ gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PLAYING);
+ } else {
+ gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PAUSED);
+ }
+}
+
+static void
+app_stop_playback (App * app)
+{
+ if ((app->state != GST_STATE_NULL) && (app->state != GST_STATE_READY)) {
+ gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_READY);
+ }
+}
+
+typedef struct
+{
+ GList *objects;
+ guint n;
+} select_info;
+
+static void
+app_update_selection (App * app)
+{
+ GList *cur;
+ GType type;
+ select_info info = { NULL, 0 };
+
+ /* clear old selection */
+ for (cur = app->selected_objects; cur; cur = cur->next) {
+ disconnect_from_object (cur->data, app);
+ gst_object_unref (cur->data);
+ cur->data = NULL;
+ }
+ g_list_free (app->selected_objects);
+ app->selected_objects = NULL;
+ app->n_selected = 0;
+
+ /* get new selection */
+ gtk_tree_selection_selected_foreach (GTK_TREE_SELECTION (app->selection),
+ selection_foreach, &info);
+ app->selected_objects = info.objects;
+ app->n_selected = info.n;
+
+ type = G_TYPE_NONE;
+ if (app->selected_objects) {
+ type = G_TYPE_FROM_INSTANCE (app->selected_objects->data);
+ for (cur = app->selected_objects; cur; cur = cur->next) {
+ if (type != G_TYPE_FROM_INSTANCE (cur->data)) {
+ type = G_TYPE_NONE;
+ break;
+ }
+ }
+ }
+
+ if (type != G_TYPE_NONE) {
+ for (cur = app->selected_objects; cur; cur = cur->next) {
+ connect_to_object (cur->data, app);
+ }
+ }
+
+ app->selected_type = type;
+ app_update_first_last_selected (app);
+}
+
+static void
+selection_foreach (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter
+ * iter, gpointer user)
+{
+ select_info *info = (select_info *) user;
+ GESClip *clip;
+
+ gtk_tree_model_get (model, iter, 2, &clip, -1);
+ info->objects = g_list_append (info->objects, clip);
+
+ info->n++;
+ return;
+}
+
+static GList *
+app_get_selected_objects (App * app)
+{
+ return g_list_copy (app->selected_objects);
+}
+
+static void
+app_delete_objects (App * app, GList * objects)
+{
+ GList *cur;
+
+ for (cur = objects; cur; cur = cur->next) {
+ ges_layer_remove_clip (app->layer, GES_CLIP (cur->data));
+ cur->data = NULL;
+ }
+
+ g_list_free (objects);
+}
+
+/* the following two methods assume exactly one clip is selected and that the
+ * requested action is valid */
+
+static void
+app_move_selected_up (App * app)
+{
+ GST_FIXME ("This function is not implement, please implement it :)");
+}
+
+static void
+app_add_effect_on_selected_clips (App * app, const gchar * bin_desc)
+{
+ GList *objects, *tmp;
+ GESTrackElement *effect = NULL;
+
+ /* No crash if the video is playing */
+ gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PAUSED);
+ objects = ges_layer_get_clips (app->layer);
+
+ for (tmp = objects; tmp; tmp = tmp->next) {
+ effect = GES_TRACK_ELEMENT (ges_effect_new (bin_desc));
+ ges_container_add (GES_CONTAINER (tmp->data),
+ GES_TIMELINE_ELEMENT (effect));
+ gst_object_unref (tmp->data);
+ }
+}
+
+gboolean
+add_effect_dlg_delete_event_cb (GtkWidget * widget, GdkEvent * event,
+ gpointer * app)
+{
+ gtk_widget_hide (((App *) app)->add_effect_dlg);
+ return TRUE;
+}
+
+void
+on_cancel_add_effect_cb (GtkButton * button, App * app)
+{
+ gtk_widget_hide (app->add_effect_dlg);
+}
+
+void
+on_apply_effect_cb (GtkButton * button, App * app)
+{
+ const gchar *effect;
+
+ effect = gtk_entry_get_text (GTK_ENTRY (app->video_effect_entry));
+ if (g_strcmp0 (effect, ""))
+ app_add_effect_on_selected_clips (app, effect);
+
+ gtk_entry_set_text (GTK_ENTRY (app->video_effect_entry), "");
+
+ effect = gtk_entry_get_text (GTK_ENTRY (app->audio_effect_entry));
+ if (g_strcmp0 (effect, ""))
+ app_add_effect_on_selected_clips (app, effect);
+
+ gtk_entry_set_text (GTK_ENTRY (app->audio_effect_entry), "");
+
+ gtk_widget_hide (app->add_effect_dlg);
+}
+
+static void
+app_move_selected_down (App * app)
+{
+ GST_FIXME ("This function is not implement, please implement it :)");
+}
+
+static void
+app_add_file (App * app, gchar * uri)
+{
+ GESClip *clip;
+
+ GST_DEBUG ("adding file %s", uri);
+
+ clip = GES_CLIP (ges_uri_clip_new (uri));
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip),
+ ges_layer_get_duration (app->layer));
+
+ ges_layer_add_clip (app->layer, clip);
+}
+
+static void
+app_launch_project (App * app, gchar * uri)
+{
+ GESTimeline *timeline;
+ GMainLoop *mainloop;
+ GESPipeline *pipeline;
+ GstBus *bus;
+ GESProject *project;
+
+ uri = g_strsplit (uri, "//", 2)[1];
+ printf ("we will launch this uri : %s\n", uri);
+ project = ges_project_new (uri);
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+ pipeline = ges_pipeline_new ();
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ ges_pipeline_set_timeline (pipeline, timeline);
+ ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_PREVIEW_VIDEO);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (project_bus_message_cb),
+ mainloop);
+ g_main_loop_run (mainloop);
+ g_object_unref (project);
+}
+
+static void
+app_add_title (App * app)
+{
+ GESClip *clip;
+
+ GST_DEBUG ("adding title");
+
+ clip = GES_CLIP (ges_title_clip_new ());
+ g_object_set (G_OBJECT (clip), "duration", GST_SECOND,
+ "start", ges_layer_get_duration (app->layer), NULL);
+ ges_layer_add_clip (app->layer, clip);
+}
+
+static void
+app_add_test (App * app)
+{
+ GESClip *clip;
+
+ GST_DEBUG ("adding test");
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ g_object_set (G_OBJECT (clip), "duration", GST_SECOND,
+ "start", ges_layer_get_duration (app->layer), NULL);
+
+ ges_layer_add_clip (app->layer, clip);
+}
+
+static void
+app_add_transition (App * app)
+{
+ GST_FIXME ("This function is not implement, please implement it :)");
+}
+
+static void
+app_save_to_uri (App * app, gchar * uri)
+{
+ ges_timeline_save_to_uri (app->timeline, uri, NULL, FALSE, NULL);
+}
+
+static void
+app_add_audio_track (App * app)
+{
+ if (app->audio_tracks)
+ return;
+
+ app->audio_track = GES_TRACK (ges_audio_track_new ());
+ ges_timeline_add_track (app->timeline, app->audio_track);
+}
+
+static void
+app_remove_audio_track (App * app)
+{
+ if (!app->audio_tracks)
+ return;
+
+ ges_timeline_remove_track (app->timeline, app->audio_track);
+ app->audio_track = NULL;
+}
+
+static void
+app_add_video_track (App * app)
+{
+ if (app->video_tracks)
+ return;
+
+ app->video_track = GES_TRACK (ges_video_track_new ());
+ ges_timeline_add_track (app->timeline, app->video_track);
+}
+
+static void
+app_remove_video_track (App * app)
+{
+ if (!app->video_tracks)
+ return;
+
+ ges_timeline_remove_track (app->timeline, app->video_track);
+ app->video_track = NULL;
+}
+
+static void
+app_dispose (App * app)
+{
+ if (app) {
+ if (app->pipeline) {
+ gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_NULL);
+ gst_object_unref (app->pipeline);
+ }
+
+ g_free (app);
+ }
+
+ n_instances--;
+
+ if (n_instances == 0) {
+ gtk_main_quit ();
+ }
+}
+
+static App *
+app_init (void)
+{
+ App *ret;
+ ret = g_new0 (App, 1);
+ n_instances++;
+
+ ret->selected_type = G_TYPE_NONE;
+
+ if (!(ret->timeline = ges_timeline_new ()))
+ goto fail;
+
+ if (!(ret->pipeline = ges_pipeline_new ()))
+ goto fail;
+
+ if (!ges_pipeline_set_timeline (ret->pipeline, ret->timeline))
+ goto fail;
+
+ if (!(create_ui (ret)))
+ goto fail;
+
+ return ret;
+
+fail:
+ app_dispose (ret);
+ return NULL;
+}
+
+static App *
+app_new (void)
+{
+ App *ret;
+ GESTrack *a = NULL, *v = NULL;
+
+ ret = app_init ();
+
+ /* add base audio and video track */
+
+ if (!(a = GES_TRACK (ges_audio_track_new ())))
+ goto fail;
+
+ if (!(ges_timeline_add_track (ret->timeline, a)))
+ goto fail;
+
+ if (!(v = GES_TRACK (ges_video_track_new ())))
+ goto fail;
+
+ if (!(ges_timeline_add_track (ret->timeline, v)))
+ goto fail;
+
+ if (!(ret->layer = ges_layer_new ()))
+ goto fail;
+
+ if (!(ges_timeline_add_layer (ret->timeline, ret->layer)))
+ goto fail;
+
+ ret->audio_track = a;
+ ret->video_track = v;
+ return ret;
+
+fail:
+
+ if (a)
+ gst_object_unref (a);
+ if (v)
+ gst_object_unref (v);
+ app_dispose (ret);
+ return NULL;
+}
+
+static gboolean
+load_file_async (App * app)
+{
+ ges_timeline_load_from_uri (app->timeline, app->pending_uri, NULL);
+
+ g_free (app->pending_uri);
+ app->pending_uri = NULL;
+
+ return FALSE;
+}
+
+static gboolean
+app_new_from_uri (gchar * uri)
+{
+ App *ret;
+
+ ret = app_init ();
+ ret->pending_uri = g_strdup (uri);
+ g_idle_add ((GSourceFunc) load_file_async, ret);
+
+ return FALSE;
+}
+
+/* UI callbacks ************************************************************/
+
+gboolean
+window_delete_event_cb (GtkWidget * window, GdkEvent * event, App * app)
+{
+ app_dispose (app);
+ return FALSE;
+}
+
+void
+new_activate_cb (GtkMenuItem * item, App * app)
+{
+ app_new ();
+}
+
+void
+launch_project_activate_cb (GtkMenuItem * item, App * app)
+{
+ GtkFileChooserDialog *dlg;
+ GtkFileFilter *filter;
+
+ GST_DEBUG ("add file signal handler");
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, "pitivi projects");
+ gtk_file_filter_add_pattern (filter, "*.xptv");
+ dlg = (GtkFileChooserDialog *)
+ gtk_file_chooser_dialog_new ("Preview Project...",
+ GTK_WINDOW (app->main_window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), filter);
+
+ g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
+
+ if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
+ gchar *uri;
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
+ gtk_widget_destroy ((GtkWidget *) dlg);
+ app_launch_project (app, uri);
+ }
+}
+
+void
+open_activate_cb (GtkMenuItem * item, App * app)
+{
+ GtkFileChooserDialog *dlg;
+
+ GST_DEBUG ("add file signal handler");
+
+ dlg = (GtkFileChooserDialog *) gtk_file_chooser_dialog_new ("Open Project...",
+ GTK_WINDOW (app->main_window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+ g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
+
+ if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
+ gchar *uri;
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
+ app_new_from_uri (uri);
+ g_free (uri);
+ }
+ gtk_widget_destroy ((GtkWidget *) dlg);
+}
+
+void
+save_as_activate_cb (GtkMenuItem * item, App * app)
+{
+ GtkFileChooserDialog *dlg;
+
+ GST_DEBUG ("save as signal handler");
+
+ dlg = (GtkFileChooserDialog *)
+ gtk_file_chooser_dialog_new ("Save project as...",
+ GTK_WINDOW (app->main_window), GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
+
+ if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
+ gchar *uri;
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
+ app_save_to_uri (app, uri);
+ g_free (uri);
+ }
+ gtk_widget_destroy ((GtkWidget *) dlg);
+}
+
+void
+quit_item_activate_cb (GtkMenuItem * item, App * app)
+{
+ gtk_main_quit ();
+}
+
+void
+delete_activate_cb (GtkAction * item, App * app)
+{
+ /* get a gslist of selected track elements */
+ GList *objects = NULL;
+
+ objects = app_get_selected_objects (app);
+ app_delete_objects (app, objects);
+}
+
+void
+add_effect_activate_cb (GtkAction * item, App * app)
+{
+ gtk_widget_show_all (app->add_effect_dlg);
+}
+
+void
+add_file_activate_cb (GtkAction * item, App * app)
+{
+ GtkFileChooserDialog *dlg;
+
+ GST_DEBUG ("add file signal handler");
+
+ dlg = (GtkFileChooserDialog *) gtk_file_chooser_dialog_new ("Add File...",
+ GTK_WINDOW (app->main_window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+ g_object_set (G_OBJECT (dlg), "select-multiple", TRUE, NULL);
+
+ if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
+ GSList *uris;
+ GSList *cur;
+ uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dlg));
+ for (cur = uris; cur; cur = cur->next)
+ app_add_file (app, cur->data);
+ g_slist_free (uris);
+ }
+ gtk_widget_destroy ((GtkWidget *) dlg);
+}
+
+void
+add_text_activate_cb (GtkAction * item, App * app)
+{
+ app_add_title (app);
+}
+
+void
+add_test_activate_cb (GtkAction * item, App * app)
+{
+ app_add_test (app);
+}
+
+void
+add_transition_activate_cb (GtkAction * item, App * app)
+{
+ app_add_transition (app);
+}
+
+void
+play_activate_cb (GtkAction * item, App * app)
+{
+ app_toggle_playpause (app);
+}
+
+void
+stop_activate_cb (GtkAction * item, App * app)
+{
+ app_stop_playback (app);
+}
+
+void
+move_up_activate_cb (GtkAction * item, App * app)
+{
+ app_move_selected_up (app);
+}
+
+void
+move_down_activate_cb (GtkAction * item, App * app)
+{
+ app_move_selected_down (app);
+}
+
+void
+audio_track_activate_cb (GtkToggleAction * item, App * app)
+{
+ if (gtk_toggle_action_get_active (item)) {
+ app_add_audio_track (app);
+ } else {
+ app_remove_audio_track (app);
+ }
+}
+
+void
+video_track_activate_cb (GtkToggleAction * item, App * app)
+{
+ if (gtk_toggle_action_get_active (item)) {
+ app_add_video_track (app);
+ } else {
+ app_remove_video_track (app);
+ }
+}
+
+void
+app_selection_changed_cb (GtkTreeSelection * selection, App * app)
+{
+ app_update_selection (app);
+
+ update_delete_sensitivity (app);
+ update_effect_sensitivity (app);
+ update_add_transition_sensitivity (app);
+ update_move_up_down_sensitivity (app);
+
+ gtk_widget_set_visible (app->properties, app->n_selected > 0);
+
+ gtk_widget_set_visible (app->filesource_properties,
+ app->selected_type == GES_TYPE_URI_CLIP);
+
+ gtk_widget_set_visible (app->text_properties,
+ app->selected_type == GES_TYPE_TITLE_CLIP);
+
+ gtk_widget_set_visible (app->generic_duration,
+ app->selected_type != G_TYPE_NONE &&
+ app->selected_type != G_TYPE_INVALID);
+
+ gtk_widget_set_visible (app->background_properties,
+ app->selected_type == GES_TYPE_TEST_CLIP);
+}
+
+gboolean
+duration_scale_change_value_cb (GtkRange * range, GtkScrollType unused,
+ gdouble value, App * app)
+{
+ GList *i;
+
+ for (i = app->selected_objects; i; i = i->next) {
+ guint64 duration, maxduration;
+ maxduration = GES_TIMELINE_ELEMENT_MAX_DURATION (i->data);
+ duration = (value < maxduration ? (value > 0 ? value : 0) : maxduration);
+ g_object_set (G_OBJECT (i->data), "duration", (guint64) duration, NULL);
+ }
+ return TRUE;
+}
+
+gboolean
+in_point_scale_change_value_cb (GtkRange * range, GtkScrollType unused,
+ gdouble value, App * app)
+{
+ GList *i;
+
+ for (i = app->selected_objects; i; i = i->next) {
+ guint64 in_point, maxduration;
+ maxduration = GES_TIMELINE_ELEMENT_MAX_DURATION (i->data) -
+ GES_TIMELINE_ELEMENT_DURATION (i->data);
+ in_point = (value < maxduration ? (value > 0 ? value : 0) : maxduration);
+ g_object_set (G_OBJECT (i->data), "in-point", (guint64) in_point, NULL);
+ }
+ return TRUE;
+}
+
+void
+halign_changed_cb (GtkComboBox * widget, App * app)
+{
+ GList *tmp;
+ int active;
+
+ if (app->ignore_input)
+ return;
+
+ active = gtk_combo_box_get_active (app->halign);
+
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (G_OBJECT (tmp->data), "halignment", active, NULL);
+ }
+}
+
+void
+valign_changed_cb (GtkComboBox * widget, App * app)
+{
+ GList *tmp;
+ int active;
+
+ if (app->ignore_input)
+ return;
+
+ active = gtk_combo_box_get_active (app->valign);
+
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (G_OBJECT (tmp->data), "valignment", active, NULL);
+ }
+}
+
+void
+background_type_changed_cb (GtkComboBox * widget, App * app)
+{
+ GList *tmp;
+ gint p;
+
+ if (app->ignore_input)
+ return;
+
+ p = gtk_combo_box_get_active (widget);
+
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (G_OBJECT (tmp->data), "vpattern", (gint) p, NULL);
+ }
+}
+
+void
+frequency_value_changed_cb (GtkSpinButton * widget, App * app)
+{
+ GList *tmp;
+ gdouble value;
+
+ if (app->ignore_input)
+ return;
+
+ value = gtk_spin_button_get_value (widget);
+
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (G_OBJECT (tmp->data), "freq", (gdouble) value, NULL);
+ }
+}
+
+gboolean
+volume_change_value_cb (GtkRange * widget, GtkScrollType unused, gdouble
+ value, App * app)
+{
+ GList *tmp;
+
+ value = value >= 0 ? (value <= 2.0 ? value : 2.0) : 0;
+
+ for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
+ g_object_set (G_OBJECT (tmp->data), "volume", (gdouble) value, NULL);
+ }
+ return TRUE;
+}
+
+/* main *********************************************************************/
+
+int
+main (int argc, char *argv[])
+{
+ App *app;
+
+ /* intialize GStreamer and GES */
+ gst_init (&argc, &argv);
+ ges_init ();
+
+ /* initialize UI */
+ gtk_init (&argc, &argv);
+
+ if ((app = app_new ())) {
+ gtk_main ();
+ }
+
+ return 0;
+}
--- /dev/null
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="liststore1">
+ <columns>
+ <!-- column-name gchararray1 -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Left</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Center</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Right</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore2">
+ <columns>
+ <!-- column-name gchararray2 -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Baseline</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Bottom</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Top</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkWindow" id="window">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">GES Demo</property>
+ <property name="default_width">540</property>
+ <property name="default_height">450</property>
+ <signal name="delete_event" handler="window_delete_event_cb"/>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="file">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="new_item">
+ <property name="label">gtk-new</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="new_activate_cb"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="open_item">
+ <property name="label">gtk-open</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="open_activate_cb"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="save_item">
+ <property name="label">gtk-save</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="as_item">
+ <property name="label">gtk-save-as</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="save_as_activate_cb"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="launch_pitivi_project">
+ <property name="label" translatable="yes">Preview Pitivi Project</property>
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes">Launches a .xptv project</property>
+ <property name="image">image1</property>
+ <property name="use_stock">False</property>
+ <signal name="activate" handler="launch_pitivi_project_activate_cb"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="Edit">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="cut_item">
+ <property name="label">gtk-cut</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="copy_item">
+ <property name="label">gtk-copy</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="paste_item">
+ <property name="label">gtk-paste</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="delete_item">
+ <property name="visible">True</property>
+ <property name="related_action">delete</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="timeline1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Timeline</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="add_file_item">
+ <property name="visible">True</property>
+ <property name="related_action">add_file</property>
+ <property name="use_action_appearance">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem4">
+ <property name="visible">True</property>
+ <property name="related_action">add_text</property>
+ <property name="use_action_appearance">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem5">
+ <property name="visible">True</property>
+ <property name="related_action">add_test</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="imagemenuitem1">
+ <property name="visible">True</property>
+ <property name="related_action">add_transition</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem7">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem8">
+ <property name="visible">True</property>
+ <property name="related_action">move_up</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem9">
+ <property name="visible">True</property>
+ <property name="related_action">move_down</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="related_action">play</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem6">
+ <property name="visible">True</property>
+ <property name="related_action">stop</property>
+ <property name="use_action_appearance">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="add_effect_item">
+ <property name="visible">True</property>
+ <property name="related_action">add_effect</property>
+ <property name="use_action_appearance">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="Help">
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="imagemenuitem10">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkToolButton" id="play_button">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">play</property>
+ <property name="label" translatable="yes">toolbutton1</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton6">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">stop</property>
+ <property name="label" translatable="yes">toolbutton6</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton3">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="delete_button">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">delete</property>
+ <property name="label" translatable="yes">Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="add_button">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">add_file</property>
+ <property name="label" translatable="yes">toolbutton1</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton2">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">add_text</property>
+ <property name="label" translatable="yes">toolbutton2</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton4">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">add_test</property>
+ <property name="label" translatable="yes">toolbutton4</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton5">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">add_transition</property>
+ <property name="label" translatable="yes">toolbutton5</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="add_button1">
+ <property name="visible">True</property>
+ <property name="related_action">add_effect</property>
+ <property name="use_action_appearance">True</property>
+ <property name="label" translatable="yes">File</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton7">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton8">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">move_up</property>
+ <property name="label" translatable="yes">toolbutton8</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton9">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">move_down</property>
+ <property name="label" translatable="yes">toolbutton9</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton10">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="toolbutton11">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">audio_track</property>
+ <property name="label" translatable="yes">toolbutton11</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="toolbutton12">
+ <property name="visible">True</property>
+ <property name="use_action_appearance">True</property>
+ <property name="related_action">video_track</property>
+ <property name="label" translatable="yes">toolbutton12</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVPaned" id="vpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">12</property>
+ <property name="position">450</property>
+ <property name="position_set">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="timeline_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="description_column">
+ <property name="resizable">True</property>
+ <property name="sizing">autosize</property>
+ <property name="title">Description</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="duration_column">
+ <property name="sizing">autosize</property>
+ <property name="title">Duration</property>
+ <child>
+ <object class="GtkCellRendererText" id="duration_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="properties">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="generic_duration">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes">Duration:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="seconds">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="filesource_properties">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label_duration">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <property name="xpad">12</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Duration:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="duration_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">bottom</property>
+ <signal name="change_value" handler="duration_scale_change_value_cb"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label_inpoint">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <property name="xpad">12</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">In Point:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="in_point_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_fill_level">True</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">bottom</property>
+ <signal name="change_value" handler="in_point_scale_change_value_cb"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="background_properties">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes">Background:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="background_type">
+ <property name="visible">True</property>
+ <signal name="changed" handler="background_type_changed_cb"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes">Sound:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Frequency:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="frequency">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="adjustment">adjustment2</property>
+ <signal name="value_changed" handler="frequency_value_changed_cb"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Volume:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="volume">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="lower_stepper_sensitivity">off</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="draw_value">False</property>
+ <signal name="change_value" handler="volume_change_value_cb"/>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="text_properties">
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes">Text:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes">Alignment:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="halign">
+ <property name="visible">True</property>
+ <property name="model">liststore1</property>
+ <signal name="changed" handler="halign_changed_cb"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="valign">
+ <property name="visible">True</property>
+ <property name="model">liststore2</property>
+ <signal name="changed" handler="valign_changed_cb"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes"><b>Edit Object</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkTreeSelection" id="selection"/>
+ <object class="GtkDialog" id="add_effect_dlg">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Add effects</property>
+ <property name="window_position">center-always</property>
+ <property name="type_hint">dialog</property>
+ <signal name="delete_event" handler="add_effect_dlg_delete_event_cb"/>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Audio effects bin description</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <signal name="activate" handler="on_apply_effect_cb"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Video effects bin desciption:</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <signal name="activate" handler="on_apply_effect_cb"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_cancel_add_effect_cb"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_apply_effect_cb"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">button2</action-widget>
+ <action-widget response="0">button1</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-floppy</property>
+ </object>
+ <object class="GtkSizeGroup" id="LabelSizeGroup">
+ <widgets>
+ <widget name="label4"/>
+ <widget name="label_inpoint"/>
+ <widget name="label_duration"/>
+ <widget name="label_duration"/>
+ <widget name="label_inpoint"/>
+ <widget name="label2"/>
+ <widget name="label3"/>
+ <widget name="label4"/>
+ <widget name="label5"/>
+ <widget name="label6"/>
+ </widgets>
+ </object>
+ <object class="GtkAction" id="delete">
+ <property name="label">Delete</property>
+ <property name="stock_id">gtk-delete</property>
+ <property name="is_important">True</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="delete_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="add_file">
+ <property name="label">Add File...</property>
+ <property name="short_label">File</property>
+ <property name="icon_name">applications-multimedia</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="add_file_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="play">
+ <property name="label">Play</property>
+ <property name="stock_id">gtk-media-play</property>
+ <property name="is_important">True</property>
+ <property name="sensitive">False</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="play_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="add_text">
+ <property name="label">Add Text</property>
+ <property name="short_label">Title</property>
+ <property name="icon_name">insert-text</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="add_text_activate_cb"/>
+ </object>
+ <object class="GtkAccelGroup" id="accelgroup1"/>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">2</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">0.10000000000000001</property>
+ <property name="page_size">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment2">
+ <property name="value">440</property>
+ <property name="upper">20000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAction" id="add_test">
+ <property name="label">Add Test Source</property>
+ <property name="short_label">Test Source</property>
+ <property name="icon_name">utilities-system-monitor</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="add_test_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="add_transition">
+ <property name="label">Add Transition</property>
+ <property name="short_label">Transition</property>
+ <property name="icon_name">media-playlist-shuffle</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="add_transition_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="stop">
+ <property name="label">Stop</property>
+ <property name="short_label">Stop</property>
+ <property name="stock_id">gtk-media-stop</property>
+ <signal name="activate" handler="stop_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="move_up">
+ <property name="label">Move Selected Up</property>
+ <property name="short_label">Move Up</property>
+ <property name="stock_id">gtk-go-up</property>
+ <signal name="activate" handler="move_up_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="move_down">
+ <property name="label">Move Selected Down</property>
+ <property name="short_label">Move Down</property>
+ <property name="stock_id">gtk-go-down</property>
+ <signal name="activate" handler="move_down_activate_cb"/>
+ </object>
+ <object class="GtkToggleAction" id="audio_track">
+ <property name="label">Audio</property>
+ <property name="short_label">Audio</property>
+ <property name="tooltip">Play Audio Track</property>
+ <property name="icon_name">audio-x-generic</property>
+ <property name="always_show_image">True</property>
+ <property name="active">True</property>
+ <signal name="activate" handler="audio_track_activate_cb"/>
+ </object>
+ <object class="GtkToggleAction" id="video_track">
+ <property name="label">Video</property>
+ <property name="short_label">Video</property>
+ <property name="tooltip">Render Video Track</property>
+ <property name="icon_name">video-x-generic</property>
+ <property name="always_show_image">True</property>
+ <property name="active">True</property>
+ <signal name="activate" handler="video_track_activate_cb"/>
+ </object>
+ <object class="GtkAction" id="add_effect">
+ <property name="label">Add effect...</property>
+ <property name="short_label">Effect</property>
+ <property name="icon_name">face-smile</property>
+ <property name="always_show_image">True</property>
+ <signal name="activate" handler="add_effect_activate_cb"/>
+ </object>
+</interface>
--- /dev/null
+/* GStreamer GES plugin
+ *
+ * Copyright (C) 2019 Igalia S.L
+ * Author: 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gesdemux.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <ges/ges.h>
+
+static void
+bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ g_printerr ("Got error message on the bus\n");
+ g_main_loop_quit (mainloop);
+ break;
+ case GST_MESSAGE_EOS:
+ g_print ("Done\n");
+ g_main_loop_quit (mainloop);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+source_setup_cb (GstElement * playbin, GstElement * source,
+ GESTimeline * timeline)
+{
+ g_object_set (source, "timeline", timeline, NULL);
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *mainloop = NULL;
+ GstElement *pipeline = NULL;
+ GESTimeline *timeline;
+ GESLayer *layer = NULL;
+ GstBus *bus = NULL;
+ guint i, ret = 0;
+ gchar *uri = NULL;
+ GstClockTime start = 0;
+
+ if (argc < 2) {
+ g_print ("Usage: %s <list of files>\n", argv[0]);
+ return -1;
+ }
+
+ gst_init (&argc, &argv);
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ layer = (GESLayer *) ges_layer_new ();
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+
+ /* Build the timeline */
+ for (i = 1; i < argc; i++) {
+ GESClip *clip;
+
+ uri = g_strdup (argv[i]);
+ if (!gst_uri_is_valid (uri)) {
+ g_free (uri);
+ uri = gst_filename_to_uri (argv[i], NULL);
+ }
+ clip = GES_CLIP (ges_uri_clip_new (uri));
+
+ if (!clip) {
+ g_printerr ("Could not create clip for file: %s\n", argv[i]);
+ g_free (uri);
+ goto err;
+ }
+ g_object_set (clip, "start", start, NULL);
+ ges_layer_add_clip (layer, clip);
+
+ start += ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (clip));
+
+ g_free (uri);
+ }
+
+ /* Use a usual playbin pipeline */
+ pipeline = gst_element_factory_make ("playbin", NULL);
+ g_object_set (pipeline, "uri", "ges://", NULL);
+ g_signal_connect (pipeline, "source-setup", G_CALLBACK (source_setup_cb),
+ timeline);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ g_main_loop_run (mainloop);
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+done:
+ gst_clear_object (&pipeline);
+ if (mainloop)
+ g_main_loop_unref (mainloop);
+
+ return ret;
+err:
+ goto done;
+}
--- /dev/null
+examples = [
+ 'concatenate',
+ 'gessrc',
+ 'simple1',
+ 'test1',
+ 'test2',
+ 'test3',
+ 'test4',
+ 'transition',
+ 'thumbnails',
+ 'overlays',
+ 'text_properties',
+ 'assets',
+ 'multifilesrc',
+ 'play_timeline_with_one_clip'
+]
+
+# TODO Properly port to Gtk 3
+#
+# if gtk_dep.found()
+# examples = examples + ['ges-ui']
+# endif
+
+
+foreach example_name : examples
+ exe = executable(example_name, '@0@.c'.format(example_name),
+ c_args : ges_c_args,
+ dependencies : libges_deps + [ges_dep],
+ )
+endforeach
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2013 Lubosz Sarnecki <lubosz@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <ges/ges.h>
+
+/* A image sequence test */
+int
+main (int argc, gchar ** argv)
+{
+ GError *err = NULL;
+ GOptionContext *ctx;
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESAsset *asset;
+ GESLayer *layer;
+ GMainLoop *mainloop;
+ GESTrack *track;
+
+ gint duration = 10;
+ gchar *filepattern = NULL;
+
+ GOptionEntry options[] = {
+ {"duration", 'd', 0, G_OPTION_ARG_INT, &duration,
+ "duration to use from the file (in seconds, default:10s)", "seconds"},
+ {"pattern-url", 'u', 0, G_OPTION_ARG_FILENAME, &filepattern,
+ "Pattern of the files. i.e. multifile:///foo/%04d.jpg",
+ "pattern-url"},
+ {NULL}
+ };
+
+ ctx = g_option_context_new ("- Plays an image sequence");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ exit (1);
+ }
+
+ if (filepattern == NULL) {
+ g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
+ exit (0);
+ }
+ g_option_context_free (ctx);
+
+ gst_init (&argc, &argv);
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ track = GES_TRACK (ges_video_track_new ());
+ ges_timeline_add_track (timeline, track);
+
+ layer = ges_layer_new ();
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+
+ asset = GES_ASSET (ges_uri_clip_asset_request_sync (filepattern, &err));
+
+ ges_layer_add_asset (layer, asset, 0, 0, 5 * GST_SECOND,
+ GES_TRACK_TYPE_VIDEO);
+
+ pipeline = ges_pipeline_new ();
+
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ g_timeout_add_seconds (4, (GSourceFunc) g_main_loop_quit, mainloop);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon@alum.berkeley.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <ges/ges.h>
+
+typedef struct
+{
+ int type;
+ char *name;
+} transition_type;
+
+GESClip *make_source (char *path, guint64 start, guint64 duration,
+ gint priority);
+
+GESClip *make_overlay (char *text, guint64 start, guint64 duration,
+ gint priority, guint32 color, gdouble xpos, gdouble ypos);
+
+GESPipeline *make_timeline (char *path, float duration, char *text,
+ guint32 color, gdouble xpos, gdouble ypos);
+
+#define DEFAULT_DURATION 5
+#define DEFAULT_POS 0.5
+
+GESClip *
+make_source (char *path, guint64 start, guint64 duration, gint priority)
+{
+ gchar *uri = gst_filename_to_uri (path, NULL);
+
+ GESClip *ret = GES_CLIP (ges_uri_clip_new (uri));
+
+ g_object_set (ret,
+ "start", (guint64) start,
+ "duration", (guint64) duration,
+ "priority", (guint32) priority, "in-point", (guint64) 0, NULL);
+
+ g_free (uri);
+
+ return ret;
+}
+
+GESClip *
+make_overlay (char *text, guint64 start, guint64 duration, gint priority,
+ guint32 color, gdouble xpos, gdouble ypos)
+{
+ GESClip *ret = GES_CLIP (ges_text_overlay_clip_new ());
+
+ g_object_set (ret,
+ "text", (gchar *) text,
+ "start", (guint64) start,
+ "duration", (guint64) duration,
+ "priority", (guint32) priority,
+ "in-point", (guint64) 0,
+ "color", (guint32) color,
+ "valignment", (gint) GES_TEXT_VALIGN_POSITION,
+ "halignment", (gint) GES_TEXT_HALIGN_POSITION,
+ "xpos", (gdouble) xpos, "ypos", (gdouble) ypos, NULL);
+
+ return ret;
+}
+
+GESPipeline *
+make_timeline (char *path, float duration, char *text, guint32 color,
+ gdouble xpos, gdouble ypos)
+{
+ GESTimeline *timeline;
+ GESTrack *trackv, *tracka;
+ GESLayer *layer1;
+ GESClip *srca;
+ GESClip *overlay;
+ GESPipeline *pipeline;
+ guint64 aduration;
+
+ pipeline = ges_pipeline_new ();
+
+ ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_PREVIEW_VIDEO);
+
+ timeline = ges_timeline_new ();
+ ges_pipeline_set_timeline (pipeline, timeline);
+
+ trackv = GES_TRACK (ges_video_track_new ());
+ ges_timeline_add_track (timeline, trackv);
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+ ges_timeline_add_track (timeline, tracka);
+
+ layer1 = GES_LAYER (ges_layer_new ());
+ g_object_set (layer1, "priority", (gint32) 0, NULL);
+
+ if (!ges_timeline_add_layer (timeline, layer1))
+ exit (-1);
+
+ aduration = (guint64) (duration * GST_SECOND);
+ srca = make_source (path, 0, aduration, 1);
+ overlay = make_overlay (text, 0, aduration, 0, color, xpos, ypos);
+ ges_layer_add_clip (layer1, srca);
+ ges_layer_add_clip (layer1, overlay);
+
+ return pipeline;
+}
+
+int
+main (int argc, char **argv)
+{
+ GError *err = NULL;
+ GOptionContext *ctx;
+ GESPipeline *pipeline;
+ GMainLoop *mainloop;
+ gdouble duration = DEFAULT_DURATION;
+ char *path = NULL, *text;
+ guint64 color;
+ gdouble xpos = DEFAULT_POS, ypos = DEFAULT_POS;
+
+ GOptionEntry options[] = {
+ {"duration", 'd', 0, G_OPTION_ARG_DOUBLE, &duration,
+ "duration of segment", "seconds"},
+ {"path", 'p', 0, G_OPTION_ARG_STRING, &path,
+ "path to file", "path"},
+ {"text", 't', 0, G_OPTION_ARG_STRING, &text,
+ "text to render", "text"},
+ {"color", 'c', 0, G_OPTION_ARG_INT64, &color,
+ "color of the text", "color"},
+ {"xpos", 'x', 0, G_OPTION_ARG_DOUBLE, &xpos,
+ "horizontal position of the text", "color"},
+ {"ypos", 'y', 0, G_OPTION_ARG_DOUBLE, &ypos,
+ "vertical position of the text", "color"},
+ {NULL}
+ };
+
+ ctx = g_option_context_new ("- file segment playback with text overlay");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ exit (1);
+ }
+
+ if (argc > 1) {
+ g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
+ exit (0);
+ }
+
+ g_option_context_free (ctx);
+
+ ges_init ();
+
+ if (path == NULL)
+ g_error ("Must specify --path=/path/to/media/file option\n");
+
+ pipeline = make_timeline (path, duration, text, color, xpos, ypos);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add_seconds ((duration) + 1, (GSourceFunc) g_main_loop_quit,
+ mainloop);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* This example can be found in the GStreamer Editing Services git repository in:
+ * examples/c/play_timeline_with_one_clip.c
+ */
+#include <ges/ges.h>
+
+int
+main (int argc, char **argv)
+{
+ GESLayer *layer;
+ GESTimeline *timeline;
+
+ if (argc == 1) {
+ g_printerr ("Usage: play_timeline_with_one_clip file:///clip/uri\n");
+
+ return 1;
+ }
+
+ gst_init (NULL, NULL);
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_timeline_append_layer (timeline);
+
+ {
+ /* Add a clip with a duration of 5 seconds */
+ GESClip *clip = GES_CLIP (ges_uri_clip_new (argv[1]));
+
+ if (clip == NULL) {
+ g_printerr ("%s can not be used, make sure it is a supported media file",
+ argv[1]);
+
+ return 1;
+ }
+
+ g_object_set (clip, "duration", 5 * GST_SECOND, "start", 0, NULL);
+ ges_layer_add_clip (layer, clip);
+ }
+
+ /* Commiting the timeline is always necessary for changes
+ * inside it to be taken into account by the Non Linear Engine */
+ ges_timeline_commit (timeline);
+
+ {
+ /* Play the timeline */
+ GESPipeline *pipeline = ges_pipeline_new ();
+ GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+ ges_pipeline_set_timeline (pipeline, timeline);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+ /* Simple way to just play the pipeline until EOS or an error pops on the bus */
+ gst_bus_timed_pop_filtered (bus, 10 * GST_SECOND,
+ GST_MESSAGE_EOS | GST_MESSAGE_ERROR);
+
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_object_unref (bus);
+ gst_object_unref (pipeline);
+ }
+
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <ges/ges.h>
+#include <stdlib.h>
+
+int
+main (int argc, gchar ** argv)
+{
+ GError *err = NULL;
+ GOptionContext *ctx;
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESTrack *tracka, *trackv;
+ GESLayer *layer1, *layer2;
+ GESUriClip *src;
+ GMainLoop *mainloop;
+
+ gint inpoint = 0, duration = 10;
+ gboolean mute = FALSE;
+ gchar *audiofile = NULL;
+ GOptionEntry options[] = {
+ {"inpoint", 'i', 0, G_OPTION_ARG_INT, &inpoint,
+ "in-point in the file (in seconds, default:0s)", "seconds"},
+ {"duration", 'd', 0, G_OPTION_ARG_INT, &duration,
+ "duration to use from the file (in seconds, default:10s)", "seconds"},
+ {"mute", 'm', 0, G_OPTION_ARG_NONE, &mute,
+ "Whether to mute the audio from the file",},
+ {"audiofile", 'a', 0, G_OPTION_ARG_FILENAME, &audiofile,
+ "Use this audiofile instead of the original audio from the file",
+ "audiofile"},
+ {NULL}
+ };
+
+ ctx =
+ g_option_context_new
+ ("- Plays an video file with sound (origin/muted/replaced)");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ exit (1);
+ }
+
+ if (argc == 1) {
+ g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
+ exit (0);
+ }
+ g_option_context_free (ctx);
+
+ ges_init ();
+
+ /* Create an Audio/Video pipeline with two layers */
+ pipeline = ges_pipeline_new ();
+
+ timeline = ges_timeline_new ();
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+ trackv = GES_TRACK (ges_video_track_new ());
+
+ layer1 = ges_layer_new ();
+ layer2 = ges_layer_new ();
+ g_object_set (layer2, "priority", 1, NULL);
+
+ if (!ges_timeline_add_layer (timeline, layer1) ||
+ !ges_timeline_add_layer (timeline, layer2) ||
+ !ges_timeline_add_track (timeline, tracka) ||
+ !ges_timeline_add_track (timeline, trackv) ||
+ !ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+ if (1) {
+ gchar *uri = gst_filename_to_uri (argv[1], NULL);
+ /* Add the main audio/video file */
+ src = ges_uri_clip_new (uri);
+ g_free (uri);
+ g_object_set (src, "start", 0, "in-point", inpoint * GST_SECOND,
+ "duration", duration * GST_SECOND, "mute", mute, NULL);
+ ges_layer_add_clip (layer1, GES_CLIP (src));
+ }
+
+ /* Play the pipeline */
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add_seconds (duration + 1, (GSourceFunc) g_main_loop_quit,
+ mainloop);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <ges/ges.h>
+
+/* A simple timeline with 3 audio/video sources */
+int
+main (int argc, gchar ** argv)
+{
+ GESAsset *src_asset;
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESClip *source;
+ GESLayer *layer;
+ GMainLoop *mainloop;
+
+ /* Initialize GStreamer (this will parse environment variables and commandline
+ * arguments. */
+ gst_init (&argc, &argv);
+
+ /* Initialize the GStreamer Editing Services */
+ ges_init ();
+
+ /* Setup of a A/V timeline */
+
+ /* This is our main GESTimeline */
+ timeline = ges_timeline_new_audio_video ();
+
+ /* We are only going to be doing one layer of clips */
+ layer = ges_layer_new ();
+
+ /* Add the tracks and the layer to the timeline */
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+
+ /* We create a simple asset able to extract GESTestClip */
+ src_asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ /* Add sources to our layer */
+ ges_layer_add_asset (layer, src_asset, 0, 0, GST_SECOND,
+ GES_TRACK_TYPE_UNKNOWN);
+ source = ges_layer_add_asset (layer, src_asset, GST_SECOND, 0,
+ GST_SECOND, GES_TRACK_TYPE_UNKNOWN);
+ g_object_set (source, "freq", 480.0, "vpattern", 2, NULL);
+ ges_layer_add_asset (layer, src_asset, 2 * GST_SECOND, 0,
+ GST_SECOND, GES_TRACK_TYPE_UNKNOWN);
+
+
+ /* In order to view our timeline, let's grab a convenience pipeline to put
+ * our timeline in. */
+ pipeline = ges_pipeline_new ();
+
+ /* Add the timeline to that pipeline */
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+ /* The following is standard usage of a GStreamer pipeline (note how you haven't
+ * had to care about GStreamer so far ?).
+ *
+ * We set the pipeline to playing ... */
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+ /* .. and we start a GMainLoop. GES **REQUIRES** a GMainLoop to be running in
+ * order to function properly ! */
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ /* Simple code to have the mainloop shutdown after 4s */
+ g_timeout_add_seconds (4, (GSourceFunc) g_main_loop_quit, mainloop);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <ges/ges.h>
+
+int
+main (int argc, gchar ** argv)
+{
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESTrack *tracka;
+ GESLayer *layer;
+ GMainLoop *mainloop;
+ GstClockTime offset = 0;
+ guint i;
+
+ if (argc < 2) {
+ g_print ("Usage: %s <list of audio files>\n", argv[0]);
+ return -1;
+ }
+
+ /* Initialize GStreamer (this will parse environment variables and commandline
+ * arguments. */
+ gst_init (&argc, &argv);
+
+ /* Initialize the GStreamer Editing Services */
+ ges_init ();
+
+ /* Setup of an audio timeline */
+
+ /* This is our main GESTimeline */
+ timeline = ges_timeline_new ();
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+
+ /* We are only going to be doing one layer of clips */
+ layer = ges_layer_new ();
+
+ /* Add the tracks and the layer to the timeline */
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+ if (!ges_timeline_add_track (timeline, tracka))
+ return -1;
+
+ /* Here we've finished initializing our timeline, we're
+ * ready to start using it... by solely working with the layer ! */
+
+ for (i = 1; i < argc; i++, offset += GST_SECOND) {
+ gchar *uri = gst_filename_to_uri (argv[i], NULL);
+ GESUriClip *src = ges_uri_clip_new (uri);
+
+ g_assert (src);
+ g_free (uri);
+
+ g_object_set (src, "start", offset, "duration", GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) src);
+ }
+
+ /* In order to listen our timeline, let's grab a convenience pipeline to put
+ * our timeline in. */
+ pipeline = ges_pipeline_new ();
+
+ /* Add the timeline to that pipeline */
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+ /* The following is standard usage of a GStreamer pipeline (note how you
+ * haven't had to care about GStreamer so far ?).
+ *
+ * We set the pipeline to playing ... */
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+ /* ... and we start a GMainLoop. GES **REQUIRES** a GMainLoop to be running in
+ * order to function properly ! */
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ /* Simple code to have the mainloop shutdown after 4s */
+ g_timeout_add_seconds (argc - 1, (GSourceFunc) g_main_loop_quit, mainloop);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <ges/ges.h>
+
+int
+main (int argc, gchar ** argv)
+{
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESTrack *tracka;
+ GESLayer *layer;
+ GMainLoop *mainloop;
+ guint i;
+
+ if (argc < 2) {
+ g_print ("Usage: %s <list of audio files>\n", argv[0]);
+ return -1;
+ }
+
+ /* Initialize GStreamer (this will parse environment variables and commandline
+ * arguments. */
+ gst_init (&argc, &argv);
+
+ /* Initialize the GStreamer Editing Services */
+ ges_init ();
+
+ /* Setup of an audio timeline */
+
+ /* This is our main GESTimeline */
+ timeline = ges_timeline_new ();
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+
+ /* We are only going to be doing one layer of clips */
+ layer = ges_layer_new ();
+
+ /* Add the tracks and the layer to the timeline */
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+ if (!ges_timeline_add_track (timeline, tracka))
+ return -1;
+
+ /* Here we've finished initializing our timeline, we're
+ * ready to start using it... by solely working with the layer ! */
+
+ for (i = 1; i < argc; i++) {
+ gchar *uri = gst_filename_to_uri (argv[i], NULL);
+ GESUriClip *src = ges_uri_clip_new (uri);
+
+ g_assert (src);
+ g_free (uri);
+
+ g_object_set (src, "start", ges_layer_get_duration (layer),
+ "duration", GST_SECOND, NULL);
+ ges_layer_add_clip (layer, (GESClip *) src);
+ }
+
+ /* In order to view our timeline, let's grab a convenience pipeline to put
+ * our timeline in. */
+ pipeline = ges_pipeline_new ();
+
+ /* Add the timeline to that pipeline */
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+ /* The following is standard usage of a GStreamer pipeline (note how you haven't
+ * had to care about GStreamer so far ?).
+ *
+ * We set the pipeline to playing ... */
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+ /* .. and we start a GMainLoop. GES **REQUIRES** a GMainLoop to be running in
+ * order to function properly ! */
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ /* Simple code to have the mainloop shutdown after 4s */
+ g_timeout_add_seconds (argc - 1, (GSourceFunc) g_main_loop_quit, mainloop);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <ges/ges.h>
+#include <gst/pbutils/encoding-profile.h>
+
+GstEncodingProfile *make_encoding_profile (gchar * audio, gchar * container);
+
+/* This example will take a series of files and create a audio-only timeline
+ * containing the first second of each file and render it to the output uri
+ * using ogg/vorbis */
+
+/* make_encoding_profile
+ * simple method creating an encoding profile. This is here in
+ * order not to clutter the main function. */
+GstEncodingProfile *
+make_encoding_profile (gchar * audio, gchar * container)
+{
+ GstEncodingContainerProfile *profile;
+ GstEncodingProfile *stream;
+ GstCaps *caps;
+
+ caps = gst_caps_from_string (container);
+ profile =
+ gst_encoding_container_profile_new ((gchar *) "ges-test4", NULL, caps,
+ NULL);
+ gst_caps_unref (caps);
+
+ caps = gst_caps_from_string (audio);
+ stream = (GstEncodingProfile *)
+ gst_encoding_audio_profile_new (caps, NULL, NULL, 0);
+ gst_encoding_container_profile_add_profile (profile, stream);
+ gst_caps_unref (caps);
+
+ return (GstEncodingProfile *) profile;
+}
+
+int
+main (int argc, gchar ** argv)
+{
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESTrack *tracka;
+ GESLayer *layer;
+ GMainLoop *mainloop;
+ GstEncodingProfile *profile;
+ gchar *container = (gchar *) "application/ogg";
+ gchar *audio = (gchar *) "audio/x-vorbis";
+ gchar *output_uri;
+ guint i;
+ GError *err = NULL;
+ GOptionEntry options[] = {
+ {"format", 'f', 0, G_OPTION_ARG_STRING, &container,
+ "Container format", "<GstCaps>"},
+ {"aformat", 'a', 0, G_OPTION_ARG_STRING, &audio,
+ "Audio format", "<GstCaps>"},
+ {NULL}
+ };
+ GOptionContext *ctx;
+
+ ctx = g_option_context_new ("- renders a sequence of audio files.");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_printerr ("Error initializing: %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ return -1;
+ }
+ g_option_context_free (ctx);
+
+ if (argc < 3) {
+ g_print ("Usage: %s <output uri> <list of audio files>\n", argv[0]);
+ return -1;
+ }
+
+ /* Initialize GStreamer (this will parse environment variables and commandline
+ * arguments. */
+ gst_init (&argc, &argv);
+
+ /* Initialize the GStreamer Editing Services */
+ ges_init ();
+
+ /* Setup of an audio timeline */
+
+ /* This is our main GESTimeline */
+ timeline = ges_timeline_new ();
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+
+ /* We are only going to be doing one layer of clips */
+ layer = ges_layer_new ();
+
+ /* Add the tracks and the layer to the timeline */
+ if (!ges_timeline_add_layer (timeline, layer))
+ return -1;
+ if (!ges_timeline_add_track (timeline, tracka))
+ return -1;
+
+ /* Here we've finished initializing our timeline, we're
+ * ready to start using it... by solely working with the layer ! */
+
+ for (i = 2; i < argc; i++) {
+ gchar *uri = gst_filename_to_uri (argv[i], NULL);
+ GESUriClip *src = ges_uri_clip_new (uri);
+
+ g_assert (src);
+ g_free (uri);
+
+ g_object_set (src, "start", ges_layer_get_duration (layer),
+ "duration", GST_SECOND, NULL);
+ /* Since we're using a GESSimpleLayer, objects will be automatically
+ * appended to the end of the layer */
+ ges_layer_add_clip (layer, (GESClip *) src);
+ }
+
+ /* In order to view our timeline, let's grab a convenience pipeline to put
+ * our timeline in. */
+ pipeline = ges_pipeline_new ();
+
+ /* Add the timeline to that pipeline */
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return -1;
+
+
+ /* RENDER SETTINGS ! */
+ /* We set our output URI and rendering setting on the pipeline */
+ if (gst_uri_is_valid (argv[1])) {
+ output_uri = g_strdup (argv[1]);
+ } else if (g_file_test (argv[1], G_FILE_TEST_EXISTS)) {
+ output_uri = gst_filename_to_uri (argv[1], NULL);
+ } else {
+ g_printerr ("Unrecognised command line argument '%s'.\n"
+ "Please pass an URI or file as argument!\n", argv[1]);
+ return -1;
+ }
+ profile = make_encoding_profile (audio, container);
+ if (!ges_pipeline_set_render_settings (pipeline, output_uri, profile))
+ return -1;
+
+ /* We want the pipeline to render (without any preview) */
+ if (!ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_SMART_RENDER))
+ return -1;
+
+
+ /* The following is standard usage of a GStreamer pipeline (note how you haven't
+ * had to care about GStreamer so far ?).
+ *
+ * We set the pipeline to playing ... */
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+ /* ... and we start a GMainLoop. GES **REQUIRES** a GMainLoop to be running in
+ * order to function properly ! */
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ /* Simple code to have the mainloop shutdown after 4s */
+ /* FIXME : We should wait for EOS ! */
+ g_timeout_add_seconds (argc - 1, (GSourceFunc) g_main_loop_quit, mainloop);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon@alum.berkeley.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <ges/ges.h>
+
+typedef struct
+{
+ int type;
+ char *name;
+} transition_type;
+
+GESClip *make_source (char *path, guint64 start, guint64 duration,
+ gint priority, gchar * text);
+
+GESPipeline *make_timeline (char *path, float duration, char *text);
+
+GESClip *
+make_source (char *path, guint64 start, guint64 duration, gint priority,
+ gchar * text)
+{
+ gchar *uri = gst_filename_to_uri (path, NULL);
+
+ GESClip *ret = GES_CLIP (ges_uri_clip_new (uri));
+
+ g_object_set (ret,
+ "start", (guint64) start,
+ "duration", (guint64) duration,
+ "priority", (guint32) priority, "in-point", (guint64) 0,
+ "text", text, NULL);
+
+ g_free (uri);
+
+ return ret;
+}
+
+GESPipeline *
+make_timeline (char *path, float duration, char *text)
+{
+ GESTimeline *timeline;
+ GESTrack *trackv, *tracka;
+ GESLayer *layer1;
+ GESClip *srca;
+ GESPipeline *pipeline;
+ guint64 aduration;
+
+ pipeline = ges_pipeline_new ();
+
+ ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_PREVIEW_VIDEO);
+
+ timeline = ges_timeline_new ();
+ ges_pipeline_set_timeline (pipeline, timeline);
+
+ trackv = GES_TRACK (ges_video_track_new ());
+ ges_timeline_add_track (timeline, trackv);
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+ ges_timeline_add_track (timeline, tracka);
+
+ layer1 = GES_LAYER (ges_layer_new ());
+ g_object_set (layer1, "priority", (gint32) 0, NULL);
+
+ if (!ges_timeline_add_layer (timeline, layer1))
+ exit (-1);
+
+ aduration = (guint64) (duration * GST_SECOND);
+ srca = make_source (path, 0, aduration, 1, text);
+ ges_layer_add_clip (layer1, srca);
+
+ return pipeline;
+}
+
+int
+main (int argc, char **argv)
+{
+ GError *err = NULL;
+ GOptionContext *ctx;
+ GESPipeline *pipeline;
+ GMainLoop *mainloop;
+ gdouble duration;
+ char *path, *text;
+
+ GOptionEntry options[] = {
+ {"duration", 'd', 0, G_OPTION_ARG_DOUBLE, &duration,
+ "duration of transition", "seconds"},
+ {"path", 'p', 0, G_OPTION_ARG_STRING, &path,
+ "path to file", "path"},
+ {"text", 't', 0, G_OPTION_ARG_STRING, &text,
+ "text to render", "text"},
+ {NULL}
+ };
+
+ ctx = g_option_context_new ("- transition between two media files");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ exit (1);
+ }
+
+ if (argc > 1) {
+ g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
+ exit (0);
+ }
+
+ g_option_context_free (ctx);
+
+ ges_init ();
+
+ pipeline = make_timeline (path, duration, text);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add_seconds ((duration) + 1, (GSourceFunc) g_main_loop_quit,
+ mainloop);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <ges/ges.h>
+#include <gst/pbutils/encoding-profile.h>
+
+/* GLOBAL VARIABLE */
+static guint repeat = 0;
+GESPipeline *pipeline = NULL;
+
+static gboolean thumbnail_cb (gpointer pipeline);
+
+#define TEST_PATH "test_thumbnail.jpg"
+
+static gboolean
+thumbnail_cb (gpointer user)
+{
+ GstSample *b = NULL;
+ GstCaps *caps;
+ GESPipeline *p;
+
+ p = GES_PIPELINE (user);
+
+ caps = gst_caps_from_string ("image/jpeg");
+ GST_INFO ("getting thumbnails");
+
+ /* check raw rgb use-case with scaling */
+ b = ges_pipeline_get_thumbnail_rgb24 (p, 320, 240);
+ g_assert (b);
+ gst_sample_unref (b);
+
+ /* check encoding use-case from caps */
+ b = NULL;
+ b = ges_pipeline_get_thumbnail (p, caps);
+ g_assert (b);
+ gst_sample_unref (b);
+
+ g_assert (ges_pipeline_save_thumbnail (p, -1, -1, (gchar *)
+ "image/jpeg", (gchar *) TEST_PATH, NULL));
+ g_assert (g_file_test (TEST_PATH, G_FILE_TEST_EXISTS));
+ g_unlink (TEST_PATH);
+
+ gst_caps_unref (caps);
+ return FALSE;
+}
+
+static GESPipeline *
+create_timeline (void)
+{
+ GESPipeline *pipeline;
+ GESLayer *layer;
+ GESTrack *tracka, *trackv;
+ GESTimeline *timeline;
+ GESClip *src;
+
+ timeline = ges_timeline_new ();
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+ trackv = GES_TRACK (ges_video_track_new ());
+
+ layer = ges_layer_new ();
+
+ /* Add the tracks and the layer to the timeline */
+ if (!ges_timeline_add_layer (timeline, layer) ||
+ !ges_timeline_add_track (timeline, tracka) ||
+ !ges_timeline_add_track (timeline, trackv))
+ return NULL;
+
+ /* Add the main audio/video file */
+ src = GES_CLIP (ges_test_clip_new ());
+ g_object_set (src,
+ "vpattern", GES_VIDEO_TEST_PATTERN_SNOW,
+ "start", 0, "duration", 10 * GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, GES_CLIP (src));
+
+ pipeline = ges_pipeline_new ();
+
+ if (!ges_pipeline_set_timeline (pipeline, timeline))
+ return NULL;
+
+ return pipeline;
+}
+
+static void
+bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ g_print ("ERROR\n");
+ g_main_loop_quit (mainloop);
+ break;
+ case GST_MESSAGE_EOS:
+ if (repeat > 0) {
+ g_print ("Looping again\n");
+ /* No need to change state before */
+ gst_element_seek_simple (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH, 0);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ repeat -= 1;
+ } else {
+ g_print ("Done\n");
+ g_main_loop_quit (mainloop);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+int
+main (int argc, gchar ** argv)
+{
+ GError *err = NULL;
+ GOptionEntry options[] = {
+ {NULL}
+ };
+ GOptionContext *ctx;
+ GMainLoop *mainloop;
+ GstBus *bus;
+
+ ctx = g_option_context_new ("tests thumbnail supoprt (produces no output)");
+ g_option_context_set_summary (ctx, "");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing: %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ exit (1);
+ }
+
+ g_option_context_free (ctx);
+ /* Initialize the GStreamer Editing Services */
+ ges_init ();
+
+ /* Create the pipeline */
+ pipeline = create_timeline ();
+ if (!pipeline)
+ exit (-1);
+
+ ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_PREVIEW);
+
+ /* Play the pipeline */
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ g_print ("thumbnailing every 1 seconds\n");
+ g_timeout_add (1000, thumbnail_cb, pipeline);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
+
+ if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
+ g_print ("Failed to start the encoding\n");
+ return 1;
+ }
+ g_main_loop_run (mainloop);
+
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon@alum.berkeley.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <ges/ges.h>
+#include <stdlib.h>
+
+typedef struct
+{
+ int type;
+ char *name;
+} transition_type;
+
+GESClip *make_source (gchar * path, guint64 start, guint64 inpoint,
+ guint64 duration, gint priority);
+
+gboolean print_transition_data (GESClip * tr);
+
+GESPipeline *make_timeline (gchar * nick, double tdur, gchar * patha,
+ gfloat adur, gdouble ainpoint, gchar * pathb, gfloat bdur,
+ gdouble binpoint);
+
+GESClip *
+make_source (gchar * path, guint64 start, guint64 duration, guint64 inpoint,
+ gint priority)
+{
+ gchar *uri = gst_filename_to_uri (path, NULL);
+
+ GESClip *ret = GES_CLIP (ges_uri_clip_new (uri));
+
+ g_object_set (ret,
+ "start", (guint64) start,
+ "duration", (guint64) duration,
+ "priority", (guint32) priority, "in-point", (guint64) inpoint, NULL);
+
+ g_free (uri);
+
+ return ret;
+}
+
+gboolean
+print_transition_data (GESClip * tr)
+{
+ GESTrackElement *trackelement;
+ GstElement *nleobj;
+ guint64 start, duration;
+ gint priority;
+ char *name;
+ GList *trackelements;
+
+ if (!tr)
+ return FALSE;
+
+ if (!(trackelements = GES_CONTAINER_CHILDREN (tr)))
+ return FALSE;
+ if (!(trackelement = GES_TRACK_ELEMENT (trackelements->data)))
+ return FALSE;
+ if (!(nleobj = ges_track_element_get_nleobject (trackelement)))
+ return FALSE;
+
+ g_object_get (nleobj, "start", &start, "duration", &duration,
+ "priority", &priority, "name", &name, NULL);
+ g_print ("nleobject for %s: %f %f %d\n", name,
+ ((gfloat) start) / GST_SECOND,
+ ((gfloat) duration) / GST_SECOND, priority);
+
+ return FALSE;
+}
+
+GESPipeline *
+make_timeline (gchar * nick, gdouble tdur, gchar * patha, gfloat adur,
+ gdouble ainp, gchar * pathb, gfloat bdur, gdouble binp)
+{
+ GESTimeline *timeline;
+ GESTrack *trackv, *tracka;
+ GESLayer *layer1;
+ GESClip *srca, *srcb;
+ GESPipeline *pipeline;
+ guint64 aduration, bduration, tduration, tstart, ainpoint, binpoint;
+ GESTransitionClip *tr = NULL;
+
+ pipeline = ges_pipeline_new ();
+
+ ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_PREVIEW_VIDEO);
+
+ timeline = ges_timeline_new ();
+ ges_pipeline_set_timeline (pipeline, timeline);
+
+ trackv = GES_TRACK (ges_video_track_new ());
+ ges_timeline_add_track (timeline, trackv);
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+ ges_timeline_add_track (timeline, tracka);
+
+ layer1 = GES_LAYER (ges_layer_new ());
+ g_object_set (layer1, "priority", (gint32) 0, NULL);
+
+ if (!ges_timeline_add_layer (timeline, layer1))
+ exit (-1);
+
+ aduration = (guint64) (adur * GST_SECOND);
+ bduration = (guint64) (bdur * GST_SECOND);
+ tduration = (guint64) (tdur * GST_SECOND);
+ ainpoint = (guint64) (ainp * GST_SECOND);
+ binpoint = (guint64) (binp * GST_SECOND);
+ tstart = aduration - tduration;
+ srca = make_source (patha, 0, aduration, ainpoint, 1);
+ srcb = make_source (pathb, tstart, bduration, binpoint, 2);
+ ges_layer_add_clip (layer1, srca);
+ ges_layer_add_clip (layer1, srcb);
+ g_timeout_add_seconds (1, (GSourceFunc) print_transition_data, srca);
+ g_timeout_add_seconds (1, (GSourceFunc) print_transition_data, srcb);
+
+ if (tduration != 0) {
+ g_print ("creating transition at %" GST_TIME_FORMAT " of %f duration (%"
+ GST_TIME_FORMAT ")\n", GST_TIME_ARGS (tstart), tdur,
+ GST_TIME_ARGS (tduration));
+ if (!(tr = ges_transition_clip_new_for_nick (nick)))
+ g_error ("invalid transition type %s\n", nick);
+
+ g_object_set (tr,
+ "start", (guint64) tstart,
+ "duration", (guint64) tduration, "in-point", (guint64) 0, NULL);
+ ges_layer_add_clip (layer1, GES_CLIP (tr));
+ g_timeout_add_seconds (1, (GSourceFunc) print_transition_data, tr);
+ }
+
+ return pipeline;
+}
+
+int
+main (int argc, char **argv)
+{
+ GError *err = NULL;
+ GOptionContext *ctx;
+ GESPipeline *pipeline;
+ GMainLoop *mainloop;
+ gchar *type = (gchar *) "crossfade";
+ gchar *patha, *pathb;
+ gdouble adur, bdur, tdur, ainpoint, binpoint;
+
+ GOptionEntry options[] = {
+ {"type", 't', 0, G_OPTION_ARG_STRING, &type,
+ "type of transition to create", "<smpte-transition>"},
+ {"duration", 'd', 0, G_OPTION_ARG_DOUBLE, &tdur,
+ "duration of transition", "seconds"},
+ {NULL}
+ };
+
+ ctx = g_option_context_new ("- transition between two media files");
+ g_option_context_set_summary (ctx,
+ "Select two files, and optionally a transition duration and type.\n"
+ "A file is a triplet of filename, inpoint (in seconds) and duration (in seconds).\n"
+ "Example:\n" "transition file1.avi 0 5 file2.avi 25 5 -d 2 -t crossfade");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing %s\n", err->message);
+ g_option_context_free (ctx);
+ g_clear_error (&err);
+ exit (1);
+ }
+
+ if (argc < 4) {
+ g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
+ exit (0);
+ }
+
+ g_option_context_free (ctx);
+
+ ges_init ();
+
+ patha = argv[1];
+ ainpoint = (gdouble) atof (argv[2]);
+ adur = (gdouble) atof (argv[3]);
+ pathb = argv[4];
+ binpoint = (gdouble) atof (argv[5]);
+ bdur = (gdouble) atof (argv[6]);
+
+ pipeline =
+ make_timeline (type, tdur, patha, adur, ainpoint, pathb, bdur, binpoint);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add_seconds ((adur + bdur) + 1, (GSourceFunc) g_main_loop_quit,
+ mainloop);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
--- /dev/null
+subdir('c')
--- /dev/null
+import sys
+import gi
+
+gi.require_version('Gst', '1.0')
+gi.require_version('GES', '1.0')
+gi.require_version('GstPlayer', '1.0')
+gi.require_version('GLib', '2.0')
+
+from gi.repository import Gst, GES, GLib, GstPlayer
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("You must specify a file URI")
+ sys.exit(-1)
+
+ Gst.init(None)
+ GES.init()
+
+ timeline = GES.Timeline.new_audio_video()
+ layer = timeline.append_layer()
+ start = 0
+ for uri in sys.argv[1:]:
+ if not Gst.uri_is_valid(uri):
+ uri = Gst.filename_to_uri(uri)
+
+ clip = GES.UriClip.new(uri)
+ clip.props.start = start
+ layer.add_clip(clip)
+
+ start += clip.props.duration
+
+ player = GstPlayer
+ player = GstPlayer.Player.new(None, GstPlayer.PlayerGMainContextSignalDispatcher.new(None))
+ player.set_uri("ges://")
+ player.get_pipeline().connect("source-setup",
+ lambda playbin, source: source.set_property("timeline", timeline))
+
+ loop = GLib.MainLoop()
+ player.connect("end-of-stream", lambda x: loop.quit())
+
+ def error(player, err):
+ loop.quit()
+ print("Got error: %s" % err)
+ sys.exit(1)
+
+ player.connect("error", error)
+ player.play()
+ loop.run()
--- /dev/null
+#!/usr/bin/env python3
+#
+# GStreamer
+#
+# Copyright (C) 2019 Thibault Saunier <tsaunier@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 Street, Suite 500,
+# Boston, MA 02110-1335, USA.
+
+import gi
+import sys
+
+gi.require_version('Gst', '1.0')
+gi.require_version('GES', '1.0')
+gi.require_version('GstController', '1.0')
+
+from gi.repository import Gst, GES, GLib, GstController # noqa
+
+Gst.init(None)
+GES.init()
+
+
+def play_timeline(timeline):
+ pipeline = GES.Pipeline()
+ pipeline.set_timeline(timeline)
+ bus = pipeline.get_bus()
+ bus.add_signal_watch()
+ loop = GLib.MainLoop()
+ bus.connect("message", bus_message_cb, loop, pipeline)
+ pipeline.set_state(Gst.State.PLAYING)
+
+ loop.run()
+
+def bus_message_cb(unused_bus, message, loop, pipeline):
+ if message.type == Gst.MessageType.EOS:
+ print("eos")
+ pipeline.set_state(Gst.State.NULL)
+ loop.quit()
+ elif message.type == Gst.MessageType.ERROR:
+ error = message.parse_error()
+ pipeline.set_state(Gst.State.NULL)
+ print("error %s" % error[1])
+ loop.quit()
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print("You must specify a file path")
+ exit(-1)
+
+ timeline = GES.Timeline.new_audio_video()
+
+ layer = timeline.append_layer()
+ clip = GES.UriClip.new(Gst.filename_to_uri(sys.argv[1]))
+
+ # Adding clip to the layer so the TrackElements are created
+ layer.add_clip(clip)
+
+ # Create an InterpolationControlSource and make sure it interpolates linearly
+ control_source = GstController.InterpolationControlSource.new()
+ control_source.props.mode = GstController.InterpolationMode.LINEAR
+
+ # Set the keyframes
+ control_source.set(0, 0.0) # Fully transparent at 0 second
+ control_source.set(Gst.SECOND, 1.0) # Fully opaque at 1 second
+
+ # Get the video source
+ video_source = clip.find_track_element(None, GES.VideoSource)
+ assert(video_source)
+
+ # And set the control source on the "alpha" property of the video source
+ # Using a "direct" binding but "direct-absolute" would work the exact
+ # same way as the alpha property range is [0.0 - 1.0] anyway.
+ video_source.set_control_source(control_source, "alpha", "direct")
+
+ play_timeline(timeline)
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python3
+#
+# GStreamer
+#
+# Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+# Boston, MA 02110-1335, USA.
+
+import os
+import gi
+
+gi.require_version('Gst', '1.0')
+gi.require_version('GES', '1.0')
+
+from gi.repository import Gst, GES, GLib # noqa
+
+
+class Simple:
+ def __init__(self, uri):
+ timeline = GES.Timeline.new_audio_video()
+ self.project = timeline.get_asset()
+
+ self.project.connect("asset-added", self._asset_added_cb)
+ self.project.connect("error-loading-asset", self._error_loading_asset_cb)
+ self.project.create_asset(uri, GES.UriClip)
+ self.layer = timeline.append_layer()
+ self._create_pipeline(timeline)
+ self.loop = GLib.MainLoop()
+
+ def _create_pipeline(self, timeline):
+ self.pipeline = GES.Pipeline()
+ self.pipeline.set_timeline(timeline)
+ bus = self.pipeline.get_bus()
+ bus.add_signal_watch()
+ bus.connect("message", self.bus_message_cb)
+
+ def bus_message_cb(self, unused_bus, message):
+ if message.type == Gst.MessageType.EOS:
+ print("eos")
+ self.loop.quit()
+ elif message.type == Gst.MessageType.ERROR:
+ error = message.parse_error()
+ print("error %s" % error[1])
+ self.loop.quit()
+
+ def start(self):
+ self.loop.run()
+
+ def _asset_added_cb(self, project, asset):
+ self.layer.add_asset(asset, 0, 0, Gst.SECOND * 5, GES.TrackType.UNKNOWN)
+ self.pipeline.set_state(Gst.State.PLAYING)
+
+ def _error_loading_asset_cb(self, project, error, asset_id, type):
+ print("Could not load asset %s: %s" % (asset_id, error))
+ self.loop.quit()
+
+if __name__ == "__main__":
+ if len(os.sys.argv) != 2:
+ print("You must specify a file URI")
+ exit(-1)
+
+ Gst.init(None)
+ GES.init()
+ simple = Simple(os.sys.argv[1])
+ simple.start()
--- /dev/null
+*.gir
+*.typelib
--- /dev/null
+built_header_make =
+built_source_make =
+
+lib_LTLIBRARIES = libges-@GST_API_VERSION@.la
+
+
+EXTRA_libges_@GST_API_VERSION@_la_SOURCES = gesmarshal.list
+
+EXTRA_DIST=parse.l
+
+CLEANFILES = $(BUILT_SOURCES) $(built_header_make) $(built_source_make) *.gcno *.gcda *.gcov *.gcov.out
+
+nodist_libges_@GST_API_VERSION@_la_SOURCES = lex.priv_ges_parse_yy.c ges-parse-lex.h
+
+
+libges_@GST_API_VERSION@_la_SOURCES = \
+ $(built_source_make) \
+ ges.c \
+ ges-enums.c \
+ ges-meta-container.c \
+ ges-timeline.c \
+ ges-layer.c \
+ ges-clip.c \
+ ges-pipeline.c \
+ ges-source-clip.c \
+ ges-base-effect-clip.c \
+ ges-effect-clip.c \
+ ges-uri-clip.c \
+ ges-operation-clip.c \
+ ges-base-transition-clip.c \
+ ges-transition-clip.c \
+ ges-test-clip.c \
+ ges-title-clip.c \
+ ges-overlay-clip.c \
+ ges-text-overlay-clip.c \
+ ges-track.c \
+ ges-audio-track.c \
+ ges-video-track.c \
+ ges-track-element.c \
+ ges-source.c \
+ ges-operation.c \
+ ges-video-source.c \
+ ges-audio-source.c \
+ ges-video-uri-source.c \
+ ges-audio-uri-source.c \
+ ges-image-source.c \
+ ges-multi-file-source.c \
+ ges-transition.c \
+ ges-audio-transition.c \
+ ges-video-transition.c \
+ ges-video-test-source.c \
+ ges-audio-test-source.c \
+ ges-title-source.c \
+ ges-text-overlay.c \
+ ges-base-effect.c \
+ ges-effect.c \
+ ges-screenshot.c \
+ ges-formatter.c \
+ ges-pitivi-formatter.c \
+ ges-asset.c \
+ ges-uri-asset.c \
+ ges-clip-asset.c \
+ ges-track-element-asset.c \
+ ges-extractable.c \
+ ges-project.c \
+ ges-base-xml-formatter.c \
+ ges-xml-formatter.c \
+ ges-command-line-formatter.c \
+ ges-auto-transition.c \
+ ges-timeline-element.c \
+ ges-timeline-tree.c \
+ ges-container.c \
+ ges-effect-asset.c \
+ ges-smart-adder.c \
+ ges-smart-video-mixer.c \
+ ges-utils.c \
+ ges-group.c \
+ ges-validate.c \
+ ges-structured-interface.c \
+ ges-structure-parser.c \
+ gstframepositioner.c
+
+libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/
+libges_@GST_API_VERSION@include_HEADERS = \
+ $(built_header_make) \
+ ges-types.h \
+ ges.h \
+ ges-prelude.h \
+ ges-enums.h \
+ ges-gerror.h \
+ ges-meta-container.h \
+ ges-timeline.h \
+ ges-layer.h \
+ ges-clip.h \
+ ges-pipeline.h \
+ ges-source-clip.h \
+ ges-uri-clip.h \
+ ges-base-effect-clip.h \
+ ges-effect-clip.h \
+ ges-operation-clip.h \
+ ges-base-transition-clip.h \
+ ges-transition-clip.h \
+ ges-test-clip.h \
+ ges-title-clip.h \
+ ges-overlay-clip.h \
+ ges-text-overlay-clip.h \
+ ges-base-effect.h \
+ ges-effect.h \
+ ges-track.h \
+ ges-audio-track.h \
+ ges-video-track.h \
+ ges-track-element.h \
+ ges-source.h \
+ ges-operation.h \
+ ges-video-source.h \
+ ges-audio-source.h \
+ ges-video-uri-source.h \
+ ges-audio-uri-source.h \
+ ges-image-source.h \
+ ges-multi-file-source.h \
+ ges-transition.h \
+ ges-audio-transition.h \
+ ges-video-transition.h \
+ ges-video-test-source.h \
+ ges-audio-test-source.h \
+ ges-title-source.h \
+ ges-text-overlay.h \
+ ges-screenshot.h \
+ ges-formatter.h \
+ ges-pitivi-formatter.h \
+ ges-asset.h \
+ ges-uri-asset.h \
+ ges-clip-asset.h \
+ ges-track-element-asset.h \
+ ges-extractable.h \
+ ges-project.h \
+ ges-base-xml-formatter.h \
+ ges-xml-formatter.h \
+ ges-command-line-formatter.h \
+ ges-timeline-element.h \
+ ges-container.h \
+ ges-effect-asset.h \
+ ges-utils.h \
+ ges-group.h \
+ ges-version.h
+
+noinst_HEADERS = \
+ ges-internal.h \
+ ges-auto-transition.h \
+ ges-structured-interface.h \
+ ges-structure-parser.h \
+ ges-smart-video-mixer.h \
+ ges-smart-adder.h \
+ ges-timeline-tree.h \
+ gstframepositioner.h
+
+libges_@GST_API_VERSION@_la_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) \
+ $(GST_VIDEO_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_CFLAGS) $(XML_CFLAGS) $(GIO_CFLAGS) $(GST_VALIDATE_CFLAGS) \
+ -DG_LOG_DOMAIN=\"GES\" -DBUILDING_GES
+libges_@GST_API_VERSION@_la_LIBADD = $(GST_PBUTILS_LIBS) \
+ $(GST_VIDEO_LIBS) $(GST_CONTROLLER_LIBS) $(GST_PLUGINS_BASE_LIBS) \
+ $(GST_BASE_LIBS) $(GST_LIBS) $(XML_LIBS) $(GIO_LIBS) $(GST_VALIDATE_LIBS)
+libges_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \
+ $(GST_LT_LDFLAGS) $(GIO_CFLAGS) $(GST_VALIDATE_CFLAGS)
+
+DISTCLEANFILE = $(CLEANFILES)
+
+#files built on make all/check/instal
+BUILT_SOURCES = \
+ $(built_header_make) \
+ $(built_source_make) \
+ lex.priv_ges_parse_yy.c \
+ ges-parse-lex.h
+
+include $(top_srcdir)/common/gst-glib-gen.mak
+
+if HAVE_INTROSPECTION
+BUILT_GIRSOURCES = GES-@GST_API_VERSION@.gir
+
+gir_headers_temp=$(patsubst %,$(srcdir)/%, $(libges_@GST_API_VERSION@include_HEADERS))
+gir_headers=$(subst $(srcdir)/ges-version.h,$(builddir)/ges-version.h,$(gir_headers_temp))
+gir_sources=$(patsubst %,$(srcdir)/%, $(libges_@GST_API_VERSION@_la_SOURCES))
+
+GES-@GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libges-@GST_API_VERSION@.la
+ $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \
+ CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" CC="$(CC)" PKG_CONFIG="$(PKG_CONFIG)" DLLTOOL="$(DLLTOOL)" \
+ $(INTROSPECTION_SCANNER) -v --namespace GES \
+ --nsversion=@GST_API_VERSION@ \
+ --identifier-prefix=GES \
+ --symbol-prefix=ges \
+ --warn-all \
+ --c-include='ges/ges.h' \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-pbutils-@GST_API_VERSION@` \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-audio-@GST_API_VERSION@` \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-video-@GST_API_VERSION@` \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-tag-@GST_API_VERSION@` \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_API_VERSION@` \
+ --add-include-path=`$(PKG_CONFIG) --variable=girdir gio-2.0` \
+ --library=libges-@GST_API_VERSION@.la \
+ --include=Gst-@GST_API_VERSION@ \
+ --include=GstVideo-@GST_API_VERSION@ \
+ --include=GstPbutils-@GST_API_VERSION@ \
+ --include=Gio-2.0 \
+ --libtool="$(top_builddir)/libtool" \
+ --pkg gstreamer-@GST_API_VERSION@ \
+ --pkg gstreamer-pbutils-@GST_API_VERSION@ \
+ --pkg gstreamer-controller-@GST_API_VERSION@ \
+ --pkg gio-2.0 \
+ --pkg-export gst-editing-services-@GST_API_VERSION@ \
+ --add-init-section="$(INTROSPECTION_INIT)" \
+ --add-init-section="extern gboolean ges_init(void); ges_init();" \
+ --output $@ \
+ $(gir_headers) \
+ $(gir_sources)
+
+# INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to
+# install anything - we need to install inside our prefix.
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(BUILT_GIRSOURCES)
+
+typelibsdir = $(libdir)/girepository-1.0/
+
+typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib)
+
+%.typelib: %.gir $(INTROSPECTION_COMPILER)
+ $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \
+ $(INTROSPECTION_COMPILER) \
+ --includedir=$(srcdir) \
+ --includedir=$(srcdir)/../video \
+ --includedir=$(builddir) \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-pbutils-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-audio-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-video-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-tag-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-controller-@GST_API_VERSION@` \
+ --includedir=`$(PKG_CONFIG) --variable=girdir gio-2.0` \
+ $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F)
+
+CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA)
+endif
+
+%.c.gcov: .libs/libges_@GST_API_VERSION@_la-%.gcda %.c
+ $(GCOV) -b -f -o $^ > $@.out
+
+gcov: $(libges_@GST_API_VERSION@_la_SOURCES:=.gcov)
+
+lex.priv_ges_parse_yy.c ges-parse-lex.h: parse.l
+ $(AM_V_GEN)$(FLEX_PATH) --header-file=ges-parse-lex.h -Ppriv_ges_parse_yy $^
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012-2015 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION: gesasset
+ * @title: GESAsset
+ * @short_description: Represents usable resources inside the GStreamer Editing Services
+ *
+ * The Assets in the GStreamer Editing Services represent the resources
+ * that can be used. You can create assets for any type that implements the #GESExtractable
+ * interface, for example #GESClips, #GESFormatter, and #GESTrackElement do implement it.
+ * This means that assets will represent for example a #GESUriClips, #GESBaseEffect etc,
+ * and then you can extract objects of those types with the appropriate parameters from the asset
+ * using the #ges_asset_extract method:
+ *
+ * |[
+ * GESAsset *effect_asset;
+ * GESEffect *effect;
+ *
+ * // You create an asset for an effect
+ * effect_asset = ges_asset_request (GES_TYPE_EFFECT, "agingtv", NULL);
+ *
+ * // And now you can extract an instance of GESEffect from that asset
+ * effect = GES_EFFECT (ges_asset_extract (effect_asset));
+ *
+ * ]|
+ *
+ * In that example, the advantages of having a #GESAsset are that you can know what effects
+ * you are working with and let your user know about the avalaible ones, you can add metadata
+ * to the #GESAsset through the #GESMetaContainer interface and you have a model for your
+ * custom effects. Note that #GESAsset management is making easier thanks to the #GESProject class.
+ *
+ * Each asset is represented by a pair of @extractable_type and @id (string). Actually the @extractable_type
+ * is the type that implements the #GESExtractable interface, that means that for example for a #GESUriClip,
+ * the type that implements the #GESExtractable interface is #GESClip.
+ * The identifier represents different things depending on the @extractable_type and you should check
+ * the documentation of each type to know what the ID of #GESAsset actually represents for that type. By default,
+ * we only have one #GESAsset per type, and the @id is the name of the type, but this behaviour is overriden
+ * to be more useful. For example, for GESTransitionClips, the ID is the vtype of the transition
+ * you will extract from it (ie crossfade, box-wipe-rc etc..) For #GESEffect the ID is the
+ * @bin-description property of the extracted objects (ie the gst-launch style description of the bin that
+ * will be used).
+ *
+ * Each and every #GESAsset is cached into GES, and you can query those with the #ges_list_assets function.
+ * Also the system will automatically register #GESAssets for #GESFormatters and #GESTransitionClips
+ * and standard effects (actually not implemented yet) and you can simply query those calling:
+ * |[
+ * GList *formatter_assets, *tmp;
+ *
+ * // List all the transitions
+ * formatter_assets = ges_list_assets (GES_TYPE_FORMATTER);
+ *
+ * // Print some infos about the formatter GESAsset
+ * for (tmp = formatter_assets; tmp; tmp = tmp->next) {
+ * g_print ("Name of the formatter: %s, file extension it produces: %s",
+ * ges_meta_container_get_string (GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_NAME),
+ * ges_meta_container_get_string (GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_EXTENSION));
+ * }
+ *
+ * g_list_free (transition_assets);
+ *
+ * ]|
+ *
+ * You can request the creation of #GESAssets using either ges_asset_request() or
+ * ges_asset_request_async(). All the #GESAssets are cached and thus any asset that has already
+ * been created can be requested again without overhead.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges.h"
+#include "ges-internal.h"
+
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <gst/gst.h>
+
+GST_DEBUG_CATEGORY_STATIC (ges_asset_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT ges_asset_debug
+
+enum
+{
+ PROP_0,
+ PROP_TYPE,
+ PROP_ID,
+ PROP_PROXY,
+ PROP_PROXY_TARGET,
+ PROP_LAST
+};
+
+typedef enum
+{
+ ASSET_NOT_INITIALIZED,
+ ASSET_INITIALIZING, ASSET_INITIALIZED_WITH_ERROR,
+ ASSET_PROXIED,
+ ASSET_NEEDS_RELOAD,
+ ASSET_INITIALIZED
+} GESAssetState;
+
+static GParamSpec *_properties[PROP_LAST];
+
+struct _GESAssetPrivate
+{
+ gchar *id;
+ GESAssetState state;
+ GType extractable_type;
+
+ /* When an asset is proxied, instantiating it will
+ * return the asset it points to */
+ char *proxied_asset_id;
+
+ GList *proxies;
+ GESAsset *proxy_target;
+
+ /* The error that occurred when an asset has been initialized with error */
+ GError *error;
+};
+
+/* Internal structure to help avoid full loading
+ * of one asset several times
+ */
+typedef struct
+{
+ GList *results;
+ GESAsset *asset;
+} GESAssetCacheEntry;
+
+/* Also protect all the entries in the cache */
+G_LOCK_DEFINE_STATIC (asset_cache_lock);
+/* We are mapping entries by types and ID, such as:
+ *
+ * {
+ * first_extractable_type_name1 :
+ * {
+ * "some ID": GESAssetCacheEntry,
+ * "some other ID": GESAssetCacheEntry 2
+ * },
+ * second_extractable_type_name :
+ * {
+ * "some ID": GESAssetCacheEntry,
+ * "some other ID": GESAssetCacheEntry 2
+ * }
+ * }
+ *
+ * (The first extractable type is the type of the class that implemented
+ * the GESExtractable interface ie: GESClip, GESTimeline,
+ * GESFomatter, etc... but not subclasses)
+ *
+ * This is in order to be able to have 2 Asset with the same ID but
+ * different extractable types.
+ **/
+static GHashTable *type_entries_table = NULL;
+#define LOCK_CACHE (G_LOCK (asset_cache_lock))
+#define UNLOCK_CACHE (G_UNLOCK (asset_cache_lock))
+
+static gchar *
+_check_and_update_parameters (GType * extractable_type, const gchar * id,
+ GError ** error)
+{
+ gchar *real_id;
+ GType old_type = *extractable_type;
+
+ *extractable_type =
+ ges_extractable_get_real_extractable_type_for_id (*extractable_type, id);
+
+ if (*extractable_type == G_TYPE_NONE) {
+ GST_WARNING ("No way to create a Asset for ID: %s, type: %s", id,
+ g_type_name (old_type));
+
+ if (error && *error == NULL)
+ g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
+ "Wrong ID, can not find any extractable_type");
+ return NULL;
+ }
+
+ real_id = ges_extractable_type_check_id (*extractable_type, id, error);
+ if (real_id == NULL) {
+ GST_WARNING ("Wrong ID %s, can not create asset", id);
+
+ g_free (real_id);
+ if (error && *error == NULL)
+ g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID, "Wrong ID");
+
+ return NULL;
+ }
+
+ return real_id;
+}
+
+static gboolean
+start_loading (GESAsset * asset)
+{
+ ges_asset_cache_put (gst_object_ref (asset), NULL);
+ return ges_asset_cache_set_loaded (asset->priv->extractable_type,
+ asset->priv->id, NULL);
+}
+
+static gboolean
+initable_init (GInitable * initable, GCancellable * cancellable,
+ GError ** error)
+{
+ g_clear_error (error);
+
+ return start_loading (GES_ASSET (initable));
+}
+
+static void
+async_initable_init_async (GAsyncInitable * initable, gint io_priority,
+ GCancellable * cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ GError *error = NULL;
+ GESAsset *asset = GES_ASSET (initable);
+
+ task = g_task_new (asset, cancellable, callback, user_data);
+
+ ges_asset_cache_put (g_object_ref (asset), task);
+ switch (GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error)) {
+ case GES_ASSET_LOADING_ERROR:
+ {
+ if (error == NULL)
+ g_set_error (&error, GES_ERROR, GES_ERROR_ASSET_LOADING,
+ "Could not start loading asset");
+
+ /* FIXME Define error code */
+ ges_asset_cache_set_loaded (asset->priv->extractable_type,
+ asset->priv->id, error);
+ g_error_free (error);
+ return;
+ }
+ case GES_ASSET_LOADING_OK:
+ {
+ ges_asset_cache_set_loaded (asset->priv->extractable_type,
+ asset->priv->id, error);
+ return;
+ }
+ case GES_ASSET_LOADING_ASYNC:
+ /* If Async.... let it go */
+ break;
+ }
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface * async_initable_iface)
+{
+ async_initable_iface->init_async = async_initable_init_async;
+}
+
+static void
+initable_iface_init (GInitableIface * initable_iface)
+{
+ initable_iface->init = initable_init;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESAsset, ges_asset, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GESAsset)
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
+
+/* GESAsset virtual methods default implementation */
+static GESAssetLoadingReturn
+ges_asset_start_loading_default (GESAsset * asset, GError ** error)
+{
+ return GES_ASSET_LOADING_OK;
+}
+
+static GESExtractable *
+ges_asset_extract_default (GESAsset * asset, GError ** error)
+{
+ guint n_params;
+ GParameter *params;
+ GESAssetPrivate *priv = asset->priv;
+ GESExtractable *n_extractable;
+
+
+ params = ges_extractable_type_get_parameters_from_id (priv->extractable_type,
+ priv->id, &n_params);
+
+#if GLIB_CHECK_VERSION(2, 53, 1)
+ {
+ gint i;
+ GValue *values;
+ const gchar **names;
+
+ values = g_malloc0 (sizeof (GValue) * n_params);
+ names = g_malloc0 (sizeof (gchar *) * n_params);
+
+ for (i = 0; i < n_params; i++) {
+ values[i] = params[i].value;
+ names[i] = params[i].name;
+ }
+
+ n_extractable =
+ GES_EXTRACTABLE (g_object_new_with_properties (priv->extractable_type,
+ n_params, names, values));
+ g_free (names);
+ g_free (values);
+ }
+#else
+ n_extractable = g_object_newv (priv->extractable_type, n_params, params);
+#endif
+
+ while (n_params--)
+ g_value_unset (¶ms[n_params].value);
+
+ g_free (params);
+
+ return n_extractable;
+}
+
+static gboolean
+ges_asset_request_id_update_default (GESAsset * self, gchar ** proposed_new_id,
+ GError * error)
+{
+ return FALSE;
+}
+
+/* GObject virtual methods implementation */
+static void
+ges_asset_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESAsset *asset = GES_ASSET (object);
+
+ switch (property_id) {
+ case PROP_TYPE:
+ g_value_set_gtype (value, asset->priv->extractable_type);
+ break;
+ case PROP_ID:
+ g_value_set_string (value, asset->priv->id);
+ break;
+ case PROP_PROXY:
+ g_value_set_object (value, ges_asset_get_proxy (asset));
+ break;
+ case PROP_PROXY_TARGET:
+ g_value_set_object (value, ges_asset_get_proxy_target (asset));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_asset_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESAsset *asset = GES_ASSET (object);
+
+ switch (property_id) {
+ case PROP_TYPE:
+ asset->priv->extractable_type = g_value_get_gtype (value);
+ ges_extractable_register_metas (asset->priv->extractable_type, asset);
+ break;
+ case PROP_ID:
+ asset->priv->id = g_value_dup_string (value);
+ break;
+ case PROP_PROXY:
+ ges_asset_set_proxy (asset, g_value_get_object (value));
+ break;
+ case PROP_PROXY_TARGET:
+ ges_asset_set_proxy (g_value_get_object (value), asset);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_asset_finalize (GObject * object)
+{
+ GESAssetPrivate *priv = GES_ASSET (object)->priv;
+
+ GST_DEBUG_OBJECT (object, "finalizing");
+
+ if (priv->id)
+ g_free (priv->id);
+
+ if (priv->proxied_asset_id)
+ g_free (priv->proxied_asset_id);
+
+ if (priv->error)
+ g_error_free (priv->error);
+
+ if (priv->proxies)
+ g_list_free (priv->proxies);
+
+ G_OBJECT_CLASS (ges_asset_parent_class)->finalize (object);
+}
+
+void
+ges_asset_class_init (GESAssetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ges_asset_get_property;
+ object_class->set_property = ges_asset_set_property;
+ object_class->finalize = ges_asset_finalize;
+
+ _properties[PROP_TYPE] =
+ g_param_spec_gtype ("extractable-type", "Extractable type",
+ "The type of the Object that can be extracted out of the asset",
+ G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+ _properties[PROP_ID] =
+ g_param_spec_string ("id", "Identifier",
+ "The unic identifier of the asset", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+ _properties[PROP_PROXY] =
+ g_param_spec_object ("proxy", "Proxy",
+ "The asset default proxy.", GES_TYPE_ASSET, G_PARAM_READWRITE);
+
+ _properties[PROP_PROXY_TARGET] =
+ g_param_spec_object ("proxy-target", "Proxy target",
+ "The target of a proxy asset.", GES_TYPE_ASSET, G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, PROP_LAST, _properties);
+
+ klass->start_loading = ges_asset_start_loading_default;
+ klass->extract = ges_asset_extract_default;
+ klass->request_id_update = ges_asset_request_id_update_default;
+ klass->inform_proxy = NULL;
+
+ GST_DEBUG_CATEGORY_INIT (ges_asset_debug, "ges-asset",
+ GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GES Asset");
+}
+
+void
+ges_asset_init (GESAsset * self)
+{
+ self->priv = ges_asset_get_instance_private (self);
+
+ self->priv->state = ASSET_INITIALIZING;
+ self->priv->proxied_asset_id = NULL;
+}
+
+/* Internal methods */
+
+/* Find the type that implemented the GESExtractable interface */
+static inline const gchar *
+_extractable_type_name (GType type)
+{
+ while (1) {
+ if (g_type_is_a (g_type_parent (type), GES_TYPE_EXTRACTABLE))
+ type = g_type_parent (type);
+ else
+ return g_type_name (type);
+ }
+}
+
+static inline GESAssetCacheEntry *
+_lookup_entry (GType extractable_type, const gchar * id)
+{
+ GHashTable *entries_table;
+
+ entries_table = g_hash_table_lookup (type_entries_table,
+ _extractable_type_name (extractable_type));
+ if (entries_table)
+ return g_hash_table_lookup (entries_table, id);
+
+ return NULL;
+}
+
+static void
+_free_entries (gpointer entry)
+{
+ GESAssetCacheEntry *data = (GESAssetCacheEntry *) entry;
+ if (data->asset)
+ gst_object_unref (data->asset);
+ g_slice_free (GESAssetCacheEntry, entry);
+}
+
+static void
+_gtask_return_error (GTask * task, GError * error)
+{
+ g_task_return_error (task, g_error_copy (error));
+}
+
+static void
+_gtask_return_true (GTask * task, gpointer udata)
+{
+ g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * ges_asset_cache_lookup:
+ *
+ * @id String identifier of asset
+ *
+ * Looks for asset with specified id in cache and it's completely loaded.
+ *
+ * Returns: (transfer none) (nullable): The #GESAsset found or %NULL
+ */
+GESAsset *
+ges_asset_cache_lookup (GType extractable_type, const gchar * id)
+{
+ GESAsset *asset = NULL;
+ GESAssetCacheEntry *entry = NULL;
+
+ g_return_val_if_fail (id, NULL);
+
+ LOCK_CACHE;
+ entry = _lookup_entry (extractable_type, id);
+ if (entry)
+ asset = entry->asset;
+ UNLOCK_CACHE;
+
+ return asset;
+}
+
+static void
+ges_asset_cache_append_task (GType extractable_type,
+ const gchar * id, GTask * task)
+{
+ GESAssetCacheEntry *entry = NULL;
+
+ LOCK_CACHE;
+ if ((entry = _lookup_entry (extractable_type, id)))
+ entry->results = g_list_append (entry->results, task);
+ UNLOCK_CACHE;
+}
+
+gboolean
+ges_asset_cache_set_loaded (GType extractable_type, const gchar * id,
+ GError * error)
+{
+ GESAsset *asset;
+ GESAssetCacheEntry *entry = NULL;
+ GList *results = NULL;
+ GFunc user_func = NULL;
+ gpointer user_data = NULL;
+
+ LOCK_CACHE;
+ if ((entry = _lookup_entry (extractable_type, id)) == NULL) {
+ UNLOCK_CACHE;
+ GST_ERROR ("Calling but type %s ID: %s not in cached, "
+ "something massively screwed", g_type_name (extractable_type), id);
+
+ return FALSE;
+ }
+
+ asset = entry->asset;
+ GST_DEBUG_OBJECT (entry->asset, ": (extractable type: %s) loaded, calling %i "
+ "callback (Error: %s)", g_type_name (asset->priv->extractable_type),
+ g_list_length (entry->results), error ? error->message : "");
+
+ results = entry->results;
+ entry->results = NULL;
+
+ if (error) {
+ asset->priv->state = ASSET_INITIALIZED_WITH_ERROR;
+ if (asset->priv->error)
+ g_error_free (asset->priv->error);
+ asset->priv->error = g_error_copy (error);
+
+ /* In case of error we do not want to emit in idle as we need to recover
+ * if possible */
+ user_func = (GFunc) _gtask_return_error;
+ user_data = error;
+ GST_DEBUG_OBJECT (asset, "initialized with error");
+ } else {
+ asset->priv->state = ASSET_INITIALIZED;
+ user_func = (GFunc) _gtask_return_true;
+ GST_DEBUG_OBJECT (asset, "initialized");
+ }
+ UNLOCK_CACHE;
+
+ g_list_foreach (results, user_func, user_data);
+ g_list_free_full (results, g_object_unref);
+
+ return TRUE;
+}
+
+void
+ges_asset_cache_put (GESAsset * asset, GTask * task)
+{
+ GType extractable_type;
+ const gchar *asset_id;
+ GESAssetCacheEntry *entry;
+
+ /* Needing to work with the cache, taking the lock */
+ asset_id = ges_asset_get_id (asset);
+ extractable_type = asset->priv->extractable_type;
+
+ LOCK_CACHE;
+ if (!(entry = _lookup_entry (extractable_type, asset_id))) {
+ GHashTable *entries_table;
+
+ entries_table = g_hash_table_lookup (type_entries_table,
+ _extractable_type_name (extractable_type));
+ if (entries_table == NULL) {
+ entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ _free_entries);
+
+ g_hash_table_insert (type_entries_table,
+ g_strdup (_extractable_type_name (extractable_type)), entries_table);
+ }
+
+ entry = g_slice_new0 (GESAssetCacheEntry);
+
+ entry->asset = asset;
+ if (task)
+ entry->results = g_list_prepend (entry->results, task);
+ g_hash_table_insert (entries_table, (gpointer) g_strdup (asset_id),
+ (gpointer) entry);
+ } else {
+ if (task) {
+ GST_DEBUG ("%s already in cache, adding result %p", asset_id, task);
+ entry->results = g_list_prepend (entry->results, task);
+ }
+ }
+ UNLOCK_CACHE;
+}
+
+void
+ges_asset_cache_init (void)
+{
+ type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_hash_table_unref);
+
+ _init_formatter_assets ();
+ _init_standard_transition_assets ();
+}
+
+void
+ges_asset_cache_deinit (void)
+{
+ g_hash_table_destroy (type_entries_table);
+ type_entries_table = NULL;
+}
+
+gboolean
+ges_asset_request_id_update (GESAsset * asset, gchar ** proposed_id,
+ GError * error)
+{
+ g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
+
+ return GES_ASSET_GET_CLASS (asset)->request_id_update (asset, proposed_id,
+ error);
+}
+
+gboolean
+ges_asset_try_proxy (GESAsset * asset, const gchar * new_id)
+{
+ GESAssetClass *class;
+
+ g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
+
+ if (g_strcmp0 (asset->priv->id, new_id) == 0) {
+ GST_WARNING_OBJECT (asset, "Trying to proxy to itself (%s),"
+ " NOT possible", new_id);
+
+ return FALSE;
+ } else if (g_strcmp0 (asset->priv->proxied_asset_id, new_id) == 0) {
+ GST_WARNING_OBJECT (asset,
+ "Trying to proxy to same currently set proxy: %s -- %s",
+ asset->priv->proxied_asset_id, new_id);
+
+ return FALSE;
+ }
+
+ g_free (asset->priv->proxied_asset_id);
+ asset->priv->state = ASSET_PROXIED;
+ asset->priv->proxied_asset_id = g_strdup (new_id);
+
+ class = GES_ASSET_GET_CLASS (asset);
+ if (class->inform_proxy)
+ GES_ASSET_GET_CLASS (asset)->inform_proxy (asset, new_id);
+
+ GST_DEBUG_OBJECT (asset, "Trying to proxy to %s", new_id);
+
+ return TRUE;
+}
+
+static gboolean
+_lookup_proxied_asset (const gchar * id, GESAssetCacheEntry * entry,
+ const gchar * asset_id)
+{
+ return !g_strcmp0 (asset_id, entry->asset->priv->proxied_asset_id);
+}
+
+/**
+ * ges_asset_set_proxy:
+ * @asset: The #GESAsset to set proxy on
+ * @proxy: (allow-none): The #GESAsset that should be used as default proxy for @asset or
+ * %NULL if you want to use the currently set proxy. Note that an asset can proxy one and only
+ * one other asset.
+ *
+ * A proxying asset is an asset that can substitue the real @asset. For example if you
+ * have a full HD #GESUriClipAsset you might want to set a lower resolution (HD version
+ * of the same file) as proxy. Note that when an asset is proxied, calling
+ * #ges_asset_request will actually return the proxy asset.
+ *
+ * Returns: %TRUE if @proxy has been set on @asset, %FALSE otherwise.
+ */
+gboolean
+ges_asset_set_proxy (GESAsset * asset, GESAsset * proxy)
+{
+ g_return_val_if_fail (asset == NULL || GES_IS_ASSET (asset), FALSE);
+ g_return_val_if_fail (proxy == NULL || GES_IS_ASSET (proxy), FALSE);
+ g_return_val_if_fail (asset != proxy, FALSE);
+
+ if (!proxy) {
+ if (asset->priv->error) {
+ GST_ERROR_OBJECT (asset,
+ "Proxy was loaded with error (%s), it should not be 'unproxied'",
+ asset->priv->error->message);
+
+ return FALSE;
+ }
+
+ if (asset->priv->proxies) {
+ GESAsset *old_proxy = GES_ASSET (asset->priv->proxies->data);
+
+ old_proxy->priv->proxy_target = NULL;
+ g_object_notify_by_pspec (G_OBJECT (old_proxy),
+ _properties[PROP_PROXY_TARGET]);
+ }
+
+ GST_DEBUG_OBJECT (asset, "%s not proxied anymore", asset->priv->id);
+ asset->priv->state = ASSET_INITIALIZED;
+ g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
+
+ return TRUE;
+ }
+
+ if (asset == NULL) {
+ GHashTable *entries_table;
+ GESAssetCacheEntry *entry;
+
+ entries_table = g_hash_table_lookup (type_entries_table,
+ _extractable_type_name (proxy->priv->extractable_type));
+ entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset,
+ (gpointer) ges_asset_get_id (proxy));
+
+ if (!entry) {
+ GST_DEBUG_OBJECT (asset, "Not proxying any asset");
+ return FALSE;
+ }
+
+ asset = entry->asset;
+ while (asset->priv->proxies)
+ asset = asset->priv->proxies->data;
+ }
+
+ if (proxy->priv->proxy_target) {
+ GST_ERROR_OBJECT (asset,
+ "Trying to use %s as a proxy, but it is already proxying %s",
+ proxy->priv->id, proxy->priv->proxy_target->priv->id);
+
+ return FALSE;
+ }
+
+ if (g_list_find (proxy->priv->proxies, asset)) {
+ GST_ERROR_OBJECT (asset, "Trying to setup a circular proxying dependency!");
+
+ return FALSE;
+ }
+
+ if (g_list_find (asset->priv->proxies, proxy)) {
+ GST_INFO_OBJECT (asset,
+ "%" GST_PTR_FORMAT " already marked as proxy, moving to first", proxy);
+ GES_ASSET (asset->priv->proxies->data)->priv->proxy_target = NULL;
+ asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
+ }
+
+ GST_INFO ("%s is now proxied by %s", asset->priv->id, proxy->priv->id);
+ asset->priv->proxies = g_list_prepend (asset->priv->proxies, proxy);
+ proxy->priv->proxy_target = asset;
+ g_object_notify_by_pspec (G_OBJECT (proxy), _properties[PROP_PROXY_TARGET]);
+
+ asset->priv->state = ASSET_PROXIED;
+ g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
+
+ return TRUE;
+}
+
+/**
+ * ges_asset_unproxy:
+ * @asset: The #GESAsset to stop proxying with @proxy
+ * @proxy: The #GESAsset to stop considering as a proxy for @asset
+ *
+ * Removes @proxy from the list of known proxies for @asset.
+ * If @proxy was the current proxy for @asset, stop using it.
+ *
+ * Returns: %TRUE if @proxy was a known proxy for @asset, %FALSE otherwise.
+ */
+gboolean
+ges_asset_unproxy (GESAsset * asset, GESAsset * proxy)
+{
+ g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
+ g_return_val_if_fail (GES_IS_ASSET (proxy), FALSE);
+ g_return_val_if_fail (asset != proxy, FALSE);
+
+ if (!g_list_find (asset->priv->proxies, proxy)) {
+ GST_INFO_OBJECT (asset, "%s is not a proxy.", proxy->priv->id);
+
+ return FALSE;
+ }
+
+ if (asset->priv->proxies->data == proxy)
+ ges_asset_set_proxy (asset, NULL);
+
+ asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
+
+ return TRUE;
+}
+
+/**
+ * ges_asset_list_proxies:
+ * @asset: The #GESAsset to get proxies from
+ *
+ * Returns: (element-type GESAsset) (transfer none): The list of proxies @asset has. Note that the default asset to be
+ * used is always the first in that list.
+ */
+GList *
+ges_asset_list_proxies (GESAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+
+ return asset->priv->proxies;
+}
+
+/**
+ * ges_asset_get_proxy:
+ * @asset: The #GESAsset to get currenlty used proxy
+ *
+ * Returns: (transfer none) (nullable): The proxy in use for @asset
+ */
+GESAsset *
+ges_asset_get_proxy (GESAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+
+ if (asset->priv->state == ASSET_PROXIED && asset->priv->proxies) {
+ return asset->priv->proxies->data;
+ }
+
+ return NULL;
+}
+
+/**
+ * ges_asset_get_proxy_target:
+ * @proxy: The #GESAsset from which to get the the asset it proxies.
+ *
+ * Returns: (transfer none) (nullable): The #GESAsset that is proxied by @proxy
+ */
+GESAsset *
+ges_asset_get_proxy_target (GESAsset * proxy)
+{
+ g_return_val_if_fail (GES_IS_ASSET (proxy), NULL);
+
+ return proxy->priv->proxy_target;
+}
+
+/* Caution, this method should be used in rare cases (ie: for the project
+ * as we can change its ID from a useless one to a proper URI). In most
+ * cases you want to update the ID creating a proxy
+ */
+void
+ges_asset_set_id (GESAsset * asset, const gchar * id)
+{
+ GHashTable *entries;
+
+ gpointer orig_id = NULL;
+ GESAssetCacheEntry *entry = NULL;
+ GESAssetPrivate *priv = NULL;
+
+ g_return_if_fail (GES_IS_ASSET (asset));
+
+ priv = asset->priv;
+
+ if (priv->state != ASSET_INITIALIZED) {
+ GST_WARNING_OBJECT (asset, "Trying to rest ID on an object that is"
+ " not properly loaded");
+ return;
+ }
+
+ if (g_strcmp0 (id, priv->id) == 0) {
+ GST_DEBUG_OBJECT (asset, "ID is already %s", id);
+
+ return;
+ }
+
+ LOCK_CACHE;
+ entries = g_hash_table_lookup (type_entries_table,
+ _extractable_type_name (asset->priv->extractable_type));
+
+ g_return_if_fail (g_hash_table_lookup_extended (entries, priv->id, &orig_id,
+ (gpointer *) & entry));
+
+ g_hash_table_steal (entries, priv->id);
+ g_hash_table_insert (entries, g_strdup (id), entry);
+
+ GST_DEBUG_OBJECT (asset, "Changing id from %s to %s", priv->id, id);
+ g_free (priv->id);
+ g_free (orig_id);
+ priv->id = g_strdup (id);
+ UNLOCK_CACHE;
+}
+
+static GESAsset *
+_ensure_asset_for_wrong_id (const gchar * wrong_id, GType extractable_type,
+ GError * error)
+{
+ GESAsset *asset;
+
+ if ((asset = ges_asset_cache_lookup (extractable_type, wrong_id)))
+ return asset;
+
+ /* It is a dummy GESAsset, we just bruteforce its creation */
+ asset = g_object_new (GES_TYPE_ASSET, "id", wrong_id,
+ "extractable-type", extractable_type, NULL);
+
+ ges_asset_cache_put (asset, NULL);
+ ges_asset_cache_set_loaded (extractable_type, wrong_id, error);
+
+ return asset;
+}
+
+/**********************************
+ * *
+ * API implementation *
+ * *
+ **********************************/
+
+/**
+ * ges_asset_get_extractable_type:
+ * @self: The #GESAsset
+ *
+ * Gets the type of object that can be extracted from @self
+ *
+ * Returns: the type of object that can be extracted from @self
+ */
+GType
+ges_asset_get_extractable_type (GESAsset * self)
+{
+ g_return_val_if_fail (GES_IS_ASSET (self), G_TYPE_INVALID);
+
+ return self->priv->extractable_type;
+}
+
+/**
+ * ges_asset_request:
+ * @extractable_type: The #GType of the object that can be extracted from the new asset.
+ * @id: (allow-none): The Identifier or %NULL
+ * @error: (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Create a #GESAsset in the most simple cases, you should look at the @extractable_type
+ * documentation to see if that constructor can be called for this particular type
+ *
+ * As it is recommanded not to instanciate assets for GESUriClip synchronously,
+ * it will not work with this method, but you can instead use the specific
+ * #ges_uri_clip_asset_request_sync method if you really want to.
+ *
+ * Returns: (transfer full) (allow-none): A reference to the wanted #GESAsset or %NULL
+ */
+GESAsset *
+ges_asset_request (GType extractable_type, const gchar * id, GError ** error)
+{
+ gchar *real_id;
+
+ GError *lerr = NULL;
+ GESAsset *asset = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ g_return_val_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT), NULL);
+ g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
+ NULL);
+
+ real_id = _check_and_update_parameters (&extractable_type, id, &lerr);
+ if (real_id == NULL) {
+ /* We create an asset for that wrong ID so we have a reference that the
+ * user requested it */
+ _ensure_asset_for_wrong_id (id, extractable_type, lerr);
+ real_id = g_strdup (id);
+ }
+ if (lerr)
+ g_error_free (lerr);
+
+ asset = ges_asset_cache_lookup (extractable_type, real_id);
+ if (asset) {
+ while (TRUE) {
+ switch (asset->priv->state) {
+ case ASSET_INITIALIZED:
+ gst_object_ref (asset);
+ goto done;
+ case ASSET_INITIALIZING:
+ asset = NULL;
+ goto done;
+ case ASSET_PROXIED:
+ if (asset->priv->proxies == NULL) {
+ GST_ERROR ("Proxied against an asset we do not"
+ " have in cache, something massively screwed");
+
+ goto done;
+ }
+
+ asset = asset->priv->proxies->data;
+ while (ges_asset_get_proxy (asset))
+ asset = ges_asset_get_proxy (asset);
+
+ break;
+ case ASSET_NEEDS_RELOAD:
+ GST_DEBUG_OBJECT (asset, "Asset in cache and needs reload");
+ start_loading (asset);
+
+ goto done;
+ case ASSET_INITIALIZED_WITH_ERROR:
+ GST_WARNING_OBJECT (asset, "Initialized with error, not returning");
+ if (asset->priv->error && error)
+ *error = g_error_copy (asset->priv->error);
+ asset = NULL;
+ goto done;
+ default:
+ GST_WARNING ("Case %i not handle, returning", asset->priv->state);
+ goto done;
+ }
+ }
+ } else {
+ GObjectClass *klass;
+ GInitableIface *iface;
+ GType asset_type = ges_extractable_type_get_asset_type (extractable_type);
+
+ klass = g_type_class_ref (asset_type);
+ iface = g_type_interface_peek (klass, G_TYPE_INITABLE);
+
+ if (iface->init) {
+ asset = g_initable_new (asset_type,
+ NULL, NULL, "id", real_id, "extractable-type",
+ extractable_type, NULL);
+ } else {
+ GST_WARNING ("Tried to create an Asset for type %s but no ->init method",
+ g_type_name (extractable_type));
+ }
+ g_type_class_unref (klass);
+ }
+
+done:
+ if (real_id)
+ g_free (real_id);
+
+ GST_DEBUG ("New asset created synchronously: %p", asset);
+ return asset;
+}
+
+/**
+ * ges_asset_request_async:
+ * @extractable_type: The #GType of the object that can be extracted from the
+ * new asset. The class must implement the #GESExtractable interface.
+ * @id: The Identifier of the asset we want to create. This identifier depends of the extractable,
+ * type you want. By default it is the name of the class itself (or %NULL), but for example for a
+ * GESEffect, it will be the pipeline description, for a GESUriClip it
+ * will be the name of the file, etc... You should refer to the documentation of the #GESExtractable
+ * type you want to create a #GESAsset for.
+ * @cancellable: (allow-none): optional %GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the initialization is finished,
+ * Note that the @source of the callback will be the #GESAsset, but you need to
+ * make sure that the asset is properly loaded using the #ges_asset_request_finish
+ * method. This asset can not be used as is.
+ * @user_data: The user data to pass when @callback is called
+ *
+ * The @callback will be called from a running #GMainLoop which is iterating a #GMainContext.
+ * Note that, users should ensure the #GMainContext, since this method will notify
+ * @callback from the thread which was associated with a thread default
+ * #GMainContext at calling ges_init().
+ * For example, if a user wants non-default #GMainContext to be associated
+ * with @callback, ges_init() must be called after g_main_context_push_thread_default ()
+ * with custom #GMainContext.
+ *
+ * Request a new #GESAsset asyncronously, @callback will be called when the materail is
+ * ready to be used or if an error occured.
+ *
+ * Example of request of a GESAsset async:
+ * |[
+ * // The request callback
+ * static void
+ * asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
+ * {
+ * GESAsset *asset;
+ * GError *error = NULL;
+ *
+ * asset = ges_asset_request_finish (res, &error);
+ * if (asset) {
+ * g_print ("The file: %s is usable as a FileSource",
+ * ges_asset_get_id (asset));
+ * } else {
+ * g_print ("The file: %s is *not* usable as a FileSource because: %s",
+ * ges_asset_get_id (source), error->message);
+ * }
+ *
+ * gst_object_unref (mfs);
+ * }
+ *
+ * // The request:
+ * ges_asset_request_async (GES_TYPE_URI_CLIP, some_uri, NULL,
+ * (GAsyncReadyCallback) asset_loaded_cb, user_data);
+ * ]|
+ */
+void
+ges_asset_request_async (GType extractable_type,
+ const gchar * id, GCancellable * cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ gchar *real_id;
+ GESAsset *asset;
+ GError *error = NULL;
+ GTask *task = NULL;
+
+ g_return_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT));
+ g_return_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE));
+ g_return_if_fail (callback);
+
+ GST_DEBUG ("Creating asset with extractable type %s and ID=%s",
+ g_type_name (extractable_type), id);
+
+ real_id = _check_and_update_parameters (&extractable_type, id, &error);
+ if (error) {
+ _ensure_asset_for_wrong_id (id, extractable_type, error);
+ real_id = g_strdup (id);
+ }
+
+ /* Check if we already have an asset for this ID */
+ asset = ges_asset_cache_lookup (extractable_type, real_id);
+ if (asset) {
+ task = g_task_new (asset, NULL, callback, user_data);
+
+ /* In the case of proxied asset, we will loop until we find the
+ * last asset of the chain of proxied asset */
+ while (TRUE) {
+ switch (asset->priv->state) {
+ case ASSET_INITIALIZED:
+ GST_DEBUG_OBJECT (asset, "Asset in cache and initialized, "
+ "using it");
+
+ /* Takes its own references to @asset */
+ g_task_return_boolean (task, TRUE);
+
+ goto done;
+ case ASSET_INITIALIZING:
+ GST_DEBUG_OBJECT (asset, "Asset in cache and but not "
+ "initialized, setting a new callback");
+ ges_asset_cache_append_task (extractable_type, real_id, task);
+ task = NULL;
+
+ goto done;
+ case ASSET_PROXIED:{
+ GESAsset *target = ges_asset_get_proxy (asset);
+
+ if (target == NULL) {
+ GST_ERROR ("Asset %s proxied against an asset (%s) we do not"
+ " have in cache, something massively screwed",
+ asset->priv->id, asset->priv->proxied_asset_id);
+
+ goto done;
+ }
+ asset = target;
+ break;
+ }
+ case ASSET_NEEDS_RELOAD:
+ GST_DEBUG_OBJECT (asset, "Asset in cache and needs reload");
+ ges_asset_cache_append_task (extractable_type, real_id, task);
+ task = NULL;
+ GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error);
+
+ goto done;
+ case ASSET_INITIALIZED_WITH_ERROR:
+ g_task_return_error (task,
+ error ? g_error_copy (error) : g_error_copy (asset->priv->error));
+
+ g_clear_error (&error);
+
+ goto done;
+ default:
+ GST_WARNING ("Case %i not handle, returning", asset->priv->state);
+ return;
+ }
+ }
+ }
+
+ g_async_initable_new_async (ges_extractable_type_get_asset_type
+ (extractable_type), G_PRIORITY_DEFAULT, cancellable, callback, user_data,
+ "id", real_id, "extractable-type", extractable_type, NULL);
+done:
+ if (task)
+ gst_object_unref (task);
+ if (real_id)
+ g_free (real_id);
+}
+
+/**
+ * ges_asset_needs_reload
+ * @extractable_type: The #GType of the object that can be extracted from the
+ * asset to be reloaded.
+ * @id: The identifier of the asset to mark as needing reload
+ *
+ * Sets an asset from the internal cache as needing reload. An asset needs reload
+ * in the case where, for example, we were missing a GstPlugin to use it and that
+ * plugin has been installed, or, that particular asset content as changed
+ * meanwhile (in the case of the usage of proxies).
+ *
+ * Once an asset has been set as "needs reload", requesting that asset again
+ * will lead to it being re discovered, and reloaded as if it was not in the
+ * cache before.
+ *
+ * Returns: %TRUE if the asset was in the cache and could be set as needing reload,
+ * %FALSE otherwise.
+ */
+gboolean
+ges_asset_needs_reload (GType extractable_type, const gchar * id)
+{
+ gchar *real_id;
+ GESAsset *asset;
+ GError *error = NULL;
+
+ real_id = _check_and_update_parameters (&extractable_type, id, &error);
+ if (error) {
+ _ensure_asset_for_wrong_id (id, extractable_type, error);
+ real_id = g_strdup (id);
+ }
+
+ asset = ges_asset_cache_lookup (extractable_type, real_id);
+
+ if (real_id) {
+ g_free (real_id);
+ }
+
+ if (asset) {
+ GST_DEBUG_OBJECT (asset,
+ "Asset with id %s switch state to ASSET_NEEDS_RELOAD",
+ ges_asset_get_id (asset));
+ asset->priv->state = ASSET_NEEDS_RELOAD;
+ g_clear_error (&asset->priv->error);
+ return TRUE;
+ }
+
+ GST_DEBUG ("Asset with id %s not found in cache", id);
+ return FALSE;
+}
+
+/**
+ * ges_asset_get_id:
+ * @self: The #GESAsset to get ID from
+ *
+ * Gets the ID of a #GESAsset
+ *
+ * Returns: The ID of @self
+ */
+const gchar *
+ges_asset_get_id (GESAsset * self)
+{
+ g_return_val_if_fail (GES_IS_ASSET (self), NULL);
+
+ return self->priv->id;
+}
+
+/**
+ * ges_asset_extract:
+ * @self: The #GESAsset to get extract an object from
+ * @error: (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Extracts a new #GObject from @asset. The type of the object is
+ * defined by the extractable-type of @asset, you can check what
+ * type will be extracted from @asset using
+ * #ges_asset_get_extractable_type
+ *
+ * Returns: (transfer floating) (allow-none): A newly created #GESExtractable
+ */
+GESExtractable *
+ges_asset_extract (GESAsset * self, GError ** error)
+{
+ GESExtractable *extractable;
+
+ g_return_val_if_fail (GES_IS_ASSET (self), NULL);
+ g_return_val_if_fail (GES_ASSET_GET_CLASS (self)->extract, NULL);
+
+ GST_DEBUG_OBJECT (self, "Extracting asset of type %s",
+ g_type_name (self->priv->extractable_type));
+ extractable = GES_ASSET_GET_CLASS (self)->extract (self, error);
+
+ if (extractable == NULL)
+ return NULL;
+
+ if (ges_extractable_get_asset (extractable) == NULL)
+ ges_extractable_set_asset (extractable, self);
+
+ return extractable;
+}
+
+/**
+ * ges_asset_request_finish:
+ * @res: The #GAsyncResult from which to get the newly created #GESAsset
+ * @error: (out) (allow-none) (transfer full): An error to be set in case
+ * something wrong happens or %NULL
+ *
+ * Finalize the request of an async #GESAsset
+ *
+ * Returns: (transfer full)(allow-none): The #GESAsset previously requested
+ */
+GESAsset *
+ges_asset_request_finish (GAsyncResult * res, GError ** error)
+{
+ GObject *object;
+ GObject *source_object;
+
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+ source_object = g_async_result_get_source_object (res);
+ g_assert (source_object != NULL);
+
+ object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+ res, error);
+
+ gst_object_unref (source_object);
+
+ return GES_ASSET (object);
+}
+
+/**
+ * ges_list_assets:
+ * @filter: Type of assets to list, #GES_TYPE_EXTRACTABLE will list
+ * all assets
+ *
+ * List all @asset filtering per filter as defined by @filter.
+ * It copies the asset and thus will not be updated in time.
+ *
+ * Returns: (transfer container) (element-type GESAsset): The list of
+ * #GESAsset the object contains
+ */
+GList *
+ges_list_assets (GType filter)
+{
+ GList *ret = NULL;
+ GESAsset *asset;
+ GHashTableIter iter, types_iter;
+ gpointer key, value, typename, assets;
+
+ g_return_val_if_fail (g_type_is_a (filter, GES_TYPE_EXTRACTABLE), NULL);
+
+ LOCK_CACHE;
+ g_hash_table_iter_init (&types_iter, type_entries_table);
+ while (g_hash_table_iter_next (&types_iter, &typename, &assets)) {
+ if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE)
+ continue;
+
+ g_hash_table_iter_init (&iter, (GHashTable *) assets);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ asset = ((GESAssetCacheEntry *) value)->asset;
+
+ if (g_type_is_a (asset->priv->extractable_type, filter))
+ ret = g_list_append (ret, asset);
+ }
+ }
+ UNLOCK_CACHE;
+
+ return ret;
+}
+
+/**
+ * ges_asset_get_error:
+ * @self: The asset to retrieve the error from
+ *
+ * Returns: (transfer none) (nullable): The #GError of the asset or %NULL if
+ * the asset was loaded without issue
+ *
+ * Since: 1.8
+ */
+GError *
+ges_asset_get_error (GESAsset * self)
+{
+ g_return_val_if_fail (GES_IS_ASSET (self), NULL);
+
+ return self->priv->error;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GES_ASSET_
+#define _GES_ASSET_
+
+#include <glib-object.h>
+#include <ges/ges-extractable.h>
+#include <ges/ges-types.h>
+#include <ges/ges-enums.h>
+#include <gio/gio.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+#define GES_TYPE_ASSET ges_asset_get_type()
+#define GES_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_ASSET, GESAsset))
+#define GES_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_ASSET, GESAssetClass))
+#define GES_IS_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_ASSET))
+#define GES_IS_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_ASSET))
+#define GES_ASSET_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_ASSET, GESAssetClass))
+
+typedef enum
+{
+ GES_ASSET_LOADING_ERROR,
+ GES_ASSET_LOADING_ASYNC,
+ GES_ASSET_LOADING_OK
+} GESAssetLoadingReturn;
+
+typedef struct _GESAssetPrivate GESAssetPrivate;
+
+GES_API
+GType ges_asset_get_type (void);
+
+struct _GESAsset
+{
+ GObject parent;
+
+ /* <private> */
+ GESAssetPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAssetClass
+{
+ GObjectClass parent;
+
+ GESAssetLoadingReturn (*start_loading) (GESAsset *self,
+ GError **error);
+ GESExtractable* (*extract) (GESAsset *self,
+ GError **error);
+ /* Let subclasses know that we proxied an asset */
+ void (*inform_proxy) (GESAsset *self,
+ const gchar *proxy_id);
+
+ void (*proxied) (GESAsset *self,
+ GESAsset *proxy);
+
+ /* Ask subclasses for a new ID for @self when the asset failed loading
+ * This function returns %FALSE when the ID could be updated or %TRUE
+ * otherwize */
+ gboolean (*request_id_update) (GESAsset *self,
+ gchar **proposed_new_id,
+ GError *error) ;
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_asset_get_extractable_type (GESAsset * self);
+GES_API
+void ges_asset_request_async (GType extractable_type,
+ const gchar * id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GES_API
+GESAsset * ges_asset_request (GType extractable_type,
+ const gchar * id,
+ GError **error);
+GES_API
+const gchar * ges_asset_get_id (GESAsset* self);
+GES_API
+GESAsset * ges_asset_request_finish (GAsyncResult *res,
+ GError **error);
+GES_API
+GError * ges_asset_get_error (GESAsset * self);
+GES_API
+GESExtractable * ges_asset_extract (GESAsset * self,
+ GError **error);
+GES_API
+GList * ges_list_assets (GType filter);
+
+
+GES_API
+gboolean ges_asset_set_proxy (GESAsset *asset, GESAsset *proxy);
+GES_API
+gboolean ges_asset_unproxy (GESAsset *asset, GESAsset * proxy);
+GES_API
+GList * ges_asset_list_proxies (GESAsset *asset);
+GES_API
+GESAsset * ges_asset_get_proxy_target(GESAsset *proxy);
+GES_API
+GESAsset * ges_asset_get_proxy (GESAsset *asset);
+GES_API
+gboolean ges_asset_needs_reload (GType extractable_type,
+ const gchar * id);
+
+G_END_DECLS
+#endif /* _GES_ASSET */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesaudiosource
+ * @title: GESAudioSource
+ * @short_description: Base Class for audio sources
+ *
+ * ## Children Properties
+ *
+ * You can use the following children properties through the
+ * #ges_track_element_set_child_property and alike set of methods:
+ *
+ * <informaltable frame="none">
+ * <tgroup cols="3">
+ * <colspec colname="properties_type" colwidth="150px"/>
+ * <colspec colname="properties_name" colwidth="200px"/>
+ * <colspec colname="properties_flags" colwidth="400px"/>
+ * <tbody>
+ * <row>
+ * <entry role="property_type"><link linkend="gdouble"><type>double</type></link></entry>
+ * <entry role="property_name"><link linkend="GESAudioSource--volume">volume</link></entry>
+ * <entry>volume factor, 1.0=100%.</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gboolean"><type>gboolean</type></link></entry>
+ * <entry role="property_name"><link linkend="GESAudioSource--mute">mute</link></entry>
+ * <entry>mute channel.</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges/ges-meta-container.h"
+#include "ges-track-element.h"
+#include "ges-audio-source.h"
+#include "ges-layer.h"
+
+struct _GESAudioSourcePrivate
+{
+ GstElement *capsfilter;
+ GESTrack *current_track;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESAudioSource, ges_audio_source,
+ GES_TYPE_SOURCE);
+
+static void
+_sync_element_to_layer_property_float (GESTrackElement * trksrc,
+ GstElement * element, const gchar * meta, const gchar * propname)
+{
+ GESTimelineElement *parent;
+ GESLayer *layer;
+ gfloat value;
+
+ parent = ges_timeline_element_get_parent (GES_TIMELINE_ELEMENT (trksrc));
+ if (!parent) {
+ GST_DEBUG_OBJECT (trksrc, "Not in a clip... doing nothing");
+
+ return;
+ }
+
+ layer = ges_clip_get_layer (GES_CLIP (parent));
+
+ gst_object_unref (parent);
+
+ if (layer != NULL) {
+
+ ges_meta_container_get_float (GES_META_CONTAINER (layer), meta, &value);
+ g_object_set (element, propname, value, NULL);
+ GST_DEBUG_OBJECT (trksrc, "Setting %s to %f", propname, value);
+
+
+ gst_object_unref (layer);
+ } else {
+
+ GST_DEBUG_OBJECT (trksrc, "NOT setting the %s", propname);
+ }
+}
+
+static void
+restriction_caps_cb (GESTrack * track,
+ GParamSpec * arg G_GNUC_UNUSED, GESAudioSource * self)
+{
+ GstCaps *caps;
+
+ g_object_get (track, "restriction-caps", &caps, NULL);
+
+ GST_DEBUG_OBJECT (self, "Setting capsfilter caps to %" GST_PTR_FORMAT, caps);
+ g_object_set (self->priv->capsfilter, "caps", caps, NULL);
+
+ if (caps)
+ gst_caps_unref (caps);
+}
+
+static void
+_track_changed_cb (GESAudioSource * self, GParamSpec * arg G_GNUC_UNUSED,
+ gpointer udata)
+{
+ GESTrack *track = ges_track_element_get_track (GES_TRACK_ELEMENT (self));
+
+ if (self->priv->current_track) {
+ g_signal_handlers_disconnect_by_func (self->priv->current_track,
+ (GCallback) restriction_caps_cb, self);
+ }
+
+ self->priv->current_track = track;
+ if (track) {
+ restriction_caps_cb (track, NULL, self);
+
+ g_signal_connect (track, "notify::restriction-caps",
+ G_CALLBACK (restriction_caps_cb), self);
+ }
+}
+
+static GstElement *
+ges_audio_source_create_element (GESTrackElement * trksrc)
+{
+ GstElement *volume, *vbin;
+ GstElement *topbin;
+ GstElement *sub_element;
+ GESAudioSourceClass *source_class = GES_AUDIO_SOURCE_GET_CLASS (trksrc);
+ const gchar *props[] = { "volume", "mute", NULL };
+ GESAudioSource *self = GES_AUDIO_SOURCE (trksrc);
+
+ if (!source_class->create_source)
+ return NULL;
+
+ sub_element = source_class->create_source (trksrc);
+
+ GST_DEBUG_OBJECT (trksrc, "Creating a bin sub_element ! volume");
+ vbin =
+ gst_parse_bin_from_description
+ ("audioconvert ! audioresample ! volume name=v ! capsfilter name=audio-track-caps-filter",
+ TRUE, NULL);
+ topbin = ges_source_create_topbin ("audiosrcbin", sub_element, vbin, NULL);
+ volume = gst_bin_get_by_name (GST_BIN (vbin), "v");
+ self->priv->capsfilter = gst_bin_get_by_name (GST_BIN (vbin),
+ "audio-track-caps-filter");
+
+ g_signal_connect (self, "notify::track", (GCallback) _track_changed_cb, NULL);
+ _track_changed_cb (self, NULL, NULL);
+
+ _sync_element_to_layer_property_float (trksrc, volume, GES_META_VOLUME,
+ "volume");
+ ges_track_element_add_children_props (trksrc, volume, NULL, NULL, props);
+ gst_object_unref (volume);
+
+ return topbin;
+}
+
+static void
+ges_audio_source_dispose (GObject * object)
+{
+ GESAudioSource *self = GES_AUDIO_SOURCE (object);
+
+ if (self->priv->capsfilter) {
+ gst_object_unref (self->priv->capsfilter);
+ self->priv->capsfilter = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_audio_source_parent_class)->dispose (object);
+}
+
+static void
+ges_audio_source_class_init (GESAudioSourceClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GESTrackElementClass *track_class = GES_TRACK_ELEMENT_CLASS (klass);
+ GESAudioSourceClass *audio_source_class = GES_AUDIO_SOURCE_CLASS (klass);
+
+ gobject_class->dispose = ges_audio_source_dispose;
+ track_class->nleobject_factorytype = "nlesource";
+ track_class->create_element = ges_audio_source_create_element;
+ audio_source_class->create_source = NULL;
+}
+
+static void
+ges_audio_source_init (GESAudioSource * self)
+{
+ self->priv = ges_audio_source_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_AUDIO_SOURCE
+#define _GES_AUDIO_SOURCE
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-track-element.h>
+#include <ges/ges-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUDIO_SOURCE ges_audio_source_get_type()
+
+#define GES_AUDIO_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUDIO_SOURCE, GESAudioSource))
+
+#define GES_AUDIO_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUDIO_SOURCE, GESAudioSourceClass))
+
+#define GES_IS_AUDIO_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUDIO_SOURCE))
+
+#define GES_IS_AUDIO_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUDIO_SOURCE))
+
+#define GES_AUDIO_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUDIO_SOURCE, GESAudioSourceClass))
+
+typedef struct _GESAudioSourcePrivate GESAudioSourcePrivate;
+
+/**
+ * GESAudioSource:
+ *
+ * Base class for audio sources
+ */
+
+struct _GESAudioSource {
+ /*< private >*/
+ GESSource parent;
+
+ GESAudioSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESAudioSourceClass:
+ * @create_source: method to return the GstElement to put in the source topbin.
+ * Other elements will be queued, like a volume.
+ * In the case of a AudioUriSource, for example, the subclass will return a decodebin,
+ * and we will append a volume.
+ */
+struct _GESAudioSourceClass {
+ /*< private >*/
+ GESSourceClass parent_class;
+
+ /*< public >*/
+ GstElement* (*create_source) (GESTrackElement * object);
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_audio_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_AUDIO_SOURCE */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesaudiotestsource
+ * @title: GESAudioTestSource
+ * @short_description: produce a simple test waveform or silence
+ *
+ * Outputs a test audio stream using audiotestsrc. The default property values
+ * output silence. Useful for testing pipelines, or to fill gaps in an audio
+ * track.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-audio-test-source.h"
+
+#define DEFAULT_VOLUME 1.0
+
+struct _GESAudioTestSourcePrivate
+{
+ gdouble freq;
+ gdouble volume;
+};
+
+enum
+{
+ PROP_0,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESAudioTestSource, ges_audio_test_source,
+ GES_TYPE_AUDIO_SOURCE);
+
+static void ges_audio_test_source_get_property (GObject * object, guint
+ property_id, GValue * value, GParamSpec * pspec);
+
+static void ges_audio_test_source_set_property (GObject * object, guint
+ property_id, const GValue * value, GParamSpec * pspec);
+
+static GstElement *ges_audio_test_source_create_source (GESTrackElement * self);
+
+static void
+ges_audio_test_source_class_init (GESAudioTestSourceClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESAudioSourceClass *source_class = GES_AUDIO_SOURCE_CLASS (klass);
+
+ object_class->get_property = ges_audio_test_source_get_property;
+ object_class->set_property = ges_audio_test_source_set_property;
+
+ source_class->create_source = ges_audio_test_source_create_source;
+}
+
+static void
+ges_audio_test_source_init (GESAudioTestSource * self)
+{
+ self->priv = ges_audio_test_source_get_instance_private (self);
+ self->priv->freq = 440;
+ self->priv->volume = DEFAULT_VOLUME;
+}
+
+static void
+ges_audio_test_source_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_audio_test_source_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GstElement *
+ges_audio_test_source_create_source (GESTrackElement * trksrc)
+{
+ GESAudioTestSource *self;
+ GstElement *ret;
+ const gchar *props[] = { "volume", "freq", NULL };
+
+ self = (GESAudioTestSource *) trksrc;
+ ret = gst_element_factory_make ("audiotestsrc", NULL);
+ g_object_set (ret, "volume", (gdouble) self->priv->volume, "freq", (gdouble)
+ self->priv->freq, NULL);
+
+ ges_track_element_add_children_props (trksrc, ret, NULL, NULL, props);
+
+ return ret;
+}
+
+/**
+ * ges_audio_test_source_set_freq:
+ * @self: a #GESAudioTestSource
+ * @freq: The frequency you want to apply on @self
+ *
+ * Lets you set the frequency applied on the track element
+ */
+void
+ges_audio_test_source_set_freq (GESAudioTestSource * self, gdouble freq)
+{
+ GstElement *element =
+ ges_track_element_get_element (GES_TRACK_ELEMENT (self));
+
+ self->priv->freq = freq;
+ if (element) {
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_DOUBLE);
+ g_value_set_double (&val, freq);
+ ges_track_element_set_child_property (GES_TRACK_ELEMENT (self), "freq",
+ &val);
+ }
+}
+
+/**
+ * ges_audio_test_source_set_volume:
+ * @self: a #GESAudioTestSource
+ * @volume: The volume you want to apply on @self
+ *
+ * Sets the volume of the test audio signal.
+ */
+void
+ges_audio_test_source_set_volume (GESAudioTestSource * self, gdouble volume)
+{
+ GstElement *element =
+ ges_track_element_get_element (GES_TRACK_ELEMENT (self));
+
+ self->priv->volume = volume;
+ if (element) {
+ GValue val = { 0 };
+
+ g_value_init (&val, G_TYPE_DOUBLE);
+ g_value_set_double (&val, volume);
+ ges_track_element_set_child_property (GES_TRACK_ELEMENT (self), "volume",
+ &val);
+ }
+}
+
+/**
+ * ges_audio_test_source_get_freq:
+ * @self: a #GESAudioTestSource
+ *
+ * Get the current frequency of @self.
+ *
+ * Returns: The current frequency of @self.
+ */
+double
+ges_audio_test_source_get_freq (GESAudioTestSource * self)
+{
+ GValue val = { 0 };
+
+ ges_track_element_get_child_property (GES_TRACK_ELEMENT (self), "freq", &val);
+ return g_value_get_double (&val);
+}
+
+/**
+ * ges_audio_test_source_get_volume:
+ * @self: a #GESAudioTestSource
+ *
+ * Get the current volume of @self.
+ *
+ * Returns: The current volume of @self
+ */
+double
+ges_audio_test_source_get_volume (GESAudioTestSource * self)
+{
+ GValue val = { 0 };
+
+ ges_track_element_get_child_property (GES_TRACK_ELEMENT (self), "volume",
+ &val);
+ return g_value_get_double (&val);
+}
+
+/**
+ * ges_audio_test_source_new:
+ *
+ * Creates a new #GESAudioTestSource.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESAudioTestSource.
+ */
+GESAudioTestSource *
+ges_audio_test_source_new (void)
+{
+ return g_object_new (GES_TYPE_AUDIO_TEST_SOURCE, "track-type",
+ GES_TRACK_TYPE_AUDIO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_AUDIO_TEST_SOURCE
+#define _GES_AUDIO_TEST_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-audio-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUDIO_TEST_SOURCE ges_audio_test_source_get_type()
+
+#define GES_AUDIO_TEST_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUDIO_TEST_SOURCE, GESAudioTestSource))
+
+#define GES_AUDIO_TEST_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUDIO_TEST_SOURCE, GESAudioTestSourceClass))
+
+#define GES_IS_AUDIO_TEST_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUDIO_TEST_SOURCE))
+
+#define GES_IS_AUDIO_TEST_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUDIO_TEST_SOURCE))
+
+#define GES_AUDIO_TEST_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUDIO_TEST_SOURCE, GESAudioTestSourceClass))
+
+typedef struct _GESAudioTestSourcePrivate GESAudioTestSourcePrivate;
+
+
+/**
+ * GESAudioTestSource:
+ *
+ */
+
+struct _GESAudioTestSource {
+ GESAudioSource parent;
+
+ /*< private >*/
+ GESAudioTestSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAudioTestSourceClass {
+ /*< private >*/
+ GESAudioSourceClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_audio_test_source_get_type (void);
+
+
+GES_API
+void ges_audio_test_source_set_freq(GESAudioTestSource *self,
+ gdouble freq);
+
+GES_API
+void ges_audio_test_source_set_volume(GESAudioTestSource *self,
+ gdouble volume);
+
+GES_API
+double ges_audio_test_source_get_freq(GESAudioTestSource *self);
+GES_API
+double ges_audio_test_source_get_volume(GESAudioTestSource *self);
+G_END_DECLS
+
+#endif /* _GES_AUDIO_TEST_SOURCE */
+
--- /dev/null
+/* GStreamer Editing Services
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gesaudiotrack
+ * @title: GESAudioTrack
+ * @short_description: A standard GESTrack for raw audio
+ *
+ * Sane default properties to specify and fixate the output stream are
+ * set as restriction-caps.
+ * It is advised, to modify these properties, to use
+ * #ges_track_update_restriction_caps, setting them directly is
+ * possible through #ges_track_set_restriction_caps, but not specifying
+ * one of them can lead to negotiation issues, only use that function
+ * if you actually know what you're doing :)
+ *
+ * The default properties are:
+ * - format: S32LE
+ * - channels: 2
+ * - rate: 44100
+ * - layout: interleaved
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-smart-adder.h"
+#include "ges-audio-track.h"
+
+#define DEFAULT_CAPS "audio/x-raw"
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define DEFAULT_RESTRICTION_CAPS "audio/x-raw, format=S32LE, channels=2, "\
+ "rate=44100, layout=interleaved"
+#else
+#define DEFAULT_RESTRICTION_CAPS "audio/x-raw, format=S32BE, channels=2, "\
+ "rate=44100, layout=interleaved"
+#endif
+
+struct _GESAudioTrackPrivate
+{
+ gpointer nothing;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESAudioTrack, ges_audio_track, GES_TYPE_TRACK);
+
+/****************************************************
+ * Private methods and utils *
+ ****************************************************/
+static GstElement *
+create_element_for_raw_audio_gap (GESTrack * track)
+{
+ GstElement *elem;
+
+ elem = gst_element_factory_make ("audiotestsrc", NULL);
+ g_object_set (elem, "wave", 4, NULL);
+
+ return elem;
+}
+
+
+/****************************************************
+ * GObject vmethods implementations *
+ ****************************************************/
+
+static void
+ges_audio_track_init (GESAudioTrack * self)
+{
+ self->priv = ges_audio_track_get_instance_private (self);
+}
+
+static void
+ges_audio_track_finalize (GObject * object)
+{
+ /* TODO: Add deinitalization code here */
+
+ G_OBJECT_CLASS (ges_audio_track_parent_class)->finalize (object);
+}
+
+static void
+ges_audio_track_class_init (GESAudioTrackClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+/* GESTrackClass *parent_class = GES_TRACK_CLASS (klass);
+ */
+
+ object_class->finalize = ges_audio_track_finalize;
+
+ GES_TRACK_CLASS (klass)->get_mixing_element = ges_smart_adder_new;
+}
+
+/****************************************************
+ * API implementation *
+ ****************************************************/
+/**
+ * ges_audio_track_new:
+ *
+ * Creates a new #GESAudioTrack of type #GES_TRACK_TYPE_AUDIO and with generic
+ * raw audio caps ("audio/x-raw");
+ *
+ * Returns: (transfer floating): A new #GESTrack
+ */
+GESAudioTrack *
+ges_audio_track_new (void)
+{
+ GESAudioTrack *ret;
+ GstCaps *caps = gst_caps_from_string (DEFAULT_CAPS);
+ GstCaps *restriction_caps = gst_caps_from_string (DEFAULT_RESTRICTION_CAPS);
+
+ ret = g_object_new (GES_TYPE_AUDIO_TRACK, "caps", caps,
+ "track-type", GES_TRACK_TYPE_AUDIO, NULL);
+
+ ges_track_set_create_element_for_gap_func (GES_TRACK (ret),
+ create_element_for_raw_audio_gap);
+
+ ges_track_set_restriction_caps (GES_TRACK (ret), restriction_caps);
+
+ gst_caps_unref (caps);
+ gst_caps_unref (restriction_caps);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_AUDIO_TRACK_H_
+#define _GES_AUDIO_TRACK_H_
+
+#include <glib-object.h>
+
+#include "ges-track.h"
+#include "ges-types.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUDIO_TRACK (ges_audio_track_get_type ())
+#define GES_AUDIO_TRACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUDIO_TRACK, GESAudioTrack))
+#define GES_AUDIO_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUDIO_TRACK, GESAudioTrackClass))
+#define GES_IS_AUDIO_TRACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUDIO_TRACK))
+#define GES_IS_AUDIO_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUDIO_TRACK))
+#define GES_AUDIO_TRACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUDIO_TRACK, GESAudioTrackClass))
+
+typedef struct _GESAudioTrackPrivate GESAudioTrackPrivate;
+
+struct _GESAudioTrackClass
+{
+ GESTrackClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAudioTrack
+{
+ GESTrack parent_instance;
+
+ /*< private >*/
+ GESAudioTrackPrivate *priv;
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_audio_track_get_type (void) G_GNUC_CONST;
+GES_API
+GESAudioTrack* ges_audio_track_new (void);
+
+G_END_DECLS
+#endif /* _GES_AUDIO_TRACK_H_ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesaudiotransition
+ * @title: GESAudioTransition
+ * @short_description: implements audio crossfade transition
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-audio-transition.h"
+
+#include <gst/controller/gstdirectcontrolbinding.h>
+
+struct _GESAudioTransitionPrivate
+{
+ /* these enable volume interpolation. Unlike video, both inputs are adjusted
+ * simultaneously */
+ GstControlSource *a_control_source;
+
+ GstControlSource *b_control_source;
+
+};
+
+enum
+{
+ PROP_0,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESAudioTransition, ges_audio_transition,
+ GES_TYPE_TRANSITION);
+
+#define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING)
+
+static void
+ges_audio_transition_duration_changed (GESTrackElement * self, guint64);
+
+static GstElement *ges_audio_transition_create_element (GESTrackElement * self);
+
+static void ges_audio_transition_dispose (GObject * object);
+
+static void ges_audio_transition_finalize (GObject * object);
+
+static void ges_audio_transition_get_property (GObject * object, guint
+ property_id, GValue * value, GParamSpec * pspec);
+
+static void ges_audio_transition_set_property (GObject * object, guint
+ property_id, const GValue * value, GParamSpec * pspec);
+
+static void
+duration_changed_cb (GESTrackElement * self, GParamSpec * arg G_GNUC_UNUSED)
+{
+ ges_audio_transition_duration_changed (self,
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (self)));
+}
+
+static void
+ges_audio_transition_class_init (GESAudioTransitionClass * klass)
+{
+ GObjectClass *object_class;
+ GESTrackElementClass *toclass;
+
+ object_class = G_OBJECT_CLASS (klass);
+ toclass = GES_TRACK_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_audio_transition_get_property;
+ object_class->set_property = ges_audio_transition_set_property;
+ object_class->dispose = ges_audio_transition_dispose;
+ object_class->finalize = ges_audio_transition_finalize;
+
+ toclass->create_element = ges_audio_transition_create_element;
+
+}
+
+static void
+ges_audio_transition_init (GESAudioTransition * self)
+{
+
+ self->priv = ges_audio_transition_get_instance_private (self);
+}
+
+static void
+ges_audio_transition_dispose (GObject * object)
+{
+ GESAudioTransition *self;
+
+ self = GES_AUDIO_TRANSITION (object);
+
+ if (self->priv->a_control_source) {
+ if (self->priv->a_control_source)
+ gst_object_unref (self->priv->a_control_source);
+ self->priv->a_control_source = NULL;
+ }
+
+ if (self->priv->b_control_source) {
+ if (self->priv->b_control_source)
+ gst_object_unref (self->priv->b_control_source);
+ self->priv->b_control_source = NULL;
+ }
+
+ g_signal_handlers_disconnect_by_func (GES_TRACK_ELEMENT (self),
+ duration_changed_cb, NULL);
+
+ G_OBJECT_CLASS (ges_audio_transition_parent_class)->dispose (object);
+}
+
+static void
+ges_audio_transition_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ges_audio_transition_parent_class)->finalize (object);
+}
+
+static void
+ges_audio_transition_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_audio_transition_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GObject *
+link_element_to_mixer_with_volume (GstBin * bin, GstElement * element,
+ GstElement * mixer)
+{
+ GstElement *volume = gst_element_factory_make ("volume", NULL);
+ GstElement *resample = gst_element_factory_make ("audioresample", NULL);
+
+ gst_bin_add (bin, volume);
+ gst_bin_add (bin, resample);
+
+ if (!fast_element_link (element, volume) ||
+ !fast_element_link (volume, resample) ||
+ !gst_element_link_pads_full (resample, "src", mixer, "sink_%u",
+ GST_PAD_LINK_CHECK_NOTHING))
+ GST_ERROR_OBJECT (bin, "Error linking volume to mixer");
+
+ return G_OBJECT (volume);
+}
+
+static GstElement *
+ges_audio_transition_create_element (GESTrackElement * track_element)
+{
+ GESAudioTransition *self;
+ GstElement *topbin, *iconva, *iconvb, *oconv;
+ GObject *atarget, *btarget = NULL;
+ const gchar *propname = "volume";
+ GstElement *mixer = NULL;
+ GstPad *sinka_target, *sinkb_target, *src_target, *sinka, *sinkb, *src;
+ guint64 duration;
+ GstControlSource *acontrol_source, *bcontrol_source;
+
+ self = GES_AUDIO_TRANSITION (track_element);
+
+ GST_LOG ("creating an audio bin");
+
+ topbin = gst_bin_new ("transition-bin");
+ iconva = gst_element_factory_make ("audioconvert", "tr-aconv-a");
+ iconvb = gst_element_factory_make ("audioconvert", "tr-aconv-b");
+ oconv = gst_element_factory_make ("audioconvert", "tr-aconv-output");
+
+ gst_bin_add_many (GST_BIN (topbin), iconva, iconvb, oconv, NULL);
+
+ mixer = gst_element_factory_make ("audiomixer", NULL);
+ gst_bin_add (GST_BIN (topbin), mixer);
+
+ atarget = link_element_to_mixer_with_volume (GST_BIN (topbin), iconva, mixer);
+ btarget = link_element_to_mixer_with_volume (GST_BIN (topbin), iconvb, mixer);
+
+ g_assert (atarget && btarget);
+
+ fast_element_link (mixer, oconv);
+
+ sinka_target = gst_element_get_static_pad (iconva, "sink");
+ sinkb_target = gst_element_get_static_pad (iconvb, "sink");
+ src_target = gst_element_get_static_pad (oconv, "src");
+
+ sinka = gst_ghost_pad_new ("sinka", sinka_target);
+ sinkb = gst_ghost_pad_new ("sinkb", sinkb_target);
+ src = gst_ghost_pad_new ("src", src_target);
+
+ gst_element_add_pad (topbin, src);
+ gst_element_add_pad (topbin, sinka);
+ gst_element_add_pad (topbin, sinkb);
+
+ /* set up interpolation */
+
+ gst_object_unref (sinka_target);
+ gst_object_unref (sinkb_target);
+ gst_object_unref (src_target);
+
+ acontrol_source = gst_interpolation_control_source_new ();
+ g_object_set (acontrol_source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+
+ bcontrol_source = gst_interpolation_control_source_new ();
+ g_object_set (bcontrol_source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+
+ self->priv->a_control_source = acontrol_source;
+ self->priv->b_control_source = bcontrol_source;
+
+ duration =
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (track_element));
+ ges_audio_transition_duration_changed (track_element, duration);
+
+ g_signal_connect (track_element, "notify::duration",
+ G_CALLBACK (duration_changed_cb), NULL);
+
+ gst_object_add_control_binding (GST_OBJECT (atarget),
+ gst_direct_control_binding_new (GST_OBJECT (atarget), propname,
+ acontrol_source));
+ gst_object_add_control_binding (GST_OBJECT (btarget),
+ gst_direct_control_binding_new (GST_OBJECT (btarget), propname,
+ bcontrol_source));
+
+ self->priv->a_control_source = acontrol_source;
+ self->priv->b_control_source = bcontrol_source;
+
+ return topbin;
+}
+
+static void
+ges_audio_transition_duration_changed (GESTrackElement * track_element,
+ guint64 duration)
+{
+ GESAudioTransition *self;
+ GstElement *nleobj = ges_track_element_get_nleobject (track_element);
+ GstTimedValueControlSource *ta, *tb;
+
+ self = GES_AUDIO_TRANSITION (track_element);
+
+ GST_INFO ("updating controller: nleobj (%p)", nleobj);
+
+ if (G_UNLIKELY ((!self->priv->a_control_source ||
+ !self->priv->b_control_source)))
+ return;
+
+ GST_INFO ("setting values on controller");
+ ta = GST_TIMED_VALUE_CONTROL_SOURCE (self->priv->a_control_source);
+ tb = GST_TIMED_VALUE_CONTROL_SOURCE (self->priv->b_control_source);
+
+ gst_timed_value_control_source_unset_all (ta);
+ gst_timed_value_control_source_unset_all (tb);
+ /* The volume property goes from 0 to 10, so we want to interpolate between
+ * 0 and 0.1 */
+ gst_timed_value_control_source_set (ta, 0, 0.1);
+ gst_timed_value_control_source_set (ta, duration, 0.0);
+
+ gst_timed_value_control_source_set (tb, 0, 0.0);
+ gst_timed_value_control_source_set (tb, duration, 0.1);
+
+ GST_INFO ("done updating controller");
+}
+
+/**
+ * ges_audio_transition_new:
+ *
+ * Creates a new #GESAudioTransition.
+ *
+ * Returns: (transfer floating): The newly created #GESAudioTransition.
+ */
+GESAudioTransition *
+ges_audio_transition_new (void)
+{
+ return g_object_new (GES_TYPE_AUDIO_TRANSITION, "track-type",
+ GES_TRACK_TYPE_AUDIO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_AUDIO_TRANSITION
+#define _GES_AUDIO_TRANSITION
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-transition.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUDIO_TRANSITION ges_audio_transition_get_type()
+
+#define GES_AUDIO_TRANSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUDIO_TRANSITION, GESAudioTransition))
+
+#define GES_AUDIO_TRANSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUDIO_TRANSITION, GESAudioTransitionClass))
+
+#define GES_IS_AUDIO_TRANSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUDIO_TRANSITION))
+
+#define GES_IS_AUDIO_TRANSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUDIO_TRANSITION))
+
+#define GES_AUDIO_TRANSITION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUDIO_TRANSITION, GESAudioTransitionClass))
+
+typedef struct _GESAudioTransitionPrivate GESAudioTransitionPrivate;
+
+/**
+ * GESAudioTransition:
+ *
+ */
+
+struct _GESAudioTransition {
+ GESTransition parent;
+
+ /*< private >*/
+ GESAudioTransitionPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAudioTransitionClass {
+ GESTransitionClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_audio_transition_get_type (void);
+
+GES_API
+GESAudioTransition* ges_audio_transition_new (void);
+
+G_END_DECLS
+
+#endif /* _GES_TRACK_AUDIO_transition */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesaudiourisource
+ * @title: GESAudioUriSource
+ * @short_description: outputs a single audio stream from a given file
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-utils.h"
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-audio-uri-source.h"
+#include "ges-uri-asset.h"
+#include "ges-extractable.h"
+
+struct _GESAudioUriSourcePrivate
+{
+ GstElement *decodebin; /* Reference owned by parent class */
+};
+
+enum
+{
+ PROP_0,
+ PROP_URI
+};
+
+static void
+ges_audio_uri_source_track_set_cb (GESAudioUriSource * self,
+ GParamSpec * arg G_GNUC_UNUSED, gpointer nothing)
+{
+ GESTrack *track;
+ const GstCaps *caps = NULL;
+
+ if (!self->priv->decodebin)
+ return;
+
+ track = ges_track_element_get_track (GES_TRACK_ELEMENT (self));
+ if (!track)
+ return;
+
+ caps = ges_track_get_caps (track);
+
+ GST_INFO_OBJECT (self, "Setting caps to: %" GST_PTR_FORMAT, caps);
+ g_object_set (self->priv->decodebin, "caps", caps, NULL);
+}
+
+/* GESSource VMethod */
+static GstElement *
+ges_audio_uri_source_create_source (GESTrackElement * trksrc)
+{
+ GESAudioUriSource *self;
+ GESTrack *track;
+ GstElement *decodebin;
+ const GstCaps *caps = NULL;
+
+ self = (GESAudioUriSource *) trksrc;
+
+ track = ges_track_element_get_track (trksrc);
+
+ self->priv->decodebin = decodebin =
+ gst_element_factory_make ("uridecodebin", NULL);
+
+ if (track)
+ caps = ges_track_get_caps (track);
+
+ g_object_set (decodebin, "caps", caps,
+ "expose-all-streams", FALSE, "uri", self->uri, NULL);
+
+ return decodebin;
+}
+
+/* Extractable interface implementation */
+
+static gchar *
+ges_extractable_check_id (GType type, const gchar * id, GError ** error)
+{
+ return g_strdup (id);
+}
+
+static void
+extractable_set_asset (GESExtractable * self, GESAsset * asset)
+{
+ /* FIXME That should go into #GESTrackElement, but
+ * some work is needed to make sure it works properly */
+
+ if (ges_track_element_get_track_type (GES_TRACK_ELEMENT (self)) ==
+ GES_TRACK_TYPE_UNKNOWN) {
+ ges_track_element_set_track_type (GES_TRACK_ELEMENT (self),
+ ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET
+ (asset)));
+ }
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_URI_SOURCE_ASSET;
+ iface->check_id = ges_extractable_check_id;
+ iface->set_asset = extractable_set_asset;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESAudioUriSource, ges_audio_uri_source,
+ GES_TYPE_AUDIO_SOURCE, G_ADD_PRIVATE (GESAudioUriSource)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+
+/* GObject VMethods */
+
+static void
+ges_audio_uri_source_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESAudioUriSource *uriclip = GES_AUDIO_URI_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, uriclip->uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_audio_uri_source_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESAudioUriSource *uriclip = GES_AUDIO_URI_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ if (uriclip->uri) {
+ GST_WARNING_OBJECT (object, "Uri already set to %s", uriclip->uri);
+ return;
+ }
+ uriclip->uri = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_audio_uri_source_dispose (GObject * object)
+{
+ GESAudioUriSource *uriclip = GES_AUDIO_URI_SOURCE (object);
+
+ if (uriclip->uri)
+ g_free (uriclip->uri);
+
+ G_OBJECT_CLASS (ges_audio_uri_source_parent_class)->dispose (object);
+}
+
+static void
+ges_audio_uri_source_class_init (GESAudioUriSourceClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESAudioSourceClass *source_class = GES_AUDIO_SOURCE_CLASS (klass);
+
+ object_class->get_property = ges_audio_uri_source_get_property;
+ object_class->set_property = ges_audio_uri_source_set_property;
+ object_class->dispose = ges_audio_uri_source_dispose;
+
+ /**
+ * GESAudioUriSource:uri:
+ *
+ * The location of the file/resource to use.
+ */
+ g_object_class_install_property (object_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "uri of the resource",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ source_class->create_source = ges_audio_uri_source_create_source;
+}
+
+static void
+ges_audio_uri_source_init (GESAudioUriSource * self)
+{
+ self->priv = ges_audio_uri_source_get_instance_private (self);
+
+ g_signal_connect (self, "notify::track",
+ G_CALLBACK (ges_audio_uri_source_track_set_cb), NULL);
+}
+
+/**
+ * ges_audio_uri_source_new:
+ * @uri: the URI the source should control
+ *
+ * Creates a new #GESAudioUriSource for the provided @uri.
+ *
+ * Returns: (transfer floating) (nullable): The newly created
+ * #GESAudioUriSource, or %NULL if there was an error.
+ */
+GESAudioUriSource *
+ges_audio_uri_source_new (gchar * uri)
+{
+ return g_object_new (GES_TYPE_AUDIO_URI_SOURCE, "uri", uri, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_AUDIO_URI_SOURCE
+#define _GES_AUDIO_URI_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-audio-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUDIO_URI_SOURCE ges_audio_uri_source_get_type()
+
+#define GES_AUDIO_URI_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUDIO_URI_SOURCE, GESAudioUriSource))
+
+#define GES_AUDIO_URI_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUDIO_URI_SOURCE, GESAudioUriSourceClass))
+
+#define GES_IS_AUDIO_URI_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUDIO_URI_SOURCE))
+
+#define GES_IS_AUDIO_URI_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUDIO_URI_SOURCE))
+
+#define GES_AUDIO_URI_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUDIO_URI_SOURCE, GESAudioUriSourceClass))
+
+typedef struct _GESAudioUriSourcePrivate GESAudioUriSourcePrivate;
+
+/**
+ * GESAudioUriSource:
+ */
+struct _GESAudioUriSource {
+ /*< private >*/
+ GESAudioSource parent;
+
+ gchar *uri;
+
+ GESAudioUriSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAudioUriSourceClass {
+ /*< private >*/
+ GESAudioSourceClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_audio_uri_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_AUDIO_URI_SOURCE */
+
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+/* This class warps a GESBaseTransitionClip, letting any implementation
+ * of a GESBaseTransitionClip to be used.
+ *
+ * NOTE: This is for internal use exclusively
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-auto-transition.h"
+#include "ges-internal.h"
+enum
+{
+ DESTROY_ME,
+ LAST_SIGNAL
+};
+
+static guint auto_transition_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GESAutoTransition, ges_auto_transition, G_TYPE_OBJECT);
+
+static void
+neighbour_changed_cb (GESClip * clip, GParamSpec * arg G_GNUC_UNUSED,
+ GESAutoTransition * self)
+{
+ gint64 new_duration;
+ GESTimelineElement *parent =
+ ges_timeline_element_get_toplevel_parent (GES_TIMELINE_ELEMENT (clip));
+
+ if (ELEMENT_FLAG_IS_SET (parent, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ return;
+ }
+
+ if (parent) {
+ GESTimelineElement *prev_topparent =
+ ges_timeline_element_get_toplevel_parent (GES_TIMELINE_ELEMENT
+ (self->next_source));
+ GESTimelineElement *next_topparent =
+ ges_timeline_element_get_toplevel_parent (GES_TIMELINE_ELEMENT
+ (self->previous_source));
+
+ if (ELEMENT_FLAG_IS_SET (prev_topparent, GES_TIMELINE_ELEMENT_SET_SIMPLE) ||
+ ELEMENT_FLAG_IS_SET (next_topparent, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ return;
+ }
+
+ if (parent == prev_topparent && parent == next_topparent) {
+ GST_DEBUG_OBJECT (self,
+ "Moving all inside the same group, nothing to do");
+ return;
+ }
+ }
+
+ if (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self->next_source) !=
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self->previous_source)) {
+ GST_DEBUG_OBJECT (self, "Destroy changed layer");
+ g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
+ return;
+ }
+
+ new_duration =
+ (_START (self->previous_source) +
+ _DURATION (self->previous_source)) - _START (self->next_source);
+
+ if (new_duration <= 0 || new_duration >= _DURATION (self->previous_source)
+ || new_duration >= _DURATION (self->next_source)) {
+
+ GST_DEBUG_OBJECT (self, "Destroy %" G_GINT64_FORMAT " not a valid duration",
+ new_duration);
+ g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
+ return;
+ }
+
+ self->positioning = TRUE;
+ _set_start0 (GES_TIMELINE_ELEMENT (self->transition_clip),
+ _START (self->next_source));
+ _set_duration0 (GES_TIMELINE_ELEMENT (self->transition_clip), new_duration);
+ self->positioning = FALSE;
+}
+
+static void
+_track_changed_cb (GESTrackElement * track_element,
+ GParamSpec * arg G_GNUC_UNUSED, GESAutoTransition * self)
+{
+ if (ges_track_element_get_track (track_element) == NULL) {
+ GST_DEBUG_OBJECT (self, "Neighboor %" GST_PTR_FORMAT
+ " removed from track ... auto destructing", track_element);
+
+ g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
+ }
+
+}
+
+static void
+ges_auto_transition_init (GESAutoTransition * ges_auto_transition)
+{
+}
+
+static void
+ges_auto_transition_finalize (GObject * object)
+{
+ GESAutoTransition *self = GES_AUTO_TRANSITION (object);
+
+ g_signal_handlers_disconnect_by_func (self->previous_source,
+ neighbour_changed_cb, self);
+ g_signal_handlers_disconnect_by_func (self->next_source, neighbour_changed_cb,
+ self);
+ g_signal_handlers_disconnect_by_func (self->next_source, _track_changed_cb,
+ self);
+ g_signal_handlers_disconnect_by_func (self->previous_source,
+ _track_changed_cb, self);
+
+ g_free (self->key);
+
+ G_OBJECT_CLASS (ges_auto_transition_parent_class)->finalize (object);
+}
+
+static void
+ges_auto_transition_class_init (GESAutoTransitionClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ auto_transition_signals[DESTROY_ME] =
+ g_signal_new ("destroy-me", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ object_class->finalize = ges_auto_transition_finalize;
+}
+
+
+GESAutoTransition *
+ges_auto_transition_new (GESTrackElement * transition,
+ GESTrackElement * previous_source, GESTrackElement * next_source)
+{
+ GESAutoTransition *self = g_object_new (GES_TYPE_AUTO_TRANSITION, NULL);
+
+ self->previous_source = previous_source;
+ self->next_source = next_source;
+ self->transition = transition;
+
+ self->previous_clip =
+ GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (previous_source));
+ self->next_clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (next_source));
+ self->transition_clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (transition));
+
+ g_signal_connect (previous_source, "notify::start",
+ G_CALLBACK (neighbour_changed_cb), self);
+ g_signal_connect_after (previous_source, "notify::priority",
+ G_CALLBACK (neighbour_changed_cb), self);
+ g_signal_connect (next_source, "notify::start",
+ G_CALLBACK (neighbour_changed_cb), self);
+ g_signal_connect (next_source, "notify::priority",
+ G_CALLBACK (neighbour_changed_cb), self);
+ g_signal_connect (previous_source, "notify::duration",
+ G_CALLBACK (neighbour_changed_cb), self);
+ g_signal_connect (next_source, "notify::duration",
+ G_CALLBACK (neighbour_changed_cb), self);
+
+ g_signal_connect (next_source, "notify::track",
+ G_CALLBACK (_track_changed_cb), self);
+ g_signal_connect (previous_source, "notify::track",
+ G_CALLBACK (_track_changed_cb), self);
+
+ GST_DEBUG_OBJECT (self, "Created transition %" GST_PTR_FORMAT
+ " between %" GST_PTR_FORMAT "[%" GST_TIME_FORMAT
+ " - %" GST_TIME_FORMAT "] and: %" GST_PTR_FORMAT
+ "[%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]"
+ " in layer nb %i, start: %" GST_TIME_FORMAT " duration: %"
+ GST_TIME_FORMAT, transition, previous_source,
+ GST_TIME_ARGS (_START (previous_source)),
+ GST_TIME_ARGS (_END (previous_source)),
+ next_source,
+ GST_TIME_ARGS (_START (next_source)),
+ GST_TIME_ARGS (_END (next_source)),
+ ges_layer_get_priority (ges_clip_get_layer
+ (self->previous_clip)),
+ GST_TIME_ARGS (_START (transition)),
+ GST_TIME_ARGS (_DURATION (transition)));
+
+ self->key = g_strdup_printf ("%p%p", self->previous_source,
+ self->next_source);
+
+ return self;
+}
+
+void
+ges_auto_transition_update (GESAutoTransition * self)
+{
+ GST_INFO ("Updating info %s",
+ GES_TIMELINE_ELEMENT_NAME (self->transition_clip));
+ neighbour_changed_cb (self->previous_clip, NULL, self);
+}
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GES_AUTO_TRANSITION_H_
+#define _GES_AUTO_TRANSITION_H_
+
+#include <glib-object.h>
+#include "ges-track-element.h"
+#include "ges-clip.h"
+#include "ges-layer.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUTO_TRANSITION (ges_auto_transition_get_type ())
+#define GES_AUTO_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUTO_TRANSITION, GESAutoTransition))
+#define GES_AUTO_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUTO_TRANSITION, GESAutoTransitionClass))
+#define GES_IS_AUTO_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUTO_TRANSITION))
+#define GES_IS_AUTO_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUTO_TRANSITION))
+#define GES_AUTO_TRANSITION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUTO_TRANSITION, GESAutoTransitionClass))
+
+typedef struct _GESAutoTransitionClass GESAutoTransitionClass;
+typedef struct _GESAutoTransition GESAutoTransition;
+
+
+
+struct _GESAutoTransitionClass
+{
+ GObjectClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAutoTransition
+{
+ GObject parent_instance;
+
+ /* <read only and construct only> */
+ GESTrackElement *previous_source;
+ GESTrackElement *next_source;
+ GESTrackElement *transition;
+
+ GESLayer *layer;
+
+ GESClip *previous_clip;
+ GESClip *next_clip;
+ GESClip *transition_clip;
+ gboolean positioning;
+
+ gchar *key;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+G_GNUC_INTERNAL GType ges_auto_transition_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL void ges_auto_transition_update (GESAutoTransition *self);
+G_GNUC_INTERNAL GESAutoTransition * ges_auto_transition_new (GESTrackElement * transition,
+ GESTrackElement * previous_source,
+ GESTrackElement * next_source);
+
+G_END_DECLS
+#endif /* _GES_AUTO_TRANSITION_H_ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2011 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gesbaseeffectclip
+ * @title: GESBaseEffectClip
+ * @short_description: An effect in a GESLayer
+ *
+ * The effect will be applied on the sources that have lower priorities
+ * (higher number) between the inpoint and the end of it.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+#include "ges-internal.h"
+#include "ges-types.h"
+
+struct _GESBaseEffectClipPrivate
+{
+ void *nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseEffectClip, ges_base_effect_clip,
+ GES_TYPE_OPERATION_CLIP);
+
+static void
+ges_base_effect_clip_class_init (GESBaseEffectClipClass * klass)
+{
+}
+
+static void
+ges_base_effect_clip_init (GESBaseEffectClip * self)
+{
+ self->priv = ges_base_effect_clip_get_instance_private (self);
+
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2011 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_BASE_EFFECT_CLIP
+#define _GES_BASE_EFFECT_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_BASE_EFFECT_CLIP ges_base_effect_clip_get_type()
+
+#define GES_BASE_EFFECT_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_BASE_EFFECT_CLIP, GESBaseEffectClip))
+
+#define GES_BASE_EFFECT_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_BASE_EFFECT_CLIP, GESBaseEffectClipClass))
+
+#define GES_IS_BASE_EFFECT_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_BASE_EFFECT_CLIP))
+
+#define GES_IS_BASE_EFFECT_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_BASE_EFFECT_CLIP))
+
+#define GES_BASE_EFFECT_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_BASE_EFFECT_CLIP, GESBaseEffectClipClass))
+
+typedef struct _GESBaseEffectClipPrivate GESBaseEffectClipPrivate;
+
+/**
+ * GESBaseEffectClip:
+ */
+struct _GESBaseEffectClip {
+ /*< private >*/
+ GESOperationClip parent;
+
+ GESBaseEffectClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESBaseEffectClipClass:
+ *
+ */
+
+struct _GESBaseEffectClipClass {
+ /*< private >*/
+ GESOperationClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_base_effect_clip_get_type (void);
+
+G_END_DECLS
+#endif /* _GES_BASE_EFFECT_CLIP */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesbaseeffect
+ * @title: GESBaseEffect
+ * @short_description: adds an effect to a stream in a GESSourceClip or a
+ * GESLayer
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gprintf.h>
+
+#include "ges-utils.h"
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-base-effect.h"
+
+struct _GESBaseEffectPrivate
+{
+ void *nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseEffect, ges_base_effect,
+ GES_TYPE_OPERATION);
+
+static void
+ges_base_effect_class_init (GESBaseEffectClass * klass)
+{
+}
+
+static void
+ges_base_effect_init (GESBaseEffect * self)
+{
+ self->priv = ges_base_effect_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_BASE_EFFECT
+#define _GES_BASE_EFFECT
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-operation.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_BASE_EFFECT ges_base_effect_get_type()
+#define GES_BASE_EFFECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_BASE_EFFECT, GESBaseEffect))
+#define GES_BASE_EFFECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_BASE_EFFECT, GESBaseEffectClass))
+#define GES_IS_BASE_EFFECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_BASE_EFFECT))
+#define GES_IS_BASE_EFFECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_BASE_EFFECT))
+#define GES_BASE_EFFECT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_BASE_EFFECT, GESBaseEffectClass))
+
+
+typedef struct _GESBaseEffectPrivate GESBaseEffectPrivate;
+
+/**
+ * GESBaseEffect:
+ */
+struct _GESBaseEffect
+{
+ /*< private > */
+ GESOperation parent;
+ GESBaseEffectPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESBaseEffectClass:
+ * @parent_class: parent class
+ */
+
+struct _GESBaseEffectClass
+{
+ /*< private > */
+ GESOperationClass parent_class;
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+
+};
+
+GES_API
+GType ges_base_effect_get_type (void);
+
+G_END_DECLS
+#endif /* _GES_BASE_EFFECT */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gesbasetransitionclip
+ * @title: GESBaseTransitionClip
+ * @short_description: Base classes for transitions
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+#include "ges-internal.h"
+
+struct _GESBaseTransitionClipPrivate
+{
+ /* Dummy variable */
+ void *nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseTransitionClip,
+ ges_base_transition_clip, GES_TYPE_OPERATION_CLIP);
+
+static void
+ges_base_transition_clip_class_init (GESBaseTransitionClipClass * klass)
+{
+}
+
+static void
+ges_base_transition_clip_init (GESBaseTransitionClip * self)
+{
+ self->priv = ges_base_transition_clip_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_BASE_TRANSITION_CLIP
+#define _GES_BASE_TRANSITION_CLIP
+
+#include "ges-operation-clip.h"
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_BASE_TRANSITION_CLIP ges_base_transition_clip_get_type()
+
+#define GES_BASE_TRANSITION_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_BASE_TRANSITION_CLIP, GESBaseTransitionClip))
+
+#define GES_BASE_TRANSITION_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_BASE_TRANSITION_CLIP, GESBaseTransitionClipClass))
+
+#define GES_IS_BASE_TRANSITION_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_BASE_TRANSITION_CLIP))
+
+#define GES_IS_BASE_TRANSITION_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_BASE_TRANSITION_CLIP))
+
+#define GES_BASE_TRANSITION_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_BASE_TRANSITION_CLIP, GESBaseTransitionClipClass))
+
+typedef struct _GESBaseTransitionClipPrivate GESBaseTransitionClipPrivate;
+
+/**
+ * GESBaseTransitionClip:
+ */
+struct _GESBaseTransitionClip {
+ /*< private >*/
+ GESOperationClip parent;
+
+ /*< private >*/
+ GESBaseTransitionClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESBaseTransitionClipClass:
+ *
+ */
+
+struct _GESBaseTransitionClipClass {
+ /*< private >*/
+ GESOperationClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_base_transition_clip_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_BASE_TRANSITION_CLIP */
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges.h"
+#include "ges-internal.h"
+
+GST_DEBUG_CATEGORY_STATIC (base_xml_formatter);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT base_xml_formatter
+
+#define parent_class ges_base_xml_formatter_parent_class
+
+#define _GET_PRIV(o)\
+ (((GESBaseXmlFormatter*) o)->priv)
+
+
+static gboolean _loading_done_cb (GESFormatter * self);
+
+typedef struct PendingEffects
+{
+ gchar *track_id;
+ GESTrackElement *trackelement;
+ GstStructure *children_properties;
+ GstStructure *properties;
+
+} PendingEffects;
+
+typedef struct PendingBinding
+{
+ gchar *track_id;
+ GstControlSource *source;
+ gchar *propname;
+ gchar *binding_type;
+} PendingBinding;
+
+typedef struct PendingChildProperties
+{
+ gchar *track_id;
+ GstStructure *structure;
+} PendingChildProperties;
+
+typedef struct PendingGroup
+{
+ GESGroup *group;
+
+ GList *pending_children;
+} PendingGroup;
+
+typedef struct PendingClip
+{
+ gchar *id;
+ guint layer_prio;
+ GstClockTime start;
+ GstClockTime inpoint;
+ GESAsset *asset;
+ GstClockTime duration;
+ GESTrackType track_types;
+ GESLayer *layer;
+
+ GstStructure *properties;
+ GstStructure *children_properties;
+ gchar *metadatas;
+
+ GList *effects;
+
+ GList *pending_bindings;
+
+ GList *children_props;
+
+ /* TODO Implement asset effect management
+ * PendingTrackElements *track_elements; */
+} PendingClip;
+
+typedef struct LayerEntry
+{
+ GESLayer *layer;
+ gboolean auto_trans;
+} LayerEntry;
+
+typedef struct PendingAsset
+{
+ GESFormatter *formatter;
+ gchar *metadatas;
+ GstStructure *properties;
+ gchar *proxy_id;
+} PendingAsset;
+
+struct _GESBaseXmlFormatterPrivate
+{
+ GMarkupParseContext *parsecontext;
+ gboolean check_only;
+
+ /* Asset.id -> PendingClip */
+ GHashTable *assetid_pendingclips;
+
+ /* Clip.ID -> Pending */
+ GHashTable *clipid_pendings;
+
+ /* Clip.ID -> Clip */
+ GHashTable *containers;
+
+ /* ID -> track */
+ GHashTable *tracks;
+
+ /* layer.prio -> LayerEntry */
+ GHashTable *layers;
+
+ /* List of asset waited to be created */
+ GList *pending_assets;
+
+ /* current track element */
+ GESTrackElement *current_track_element;
+
+ GESClip *current_clip;
+ PendingClip *current_pending_clip;
+
+ gboolean timeline_auto_transition;
+
+ GList *groups;
+};
+
+static void
+_free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend);
+
+static void
+_free_layer_entry (LayerEntry * entry)
+{
+ gst_object_unref (entry->layer);
+ g_slice_free (LayerEntry, entry);
+}
+
+static void
+_free_pending_group (PendingGroup * pgroup)
+{
+ if (pgroup->group)
+ g_object_unref (pgroup->group);
+ g_list_free_full (pgroup->pending_children, g_free);
+ g_slice_free (PendingGroup, pgroup);
+}
+
+/*
+enum
+{
+ PROP_0,
+ PROP_LAST
+};
+static GParamSpec *properties[PROP_LAST];
+
+enum
+{
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL];
+*/
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseXmlFormatter,
+ ges_base_xml_formatter, GES_TYPE_FORMATTER);
+
+static GMarkupParseContext *
+create_parser_context (GESBaseXmlFormatter * self, const gchar * uri,
+ GError ** error)
+{
+ gsize xmlsize;
+ GFile *file = NULL;
+ gchar *xmlcontent = NULL;
+ GMarkupParseContext *parsecontext = NULL;
+ GESBaseXmlFormatterClass *self_class =
+ GES_BASE_XML_FORMATTER_GET_CLASS (self);
+
+ GError *err = NULL;
+
+ GST_DEBUG_OBJECT (self, "loading xml from %s", uri);
+
+ file = g_file_new_for_uri (uri);
+
+ /* TODO Handle GCancellable */
+ if (!g_file_query_exists (file, NULL)) {
+ err = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
+ "Invalid URI: \"%s\"", uri);
+ goto failed;
+ }
+
+ if (!g_file_load_contents (file, NULL, &xmlcontent, &xmlsize, NULL, &err))
+ goto failed;
+
+ if (g_strcmp0 (xmlcontent, "") == 0)
+ goto failed;
+
+ parsecontext = g_markup_parse_context_new (&self_class->content_parser,
+ G_MARKUP_TREAT_CDATA_AS_TEXT, self, NULL);
+
+ if (g_markup_parse_context_parse (parsecontext, xmlcontent, xmlsize,
+ &err) == FALSE)
+ goto failed;
+
+ if (!g_markup_parse_context_end_parse (parsecontext, &err))
+ goto failed;
+
+
+done:
+ g_free (xmlcontent);
+ g_object_unref (file);
+
+ return parsecontext;
+
+failed:
+ GST_WARNING ("failed to load contents from \"%s\"", uri);
+ g_propagate_error (error, err);
+
+ if (parsecontext) {
+ g_markup_parse_context_free (parsecontext);
+ parsecontext = NULL;
+ }
+
+ goto done;
+}
+
+/***********************************************
+ * *
+ * GESFormatter virtual methods implementation *
+ * *
+ ***********************************************/
+
+static gboolean
+_can_load_uri (GESFormatter * dummy_formatter, const gchar * uri,
+ GError ** error)
+{
+ GMarkupParseContext *ctx;
+ GESBaseXmlFormatter *self = GES_BASE_XML_FORMATTER (dummy_formatter);
+
+ /* we create a temporary object so we can use it as a context */
+ _GET_PRIV (self)->check_only = TRUE;
+
+
+ ctx = create_parser_context (self, uri, error);
+ if (!ctx)
+ return FALSE;
+
+ g_markup_parse_context_free (ctx);
+ return TRUE;
+}
+
+static gboolean
+_load_from_uri (GESFormatter * self, GESTimeline * timeline, const gchar * uri,
+ GError ** error)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ ges_timeline_set_auto_transition (timeline, FALSE);
+
+ priv->parsecontext =
+ create_parser_context (GES_BASE_XML_FORMATTER (self), uri, error);
+
+ if (!priv->parsecontext)
+ return FALSE;
+
+ if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
+ priv->pending_assets == NULL)
+ g_idle_add ((GSourceFunc) _loading_done_cb, g_object_ref (self));
+
+ return TRUE;
+}
+
+static gboolean
+_save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
+ const gchar * uri, gboolean overwrite, GError ** error)
+{
+ GFile *file;
+ gboolean ret;
+ GString *str;
+ GOutputStream *stream;
+ GError *lerror = NULL;
+
+ g_return_val_if_fail (formatter->project, FALSE);
+
+ file = g_file_new_for_uri (uri);
+ stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL,
+ &lerror));
+ if (stream == NULL) {
+ if (overwrite && lerror->code == G_IO_ERROR_EXISTS) {
+ g_clear_error (&lerror);
+ stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
+ G_FILE_CREATE_NONE, NULL, &lerror));
+ }
+
+ if (stream == NULL)
+ goto failed_opening_file;
+ }
+
+ str = GES_BASE_XML_FORMATTER_GET_CLASS (formatter)->save (formatter,
+ timeline, error);
+
+ if (str == NULL)
+ goto serialization_failed;
+
+ ret = g_output_stream_write_all (stream, str->str, str->len, NULL,
+ NULL, &lerror);
+ ret = g_output_stream_close (stream, NULL, &lerror);
+
+ if (ret == FALSE)
+ GST_WARNING_OBJECT (formatter, "Could not save %s because: %s", uri,
+ lerror->message);
+
+ g_string_free (str, TRUE);
+ gst_object_unref (file);
+ gst_object_unref (stream);
+
+ if (lerror)
+ g_propagate_error (error, lerror);
+
+ return ret;
+
+serialization_failed:
+ gst_object_unref (file);
+
+ g_output_stream_close (stream, NULL, NULL);
+ gst_object_unref (stream);
+ if (lerror)
+ g_propagate_error (error, lerror);
+
+ return FALSE;
+
+failed_opening_file:
+ gst_object_unref (file);
+
+ GST_WARNING_OBJECT (formatter, "Could not open %s because: %s", uri,
+ lerror->message);
+
+ if (lerror)
+ g_propagate_error (error, lerror);
+
+ return FALSE;
+}
+
+/***********************************************
+ * *
+ * GOBject virtual methods implementation *
+ * *
+ ***********************************************/
+
+static void
+_dispose (GObject * object)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object);
+ GList *pendings, *pending_clips_lists;
+
+ pending_clips_lists = g_hash_table_get_values (priv->assetid_pendingclips);
+ for (pendings = pending_clips_lists; pendings; pendings = pendings->next)
+ g_list_free_full (pendings, (GDestroyNotify) _free_pending_clip);
+ g_list_free (pending_clips_lists);
+
+ g_clear_pointer (&priv->assetid_pendingclips, g_hash_table_unref);
+ g_clear_pointer (&priv->containers, g_hash_table_unref);
+ g_clear_pointer (&priv->clipid_pendings, g_hash_table_unref);
+ g_clear_pointer (&priv->tracks, g_hash_table_unref);
+ g_clear_pointer (&priv->layers, g_hash_table_unref);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+_finalize (GObject * object)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object);
+
+ if (priv->parsecontext != NULL)
+ g_markup_parse_context_free (priv->parsecontext);
+
+ g_list_free_full (priv->groups, (GDestroyNotify) _free_pending_group);
+ priv->groups = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+ges_base_xml_formatter_init (GESBaseXmlFormatter * self)
+{
+ GESBaseXmlFormatterPrivate *priv;
+
+ self->priv = ges_base_xml_formatter_get_instance_private (self);
+
+ priv = self->priv;
+
+ priv->check_only = FALSE;
+ priv->parsecontext = NULL;
+ priv->pending_assets = NULL;
+
+ /* The PendingClip are owned by the assetid_pendingclips table */
+ priv->assetid_pendingclips = g_hash_table_new_full (g_str_hash,
+ g_str_equal, g_free, NULL);
+ priv->clipid_pendings = g_hash_table_new_full (g_str_hash,
+ g_str_equal, g_free, NULL);
+ priv->containers = g_hash_table_new_full (g_str_hash,
+ g_str_equal, g_free, gst_object_unref);
+ priv->tracks = g_hash_table_new_full (g_str_hash,
+ g_str_equal, g_free, gst_object_unref);
+ priv->layers = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
+ priv->current_track_element = NULL;
+ priv->current_clip = NULL;
+ priv->current_pending_clip = NULL;
+ priv->timeline_auto_transition = FALSE;
+}
+
+static void
+ges_base_xml_formatter_class_init (GESBaseXmlFormatterClass * self_class)
+{
+ GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (self_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (self_class);
+
+ object_class->dispose = _dispose;
+ object_class->finalize = _finalize;
+
+ formatter_klass->can_load_uri = _can_load_uri;
+ formatter_klass->load_from_uri = _load_from_uri;
+ formatter_klass->save_to_uri = _save_to_uri;
+
+ self_class->save = NULL;
+
+ GST_DEBUG_CATEGORY_INIT (base_xml_formatter, "base-xml-formatter",
+ GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "Base XML Formatter");
+}
+
+/***********************************************
+ * *
+ * Private methods *
+ * *
+ ***********************************************/
+
+
+static GESTrackElement *
+_get_element_by_track_id (GESBaseXmlFormatterPrivate * priv,
+ const gchar * track_id, GESClip * clip)
+{
+ GESTrack *track = g_hash_table_lookup (priv->tracks, track_id);
+
+ return ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
+}
+
+static void
+_set_auto_transition (gpointer prio, LayerEntry * entry, gpointer udata)
+{
+ ges_layer_set_auto_transition (entry->layer, entry->auto_trans);
+}
+
+static void
+_add_all_groups (GESFormatter * self)
+{
+ GList *tmp;
+ GESTimelineElement *child;
+ GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
+
+ for (tmp = priv->groups; tmp; tmp = tmp->next) {
+ GList *lchild;
+ PendingGroup *pgroup = tmp->data;
+
+ timeline_add_group (self->timeline, pgroup->group);
+
+ for (lchild = ((PendingGroup *) tmp->data)->pending_children; lchild;
+ lchild = lchild->next) {
+ child = g_hash_table_lookup (priv->containers, lchild->data);
+
+ GST_DEBUG_OBJECT (tmp->data, "Adding %s child %" GST_PTR_FORMAT " %s",
+ (const gchar *) lchild->data, child,
+ GES_TIMELINE_ELEMENT_NAME (child));
+ ges_container_add (GES_CONTAINER (pgroup->group), child);
+ }
+ pgroup->group = NULL;
+ }
+
+ g_list_free_full (priv->groups, (GDestroyNotify) _free_pending_group);
+ priv->groups = NULL;
+}
+
+static void
+_loading_done (GESFormatter * self)
+{
+ GList *assets, *tmp;
+ GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
+
+ _add_all_groups (self);
+
+ if (priv->parsecontext)
+ g_markup_parse_context_free (priv->parsecontext);
+ priv->parsecontext = NULL;
+
+ ges_timeline_set_auto_transition (self->timeline,
+ priv->timeline_auto_transition);
+
+ /* Go over all assets and make sure that all proxies we were 'trying' to set are finally
+ * properly set */
+ assets = ges_project_list_assets (self->project, GES_TYPE_EXTRACTABLE);
+ for (tmp = assets; tmp; tmp = tmp->next) {
+ ges_asset_set_proxy (NULL, tmp->data);
+ }
+ g_list_free_full (assets, g_object_unref);
+
+ g_hash_table_foreach (priv->layers, (GHFunc) _set_auto_transition, NULL);
+ ges_project_set_loaded (self->project, self);
+}
+
+static gboolean
+_loading_done_cb (GESFormatter * self)
+{
+ _loading_done (self);
+ gst_object_unref (self);
+
+ return FALSE;
+}
+
+static gboolean
+_set_child_property (GQuark field_id, const GValue * value,
+ GESTimelineElement * tlelement)
+{
+ GParamSpec *pspec;
+ GObject *object;
+
+ /* FIXME: error handling? */
+ if (!ges_timeline_element_lookup_child (tlelement,
+ g_quark_to_string (field_id), &object, &pspec)) {
+#ifndef GST_DISABLE_GST_DEBUG
+ gchar *tmp = gst_value_serialize (value);
+ GST_ERROR_OBJECT (tlelement, "Could not set %s=%s",
+ g_quark_to_string (field_id), tmp);
+ g_free (tmp);
+#endif
+ return TRUE;
+ }
+
+ g_object_set_property (G_OBJECT (object), pspec->name, value);
+ g_param_spec_unref (pspec);
+ gst_object_unref (object);
+ return TRUE;
+}
+
+gboolean
+set_property_foreach (GQuark field_id, const GValue * value, GObject * object)
+{
+ g_object_set_property (object, g_quark_to_string (field_id), value);
+ return TRUE;
+}
+
+static inline GESClip *
+_add_object_to_layer (GESBaseXmlFormatterPrivate * priv, const gchar * id,
+ GESLayer * layer, GESAsset * asset, GstClockTime start,
+ GstClockTime inpoint, GstClockTime duration,
+ GESTrackType track_types, const gchar * metadatas,
+ GstStructure * properties, GstStructure * children_properties)
+{
+ GESClip *clip = ges_layer_add_asset (layer,
+ asset, start, inpoint, duration, track_types);
+
+ if (clip == NULL) {
+ GST_WARNING_OBJECT (clip, "Could not add object from asset: %s",
+ ges_asset_get_id (asset));
+
+ return NULL;
+ }
+
+ if (metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER (clip),
+ metadatas);
+
+ if (properties)
+ gst_structure_foreach (properties,
+ (GstStructureForeachFunc) set_property_foreach, clip);
+
+ if (children_properties)
+ gst_structure_foreach (children_properties,
+ (GstStructureForeachFunc) _set_child_property, clip);
+
+ g_hash_table_insert (priv->containers, g_strdup (id), gst_object_ref (clip));
+ return clip;
+}
+
+static void
+_add_track_element (GESFormatter * self, GESClip * clip,
+ GESTrackElement * trackelement, const gchar * track_id,
+ GstStructure * children_properties, GstStructure * properties)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+ GESTrack *track = g_hash_table_lookup (priv->tracks, track_id);
+
+ if (track == NULL) {
+ GST_WARNING_OBJECT (self, "No track with id %s, can not add trackelement",
+ track_id);
+ gst_object_unref (trackelement);
+ return;
+ }
+
+ GST_DEBUG_OBJECT (self, "Adding track_element: %" GST_PTR_FORMAT
+ " To : %" GST_PTR_FORMAT, trackelement, clip);
+
+ ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (trackelement));
+ gst_structure_foreach (children_properties,
+ (GstStructureForeachFunc) _set_child_property, trackelement);
+
+ if (properties) {
+ /* We do not serialize the priority anymore, and we should never have. */
+ gst_structure_remove_field (properties, "priority");
+ gst_structure_foreach (properties,
+ (GstStructureForeachFunc) set_property_foreach, trackelement);
+ }
+}
+
+static void
+_free_pending_children_props (PendingChildProperties * pend)
+{
+ g_free (pend->track_id);
+ if (pend->structure)
+ gst_structure_free (pend->structure);
+}
+
+static void
+_free_pending_binding (PendingBinding * pend)
+{
+ g_free (pend->propname);
+ g_free (pend->binding_type);
+ g_free (pend->track_id);
+}
+
+static void
+_free_pending_effect (PendingEffects * pend)
+{
+ g_free (pend->track_id);
+ gst_object_unref (pend->trackelement);
+ if (pend->children_properties)
+ gst_structure_free (pend->children_properties);
+ if (pend->properties)
+ gst_structure_free (pend->properties);
+
+ g_slice_free (PendingEffects, pend);
+}
+
+static void
+_free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend)
+{
+ gst_object_unref (pend->layer);
+ if (pend->properties)
+ gst_structure_free (pend->properties);
+ g_list_free_full (pend->effects, (GDestroyNotify) _free_pending_effect);
+ g_list_free_full (pend->pending_bindings,
+ (GDestroyNotify) _free_pending_binding);
+ g_list_free_full (pend->children_props,
+ (GDestroyNotify) _free_pending_children_props);
+ g_hash_table_remove (priv->clipid_pendings, pend->id);
+ g_free (pend->id);
+ g_slice_free (PendingClip, pend);
+}
+
+static void
+_free_pending_asset (GESBaseXmlFormatterPrivate * priv, PendingAsset * passet)
+{
+ g_free (passet->metadatas);
+ g_free (passet->proxy_id);
+ if (passet->properties)
+ gst_structure_free (passet->properties);
+
+ priv->pending_assets = g_list_remove (priv->pending_assets, passet);
+ g_slice_free (PendingAsset, passet);
+}
+
+static void
+_add_children_properties (GESBaseXmlFormatterPrivate * priv, GList * childprops,
+ GESClip * clip)
+{
+ GList *tmpchildprops;
+
+ for (tmpchildprops = childprops; tmpchildprops;
+ tmpchildprops = tmpchildprops->next) {
+ PendingChildProperties *pchildprops = tmpchildprops->data;
+ GESTrackElement *element =
+ _get_element_by_track_id (priv, pchildprops->track_id, clip);
+ if (element && pchildprops->structure)
+ gst_structure_foreach (pchildprops->structure,
+ (GstStructureForeachFunc) _set_child_property, element);
+ }
+}
+
+static void
+_add_pending_bindings (GESBaseXmlFormatterPrivate * priv, GList * bindings,
+ GESClip * clip)
+{
+ GList *tmpbinding;
+
+ for (tmpbinding = bindings; tmpbinding; tmpbinding = tmpbinding->next) {
+ PendingBinding *pbinding = tmpbinding->data;
+ GESTrackElement *element =
+ _get_element_by_track_id (priv, pbinding->track_id, clip);
+ if (element)
+ ges_track_element_set_control_source (element,
+ pbinding->source, pbinding->propname, pbinding->binding_type);
+ }
+}
+
+static void
+new_asset_cb (GESAsset * source, GAsyncResult * res, PendingAsset * passet)
+{
+ GError *error = NULL;
+ gchar *possible_id = NULL;
+ GList *tmp, *pendings = NULL;
+ GESFormatter *self = passet->formatter;
+ const gchar *id = ges_asset_get_id (source);
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+ GESAsset *asset = ges_asset_request_finish (res, &error);
+
+ if (error) {
+ GST_LOG_OBJECT (self, "Error %s creating asset id: %s", error->message, id);
+
+ /* We set the metas on the Asset to give hints to the user */
+ if (passet->metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER (source),
+ passet->metadatas);
+ if (passet->properties)
+ gst_structure_foreach (passet->properties,
+ (GstStructureForeachFunc) set_property_foreach, source);
+
+ possible_id = ges_project_try_updating_id (GES_FORMATTER (self)->project,
+ source, error);
+
+ if (possible_id == NULL) {
+ GST_WARNING_OBJECT (self, "Abandoning creation of asset %s with ID %s"
+ "- Error: %s", g_type_name (G_OBJECT_TYPE (source)), id,
+ error->message);
+
+ pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
+ _free_pending_asset (priv, passet);
+ goto done;
+ }
+
+ /* We got a possible ID replacement for that asset, create it, and
+ * make sure the assetid_pendingclips will use it */
+ ges_asset_request_async (ges_asset_get_extractable_type (source),
+ possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, passet);
+ ges_project_add_loading_asset (GES_FORMATTER (self)->project,
+ ges_asset_get_extractable_type (source), possible_id);
+
+ pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
+ if (pendings) {
+ g_hash_table_remove (priv->assetid_pendingclips, id);
+ g_hash_table_insert (priv->assetid_pendingclips,
+ g_strdup (possible_id), pendings);
+
+ /* pendings should no be freed */
+ pendings = NULL;
+ }
+ goto done;
+ }
+
+ if (passet->proxy_id) {
+ /* We set the URI to be used as a proxy,
+ * this will finally be set as the proxy when we
+ * are done loading all assets */
+ ges_asset_try_proxy (asset, passet->proxy_id);
+ }
+
+ /* now that we have the GESAsset, we create the GESClips */
+ pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
+ GST_DEBUG_OBJECT (self, "Asset created with ID %s, now creating pending "
+ " Clips, nb pendings: %i", id, g_list_length (pendings));
+ for (tmp = pendings; tmp; tmp = tmp->next) {
+ GList *tmpeffect;
+ GESClip *clip;
+ PendingClip *pend = (PendingClip *) tmp->data;
+
+ clip =
+ _add_object_to_layer (priv, pend->id, pend->layer, asset,
+ pend->start, pend->inpoint, pend->duration, pend->track_types,
+ pend->metadatas, pend->properties, pend->children_properties);
+
+ if (clip == NULL)
+ continue;
+
+ _add_children_properties (priv, pend->children_props, clip);
+ _add_pending_bindings (priv, pend->pending_bindings, clip);
+
+ GST_DEBUG_OBJECT (self, "Adding %i effect to new object",
+ g_list_length (pend->effects));
+ for (tmpeffect = pend->effects; tmpeffect; tmpeffect = tmpeffect->next) {
+ PendingEffects *peffect = (PendingEffects *) tmpeffect->data;
+
+ /* We keep a ref as _free_pending_effect unrefs it */
+ _add_track_element (self, clip, gst_object_ref (peffect->trackelement),
+ peffect->track_id, peffect->children_properties, peffect->properties);
+ }
+ }
+
+ /* And now add to the project */
+ ges_project_add_asset (self->project, asset);
+ gst_object_unref (self);
+
+ _free_pending_asset (priv, passet);
+
+done:
+ if (asset)
+ gst_object_unref (asset);
+ if (possible_id)
+ g_free (possible_id);
+
+ g_clear_error (&error);
+
+ if (pendings) {
+ for (tmp = pendings; tmp; tmp = tmp->next)
+ _free_pending_clip (priv, tmp->data);
+ g_hash_table_remove (priv->assetid_pendingclips, id);
+ g_list_free (pendings);
+ }
+
+ if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
+ priv->pending_assets == NULL)
+ _loading_done (self);
+}
+
+GstElement *
+get_element_for_encoding_profile (GstEncodingProfile * prof,
+ GstElementFactoryListType type)
+{
+ GstEncodingProfile *prof_copy;
+ GstElement *encodebin;
+ GList *tmp;
+ GstElement *element = NULL;
+
+ prof_copy = gst_encoding_profile_copy (prof);
+
+ gst_encoding_profile_set_presence (prof_copy, 1);
+ gst_encoding_profile_set_preset (prof_copy, NULL);
+
+ encodebin = gst_element_factory_make ("encodebin", NULL);
+ g_object_set (encodebin, "profile", prof_copy, NULL);
+
+ GST_OBJECT_LOCK (encodebin);
+ for (tmp = GST_BIN (encodebin)->children; tmp; tmp = tmp->next) {
+ GstElementFactory *factory;
+ factory = gst_element_get_factory (GST_ELEMENT (tmp->data));
+
+ if (factory && gst_element_factory_list_is_type (factory, type)) {
+ element = GST_ELEMENT (tmp->data);
+ gst_object_ref (element);
+ break;
+ }
+ }
+ GST_OBJECT_UNLOCK (encodebin);
+ gst_object_unref (encodebin);
+
+ gst_encoding_profile_unref (prof_copy);
+
+ return element;
+}
+
+static GstEncodingProfile *
+_create_profile (GESBaseXmlFormatter * self,
+ const gchar * type, const gchar * parent, const gchar * name,
+ const gchar * description, GstCaps * format, const gchar * preset,
+ GstStructure * preset_properties, const gchar * preset_name, gint id,
+ guint presence, GstCaps * restriction, guint pass,
+ gboolean variableframerate, gboolean enabled)
+{
+ GstEncodingProfile *profile = NULL;
+
+ if (!g_strcmp0 (type, "container")) {
+ profile = GST_ENCODING_PROFILE (gst_encoding_container_profile_new (name,
+ description, format, preset));
+ gst_encoding_profile_set_preset_name (profile, preset_name);
+ } else if (!g_strcmp0 (type, "video")) {
+ GstEncodingVideoProfile *sprof = gst_encoding_video_profile_new (format,
+ preset, restriction, presence);
+
+ gst_encoding_video_profile_set_variableframerate (sprof, variableframerate);
+ gst_encoding_video_profile_set_pass (sprof, pass);
+
+ profile = GST_ENCODING_PROFILE (sprof);
+ } else if (!g_strcmp0 (type, "audio")) {
+ profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (format,
+ preset, restriction, presence));
+ } else {
+ GST_ERROR_OBJECT (self, "Unknown profile format '%s'", type);
+
+ return NULL;
+ }
+
+ if (!g_strcmp0 (type, "video") || !g_strcmp0 (type, "audio")) {
+ gst_encoding_profile_set_name (profile, name);
+ gst_encoding_profile_set_enabled (profile, enabled);
+ gst_encoding_profile_set_description (profile, description);
+ gst_encoding_profile_set_preset_name (profile, preset_name);
+ }
+
+ if (preset && preset_properties) {
+ GstElement *element;
+
+ if (!g_strcmp0 (type, "container")) {
+ element = get_element_for_encoding_profile (profile,
+ GST_ELEMENT_FACTORY_TYPE_MUXER);
+ } else {
+ element = get_element_for_encoding_profile (profile,
+ GST_ELEMENT_FACTORY_TYPE_ENCODER);
+ }
+
+ if (G_UNLIKELY (!element || !GST_IS_PRESET (element))) {
+ GST_WARNING_OBJECT (element, "Element is not a GstPreset");
+ goto done;
+ }
+
+ /* If the preset doesn't exist on the system, create it */
+ if (!gst_preset_load_preset (GST_PRESET (element), preset)) {
+ gst_structure_foreach (preset_properties,
+ (GstStructureForeachFunc) set_property_foreach, element);
+
+ if (!gst_preset_save_preset (GST_PRESET (element), preset)) {
+ GST_WARNING_OBJECT (element, "Could not save preset %s", preset);
+ }
+ }
+
+ done:
+ if (element)
+ gst_object_unref (element);
+ }
+
+ return profile;
+}
+
+/***********************************************
+ * *
+ * Public methods *
+ * *
+ ***********************************************/
+
+void
+ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
+ const gchar * id, GType extractable_type, GstStructure * properties,
+ const gchar * metadatas, const gchar * proxy_id, GError ** error)
+{
+ PendingAsset *passet;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ return;
+
+ passet = g_slice_new0 (PendingAsset);
+ passet->metadatas = g_strdup (metadatas);
+ passet->proxy_id = g_strdup (proxy_id);
+ passet->formatter = gst_object_ref (self);
+ if (properties)
+ passet->properties = gst_structure_copy (properties);
+
+ ges_asset_request_async (extractable_type, id, NULL,
+ (GAsyncReadyCallback) new_asset_cb, passet);
+ ges_project_add_loading_asset (GES_FORMATTER (self)->project,
+ extractable_type, id);
+ priv->pending_assets = g_list_prepend (priv->pending_assets, passet);
+}
+
+void
+ges_base_xml_formatter_add_clip (GESBaseXmlFormatter * self,
+ const gchar * id, const char *asset_id, GType type, GstClockTime start,
+ GstClockTime inpoint, GstClockTime duration,
+ guint layer_prio, GESTrackType track_types, GstStructure * properties,
+ GstStructure * children_properties,
+ const gchar * metadatas, GError ** error)
+{
+ GESAsset *asset;
+ GESClip *nclip;
+ LayerEntry *entry;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ return;
+
+ entry = g_hash_table_lookup (priv->layers, GINT_TO_POINTER (layer_prio));
+ if (entry == NULL) {
+ g_set_error (error, GES_ERROR, GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE,
+ "We got a Clip in a layer"
+ " that does not exist, something is wrong either in the project file or"
+ " in %s", g_type_name (G_OBJECT_TYPE (self)));
+ return;
+ }
+
+ /* We do not want the properties that are passed to layer-add_asset to be reset */
+ if (properties)
+ gst_structure_remove_fields (properties, "supported-formats",
+ "inpoint", "start", "duration", NULL);
+
+ asset = ges_asset_request (type, asset_id, NULL);
+ if (asset == NULL) {
+ gchar *real_id;
+ PendingClip *pclip;
+ GList *pendings;
+
+ real_id = ges_extractable_type_check_id (type, asset_id, error);
+ if (real_id == NULL) {
+ if (*error == NULL)
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Object type '%s' with Asset id: %s not be created'",
+ g_type_name (type), asset_id);
+
+ return;
+ }
+
+ pendings = g_hash_table_lookup (priv->assetid_pendingclips, asset_id);
+
+ pclip = g_slice_new0 (PendingClip);
+ GST_DEBUG_OBJECT (self, "Adding pending %p for %s, currently: %i",
+ pclip, asset_id, g_list_length (pendings));
+
+ pclip->id = g_strdup (id);
+ pclip->track_types = track_types;
+ pclip->duration = duration;
+ pclip->inpoint = inpoint;
+ pclip->start = start;
+ pclip->layer = gst_object_ref (entry->layer);
+
+ pclip->properties = properties ? gst_structure_copy (properties) : NULL;
+ pclip->children_properties =
+ children_properties ? gst_structure_copy (children_properties) : NULL;
+ pclip->metadatas = g_strdup (metadatas);
+
+ /* Add the new pending object to the hashtable */
+ g_hash_table_insert (priv->assetid_pendingclips, real_id,
+ g_list_append (pendings, pclip));
+ g_hash_table_insert (priv->clipid_pendings, g_strdup (id), pclip);
+
+ priv->current_clip = NULL;
+ priv->current_pending_clip = pclip;
+
+ return;
+ }
+
+ nclip = _add_object_to_layer (priv, id, entry->layer,
+ asset, start, inpoint, duration, track_types, metadatas, properties,
+ children_properties);
+
+ if (!nclip)
+ return;
+
+ priv->current_clip = nclip;
+}
+
+void
+ges_base_xml_formatter_set_timeline_properties (GESBaseXmlFormatter * self,
+ GESTimeline * timeline, const gchar * properties, const gchar * metadatas)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+ gboolean auto_transition = FALSE;
+
+ if (properties) {
+ GstStructure *props = gst_structure_from_string (properties, NULL);
+
+ if (props) {
+ if (gst_structure_get_boolean (props, "auto-transition",
+ &auto_transition))
+ gst_structure_remove_field (props, "auto-transition");
+
+ gst_structure_foreach (props,
+ (GstStructureForeachFunc) set_property_foreach, timeline);
+ gst_structure_free (props);
+ }
+ }
+
+ if (metadatas) {
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER (timeline),
+ metadatas);
+ };
+
+ priv->timeline_auto_transition = auto_transition;
+}
+
+void
+ges_base_xml_formatter_add_layer (GESBaseXmlFormatter * self,
+ GType extractable_type, guint priority, GstStructure * properties,
+ const gchar * metadatas, GError ** error)
+{
+ LayerEntry *entry;
+ GESAsset *asset;
+ GESLayer *layer;
+ gboolean auto_transition = FALSE;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ return;
+
+ if (extractable_type == G_TYPE_NONE)
+ layer = ges_layer_new ();
+ else {
+ asset = ges_asset_request (extractable_type, NULL, error);
+ if (asset == NULL) {
+ if (error && *error == NULL) {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Layer type %s could not be created'",
+ g_type_name (extractable_type));
+ return;
+ }
+ }
+ layer = GES_LAYER (ges_asset_extract (asset, error));
+ }
+
+ ges_layer_set_priority (layer, priority);
+ ges_timeline_add_layer (GES_FORMATTER (self)->timeline, layer);
+ if (properties) {
+ if (gst_structure_get_boolean (properties, "auto-transition",
+ &auto_transition))
+ gst_structure_remove_field (properties, "auto-transition");
+
+ gst_structure_foreach (properties,
+ (GstStructureForeachFunc) set_property_foreach, layer);
+ }
+
+ if (metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER (layer),
+ metadatas);
+
+ entry = g_slice_new0 (LayerEntry);
+ entry->layer = gst_object_ref (layer);
+ entry->auto_trans = auto_transition;
+
+ g_hash_table_insert (priv->layers, GINT_TO_POINTER (priority), entry);
+}
+
+void
+ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self,
+ GESTrackType track_type, GstCaps * caps, const gchar * id,
+ GstStructure * properties, const gchar * metadatas, GError ** error)
+{
+ GESTrack *track;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only) {
+ return;
+ }
+
+ track = ges_track_new (track_type, caps);
+ ges_timeline_add_track (GES_FORMATTER (self)->timeline, track);
+
+ if (properties) {
+ gchar *restriction;
+ GstCaps *restriction_caps;
+
+ gst_structure_get (properties, "restriction-caps", G_TYPE_STRING,
+ &restriction, NULL);
+ gst_structure_remove_fields (properties, "restriction-caps", "caps",
+ "message-forward", NULL);
+ if (g_strcmp0 (restriction, "NULL")) {
+ restriction_caps = gst_caps_from_string (restriction);
+ ges_track_set_restriction_caps (track, restriction_caps);
+ gst_caps_unref (restriction_caps);
+ }
+ gst_structure_foreach (properties,
+ (GstStructureForeachFunc) set_property_foreach, track);
+ g_free (restriction);
+ }
+
+ g_hash_table_insert (priv->tracks, g_strdup (id), gst_object_ref (track));
+ if (metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER (track),
+ metadatas);
+}
+
+void
+ges_base_xml_formatter_add_control_binding (GESBaseXmlFormatter * self,
+ const gchar * binding_type, const gchar * source_type,
+ const gchar * property_name, gint mode, const gchar * track_id,
+ GSList * timed_values)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+ GESTrackElement *element = NULL;
+
+ if (track_id[0] != '-' && priv->current_clip)
+ element = _get_element_by_track_id (priv, track_id, priv->current_clip);
+
+ else if (track_id[0] != '-' && priv->current_pending_clip) {
+ PendingBinding *pbinding;
+
+ pbinding = g_slice_new0 (PendingBinding);
+ pbinding->source = gst_interpolation_control_source_new ();
+ g_object_set (pbinding->source, "mode", mode, NULL);
+ gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
+ (pbinding->source), timed_values);
+ pbinding->propname = g_strdup (property_name);
+ pbinding->binding_type = g_strdup (binding_type);
+ pbinding->track_id = g_strdup (track_id);
+ priv->current_pending_clip->pending_bindings =
+ g_list_append (priv->current_pending_clip->pending_bindings, pbinding);
+ return;
+ }
+
+ else {
+ element = priv->current_track_element;
+ }
+
+ if (element == NULL) {
+ GST_WARNING ("No current track element to which we can append a binding");
+ return;
+ }
+
+ if (!g_strcmp0 (source_type, "interpolation")) {
+ GstControlSource *source;
+
+ source = gst_interpolation_control_source_new ();
+ ges_track_element_set_control_source (element, source,
+ property_name, binding_type);
+
+ g_object_set (source, "mode", mode, NULL);
+
+ gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source), timed_values);
+ } else
+ GST_WARNING ("This interpolation type is not supported\n");
+}
+
+void
+ges_base_xml_formatter_add_source (GESBaseXmlFormatter * self,
+ const gchar * track_id, GstStructure * children_properties)
+{
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+ GESTrackElement *element = NULL;
+
+ if (track_id[0] != '-' && priv->current_clip)
+ element = _get_element_by_track_id (priv, track_id, priv->current_clip);
+
+ else if (track_id[0] != '-' && priv->current_pending_clip) {
+ PendingChildProperties *pchildprops;
+
+ pchildprops = g_slice_new0 (PendingChildProperties);
+ pchildprops->track_id = g_strdup (track_id);
+ pchildprops->structure = children_properties ?
+ gst_structure_copy (children_properties) : NULL;
+ priv->current_pending_clip->children_props =
+ g_list_append (priv->current_pending_clip->children_props, pchildprops);
+ return;
+ } else {
+ element = priv->current_track_element;
+ }
+
+ if (element == NULL) {
+ GST_WARNING
+ ("No current track element to which we can append children properties");
+ return;
+ }
+
+ gst_structure_foreach (children_properties,
+ (GstStructureForeachFunc) _set_child_property, element);
+}
+
+void
+ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
+ GType track_element_type, const gchar * asset_id, const gchar * track_id,
+ const gchar * timeline_obj_id, GstStructure * children_properties,
+ GstStructure * properties, const gchar * metadatas, GError ** error)
+{
+ GESTrackElement *trackelement;
+
+ GError *err = NULL;
+ GESAsset *asset = NULL;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ return;
+
+ if (g_type_is_a (track_element_type, GES_TYPE_TRACK_ELEMENT) == FALSE) {
+ GST_DEBUG_OBJECT (self, "%s is not a TrackElement, can not create it",
+ g_type_name (track_element_type));
+ goto out;
+ }
+
+ if (g_type_is_a (track_element_type, GES_TYPE_BASE_EFFECT) == FALSE) {
+ GST_FIXME_OBJECT (self, "%s currently not supported",
+ g_type_name (track_element_type));
+ goto out;
+ }
+
+ asset = ges_asset_request (track_element_type, asset_id, &err);
+ if (asset == NULL) {
+ GST_DEBUG_OBJECT (self, "Can not create trackelement %s", asset_id);
+ GST_FIXME_OBJECT (self, "Check if missing plugins etc %s",
+ err ? err->message : "");
+
+ goto out;
+ }
+
+ trackelement = GES_TRACK_ELEMENT (ges_asset_extract (asset, NULL));
+ if (trackelement) {
+ GESClip *clip;
+ if (metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER
+ (trackelement), metadatas);
+
+ clip = g_hash_table_lookup (priv->containers, timeline_obj_id);
+ if (clip) {
+ _add_track_element (GES_FORMATTER (self), clip, trackelement, track_id,
+ children_properties, properties);
+ } else {
+ PendingEffects *peffect;
+ PendingClip *pend = g_hash_table_lookup (priv->clipid_pendings,
+ timeline_obj_id);
+ if (pend == NULL) {
+ GST_WARNING_OBJECT (self, "No Clip with id: %s can not "
+ "add TrackElement", timeline_obj_id);
+ goto out;
+ }
+
+ peffect = g_slice_new0 (PendingEffects);
+
+ peffect->trackelement = trackelement;
+ peffect->track_id = g_strdup (track_id);
+ peffect->properties = properties ? gst_structure_copy (properties) : NULL;
+ peffect->children_properties = children_properties ?
+ gst_structure_copy (children_properties) : NULL;
+
+ pend->effects = g_list_append (pend->effects, peffect);
+ }
+ priv->current_track_element = trackelement;
+ }
+
+ ges_project_add_asset (GES_FORMATTER (self)->project, asset);
+
+out:
+ if (asset)
+ gst_object_unref (asset);
+ if (err)
+ g_error_free (err);
+
+ return;
+}
+
+void
+ges_base_xml_formatter_add_encoding_profile (GESBaseXmlFormatter * self,
+ const gchar * type, const gchar * parent, const gchar * name,
+ const gchar * description, GstCaps * format, const gchar * preset,
+ GstStructure * preset_properties, const gchar * preset_name, guint id,
+ guint presence, GstCaps * restriction, guint pass,
+ gboolean variableframerate, GstStructure * properties, gboolean enabled,
+ GError ** error)
+{
+ const GList *tmp;
+ GstEncodingProfile *profile;
+ GstEncodingContainerProfile *parent_profile = NULL;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ goto done;
+
+ if (parent == NULL) {
+ profile =
+ _create_profile (self, type, parent, name, description, format, preset,
+ preset_properties, preset_name, id, presence, restriction, pass,
+ variableframerate, enabled);
+ ges_project_add_encoding_profile (GES_FORMATTER (self)->project, profile);
+ gst_object_unref (profile);
+
+ goto done;
+ }
+
+ for (tmp = ges_project_list_encoding_profiles (GES_FORMATTER (self)->project);
+ tmp; tmp = tmp->next) {
+ GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data);
+
+ if (g_strcmp0 (gst_encoding_profile_get_name (tmpprofile),
+ gst_encoding_profile_get_name (tmpprofile)) == 0) {
+
+ if (!GST_IS_ENCODING_CONTAINER_PROFILE (tmpprofile)) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Profile '%s' parent %s is not a container...'", name, parent);
+ goto done;
+ }
+
+ parent_profile = GST_ENCODING_CONTAINER_PROFILE (tmpprofile);
+ break;
+ }
+ }
+
+ if (parent_profile == NULL) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Profile '%s' parent %s does not exist'", name, parent);
+ goto done;
+ }
+
+ profile =
+ _create_profile (self, type, parent, name, description, format, preset,
+ preset_properties, preset_name, id, presence, restriction, pass,
+ variableframerate, enabled);
+
+ if (profile == NULL)
+ goto done;
+
+ gst_encoding_container_profile_add_profile (parent_profile, profile);
+
+done:
+ if (format)
+ gst_caps_unref (format);
+ if (restriction)
+ gst_caps_unref (restriction);
+}
+
+void
+ges_base_xml_formatter_add_group (GESBaseXmlFormatter * self,
+ const gchar * id, const gchar * properties, const gchar * metadatas)
+{
+ PendingGroup *pgroup;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ return;
+
+ pgroup = g_slice_new0 (PendingGroup);
+ pgroup->group = ges_group_new ();
+
+ if (metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER
+ (pgroup->group), metadatas);
+
+ g_hash_table_insert (priv->containers, g_strdup (id),
+ gst_object_ref (pgroup->group));
+ priv->groups = g_list_prepend (priv->groups, pgroup);
+
+ return;
+}
+
+void
+ges_base_xml_formatter_last_group_add_child (GESBaseXmlFormatter * self,
+ const gchar * child_id, const gchar * name)
+{
+ PendingGroup *pgroup;
+ GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (priv->check_only)
+ return;
+
+ g_return_if_fail (priv->groups);
+
+ pgroup = priv->groups->data;
+
+ pgroup->pending_children =
+ g_list_prepend (pgroup->pending_children, g_strdup (child_id));
+
+ GST_DEBUG_OBJECT (self, "Adding %s to %s", child_id,
+ GES_TIMELINE_ELEMENT_NAME (((PendingGroup *) priv->groups->data)->group));
+}
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+
+#include "ges-formatter.h"
+
+#ifndef GES_BASE_XML_FORMATTER_H
+#define GES_BASE_XML_FORMATTER_H
+
+G_BEGIN_DECLS
+#define GES_TYPE_BASE_XML_FORMATTER (ges_base_xml_formatter_get_type ())
+#define GES_BASE_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatter))
+#define GES_BASE_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatterClass))
+#define GES_IS_BASE_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_BASE_XML_FORMATTER))
+#define GES_IS_BASE_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_BASE_XML_FORMATTER))
+#define GES_BASE_XML_FORMATTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatterClass))
+
+typedef struct _GESBaseXmlFormatterPrivate GESBaseXmlFormatterPrivate;
+typedef struct _GESBaseXmlFormatter GESBaseXmlFormatter;
+typedef struct _GESBaseXmlFormatterClass GESBaseXmlFormatterClass;
+
+/**
+ * GESBaseXmlFormatter:
+ */
+struct _GESBaseXmlFormatter
+{
+ GESFormatter parent;
+
+ /*< public > */
+ /* <private> */
+ GESBaseXmlFormatterPrivate *priv;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESBaseXmlFormatterClass:
+ */
+struct _GESBaseXmlFormatterClass
+{
+ GESFormatterClass parent;
+
+ /* Should be overriden by subclasses */
+ GMarkupParser content_parser;
+
+ GString * (*save) (GESFormatter *formatter, GESTimeline *timeline, GError **error);
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_base_xml_formatter_get_type (void);
+
+G_END_DECLS
+#endif /* _GES_BASE_XML_FORMATTER_H */
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2011> 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.
+ */
+/**
+ * SECTION: gesclipasset
+ * @title: GESClipAsset
+ * @short_description: A GESAsset subclass specialized in GESClip extraction
+ *
+ * The #GESUriClipAsset is a special #GESAsset specilized in #GESClip.
+ * it is mostly used to get information about the #GESTrackType-s the objects extracted
+ * from it can potentialy create #GESTrackElement for.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-clip-asset.h"
+
+#define GES_CLIP_ASSET_GET_PRIVATE(o)\
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GES_TYPE_CLIP_ASSET, \
+ GESClipAssetPrivate))
+
+#define parent_class ges_clip_asset_parent_class
+
+struct _GESClipAssetPrivate
+{
+ GESTrackType supportedformats;
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_SUPPORTED_FORMATS,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESClipAsset, ges_clip_asset, GES_TYPE_ASSET);
+
+/***********************************************
+ * *
+ * GObject vmetods implemenation *
+ * *
+ ***********************************************/
+
+static void
+_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESClipAssetPrivate *priv = GES_CLIP_ASSET (object)->priv;
+ switch (property_id) {
+ case PROP_SUPPORTED_FORMATS:
+ g_value_set_flags (value, priv->supportedformats);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESClipAssetPrivate *priv = GES_CLIP_ASSET (object)->priv;
+
+ switch (property_id) {
+ case PROP_SUPPORTED_FORMATS:
+ priv->supportedformats = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_clip_asset_init (GESClipAsset * self)
+{
+ self->priv = ges_clip_asset_get_instance_private (self);
+}
+
+static void
+_constructed (GObject * object)
+{
+ GType extractable_type = ges_asset_get_extractable_type (GES_ASSET (object));
+ GObjectClass *class = g_type_class_ref (extractable_type);
+ GParamSpecFlags *pspec;
+
+ pspec = G_PARAM_SPEC_FLAGS (g_object_class_find_property (class,
+ "supported-formats"));
+
+ GES_CLIP_ASSET (object)->priv->supportedformats = pspec->default_value;
+ g_type_class_unref (class);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+}
+
+static void
+ges_clip_asset_class_init (GESClipAssetClass * self_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (self_class);
+
+ object_class->constructed = _constructed;
+ object_class->get_property = _get_property;
+ object_class->set_property = _set_property;
+
+ /**
+ * GESClipAsset:supported-formats:
+ *
+ * The formats supported by the asset.
+ */
+ properties[PROP_SUPPORTED_FORMATS] = g_param_spec_flags ("supported-formats",
+ "Supported formats", "Formats supported by the file",
+ GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_AUDIO | GES_TRACK_TYPE_VIDEO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
+ properties[PROP_SUPPORTED_FORMATS]);
+}
+
+/***********************************************
+ * *
+ * Public methods *
+ * *
+ ***********************************************/
+/**
+ * ges_clip_asset_set_supported_formats:
+ * @self: a #GESClipAsset
+ * @supportedformats: The track types supported by the GESClipAsset
+ *
+ * Sets track types for which objects extracted from @self can create #GESTrackElement
+ */
+void
+ges_clip_asset_set_supported_formats (GESClipAsset * self,
+ GESTrackType supportedformats)
+{
+ g_return_if_fail (GES_IS_CLIP_ASSET (self));
+
+ self->priv->supportedformats = supportedformats;
+}
+
+/**
+ * ges_clip_asset_get_supported_formats:
+ * @self: a #GESClipAsset
+ *
+ * Gets track types for which objects extracted from @self can create #GESTrackElement
+ *
+ * Returns: The track types on which @self will create TrackElement when added to
+ * a layer
+ */
+GESTrackType
+ges_clip_asset_get_supported_formats (GESClipAsset * self)
+{
+ g_return_val_if_fail (GES_IS_CLIP_ASSET (self), GES_TRACK_TYPE_UNKNOWN);
+
+ return self->priv->supportedformats;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef GES_CLIP_ASSET_H
+#define GES_CLIP_ASSET_H
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-asset.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_CLIP_ASSET (ges_clip_asset_get_type ())
+#define GES_CLIP_ASSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_CLIP_ASSET, GESClipAsset))
+#define GES_CLIP_ASSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_CLIP_ASSET, GESClipAssetClass))
+#define GES_IS_CLIP_ASSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_CLIP_ASSET))
+#define GES_IS_CLIP_ASSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_CLIP_ASSET))
+#define GES_CLIP_ASSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_CLIP_ASSET, GESClipAssetClass))
+
+typedef struct _GESClipAssetPrivate GESClipAssetPrivate;
+
+struct _GESClipAsset
+{
+ GESAsset parent;
+
+ /* <private> */
+ GESClipAssetPrivate *priv;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESClipAssetClass
+{
+ GESAssetClass parent;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_clip_asset_get_type (void);
+GES_API
+void ges_clip_asset_set_supported_formats (GESClipAsset *self,
+ GESTrackType supportedformats);
+GES_API
+GESTrackType ges_clip_asset_get_supported_formats (GESClipAsset *self);
+
+G_END_DECLS
+#endif /* _GES_CLIP_ASSET_H */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ * 2012 Collabora Ltd.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesclip
+ * @title: GESClip
+ * @short_description: Base Class for objects in a GESLayer
+ *
+ * A #GESClip is a 'natural' object which controls one or more
+ * #GESTrackElement(s) in one or more #GESTrack(s).
+ *
+ * Keeps a reference to the #GESTrackElement(s) it created and
+ * sets/updates their properties.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-clip.h"
+#include "ges.h"
+#include "ges-internal.h"
+
+#include <string.h>
+
+static GList *ges_clip_create_track_elements_func (GESClip * clip,
+ GESTrackType type);
+static gboolean _ripple (GESTimelineElement * element, GstClockTime start);
+static gboolean _ripple_end (GESTimelineElement * element, GstClockTime end);
+static gboolean _roll_start (GESTimelineElement * element, GstClockTime start);
+static gboolean _roll_end (GESTimelineElement * element, GstClockTime end);
+static gboolean _trim (GESTimelineElement * element, GstClockTime start);
+static void _compute_height (GESContainer * container);
+
+struct _GESClipPrivate
+{
+ /*< public > */
+ GESLayer *layer;
+
+ /*< private > */
+ guint nb_effects;
+
+ GList *copied_track_elements;
+ GESLayer *copied_layer;
+
+ /* The formats supported by this Clip */
+ GESTrackType supportedformats;
+};
+
+typedef struct _CheckTrack
+{
+ GESTrack *track;
+ GESTrackElement *source;
+} CheckTrack;
+
+enum
+{
+ PROP_0,
+ PROP_LAYER,
+ PROP_SUPPORTED_FORMATS,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESClip, ges_clip, GES_TYPE_CONTAINER);
+
+/****************************************************
+ * Listen to our children *
+ ****************************************************/
+
+/* @min_priority: The absolute minimum priority a child of @container should have
+ * @max_priority: The absolute maximum priority a child of @container should have
+ */
+static void
+_get_priority_range (GESContainer * container, guint32 * min_priority,
+ guint32 * max_priority)
+{
+ GESLayer *layer = GES_CLIP (container)->priv->layer;
+
+ if (layer) {
+ *min_priority = _PRIORITY (container) + layer->min_nle_priority;
+ *max_priority = layer->max_nle_priority;
+ } else {
+ *min_priority = _PRIORITY (container) + MIN_NLE_PRIO;
+ *max_priority = G_MAXUINT32;
+ }
+}
+
+static void
+_child_priority_changed_cb (GESTimelineElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
+{
+ guint32 min_prio, max_prio;
+
+ GST_DEBUG_OBJECT (container, "TimelineElement %p priority changed to %i",
+ child, _PRIORITY (child));
+
+ if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
+ return;
+
+ /* Update mapping */
+ _get_priority_range (container, &min_prio, &max_prio);
+
+ _ges_container_set_priority_offset (container, child,
+ min_prio - _PRIORITY (child));
+}
+
+/*****************************************************
+ * *
+ * GESTimelineElement virtual methods implementation *
+ * *
+ *****************************************************/
+
+static gboolean
+_set_start (GESTimelineElement * element, GstClockTime start)
+{
+ GList *tmp;
+ GESContainer *container = GES_CONTAINER (element);
+
+ GST_DEBUG_OBJECT (element, "Setting children start, (initiated_move: %"
+ GST_PTR_FORMAT ")", container->initiated_move);
+
+ element->start = start;
+ g_object_notify (G_OBJECT (element), "start");
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+
+ _set_start0 (GES_TIMELINE_ELEMENT (child), start);
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return FALSE;
+}
+
+static gboolean
+_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
+{
+ GList *tmp;
+ GESContainer *container = GES_CONTAINER (element);
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+
+ if (child != container->initiated_move) {
+ _set_inpoint0 (child, inpoint);
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_duration (GESTimelineElement * element, GstClockTime duration)
+{
+ GList *tmp;
+
+ GESContainer *container = GES_CONTAINER (element);
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+
+ if (child != container->initiated_move)
+ _set_duration0 (GES_TIMELINE_ELEMENT (child), duration);
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
+{
+ GList *tmp;
+
+ for (tmp = GES_CONTAINER (element)->children; tmp; tmp = g_list_next (tmp))
+ ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (tmp->data),
+ maxduration);
+
+ return TRUE;
+}
+
+static gboolean
+_set_priority (GESTimelineElement * element, guint32 priority)
+{
+ GList *tmp;
+ guint32 min_prio, max_prio;
+
+ GESContainer *container = GES_CONTAINER (element);
+
+ _get_priority_range (container, &min_prio, &max_prio);
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ guint32 track_element_prio;
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+ gint off = _ges_container_get_priority_offset (container, child);
+
+
+ if (off >= LAYER_HEIGHT) {
+ GST_ERROR ("%s child %s as a priority offset %d >= LAYER_HEIGHT %d"
+ " ==> clamping it to 0", GES_TIMELINE_ELEMENT_NAME (element),
+ GES_TIMELINE_ELEMENT_NAME (child), off, LAYER_HEIGHT);
+ off = 0;
+ }
+
+ /* We need to remove our current priority from @min_prio
+ * as it is the absolute minimum priority @child could have
+ * before we set @container to @priority.
+ */
+ track_element_prio = min_prio - _PRIORITY (container) + priority - off;
+
+ if (track_element_prio > max_prio) {
+ GST_WARNING ("%p priority of %i, is outside of the its containing "
+ "layer space. (%d/%d) setting it to the maximum it can be",
+ container, priority, min_prio - _PRIORITY (container) + priority,
+ max_prio);
+
+ track_element_prio = max_prio;
+ }
+ _set_priority0 (child, track_element_prio);
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+ _compute_height (container);
+
+ return TRUE;
+}
+
+static guint32
+_get_layer_priority (GESTimelineElement * element)
+{
+ GESClip *clip = GES_CLIP (element);
+
+ if (clip->priv->layer == NULL)
+ return GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY;
+
+ return ges_layer_get_priority (clip->priv->layer);
+}
+
+/****************************************************
+ * *
+ * GESContainer virtual methods implementation *
+ * *
+ ****************************************************/
+
+static void
+_compute_height (GESContainer * container)
+{
+ GList *tmp;
+ guint32 min_prio = G_MAXUINT32, max_prio = 0;
+
+ if (container->children == NULL) {
+ /* FIXME Why not 0! */
+ _ges_container_set_height (container, 1);
+ return;
+ }
+
+ /* Go over all childs and check if height has changed */
+ for (tmp = container->children; tmp; tmp = tmp->next) {
+ guint tck_priority = _PRIORITY (tmp->data);
+
+ if (tck_priority < min_prio)
+ min_prio = tck_priority;
+ if (tck_priority > max_prio)
+ max_prio = tck_priority;
+ }
+
+ _ges_container_set_height (container, max_prio - min_prio + 1);
+}
+
+static gboolean
+_add_child (GESContainer * container, GESTimelineElement * element)
+{
+ GList *tmp;
+ guint max_prio, min_prio;
+ GESClipPrivate *priv = GES_CLIP (container)->priv;
+
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (element), FALSE);
+
+ /* First make sure we work with a sorted list of GESTimelineElement-s */
+ _ges_container_sort_children (container);
+
+ /* If the TrackElement is an effect:
+ * - We add it on top of the list of TrackEffect
+ * - We put all TrackElements present in the Clip
+ * which are not BaseEffect on top of them
+ * FIXME: Let the full control over priorities to the user
+ */
+ _get_priority_range (container, &min_prio, &max_prio);
+ if (GES_IS_BASE_EFFECT (element)) {
+ GESChildrenControlMode mode = container->children_control_mode;
+
+ GST_DEBUG_OBJECT (container, "Adding %ith effect: %" GST_PTR_FORMAT
+ " Priority %i", priv->nb_effects + 1, element,
+ min_prio + priv->nb_effects);
+
+ tmp = g_list_nth (GES_CONTAINER_CHILDREN (container), priv->nb_effects);
+ container->children_control_mode = GES_CHILDREN_UPDATE_OFFSETS;
+ for (; tmp; tmp = tmp->next) {
+ ges_timeline_element_set_priority (GES_TIMELINE_ELEMENT (tmp->data),
+ GES_TIMELINE_ELEMENT_PRIORITY (tmp->data) + 1);
+ }
+
+ _set_priority0 (element, min_prio + priv->nb_effects);
+ container->children_control_mode = mode;
+ priv->nb_effects++;
+ } else {
+ /* We add the track element on top of the effect list */
+ _set_priority0 (element, min_prio + priv->nb_effects);
+ }
+
+ /* We set the timing value of the child to ours, we avoid infinite loop
+ * making sure the container ignore notifies from the child */
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ _set_start0 (element, GES_TIMELINE_ELEMENT_START (container));
+ _set_inpoint0 (element, GES_TIMELINE_ELEMENT_INPOINT (container));
+ _set_duration0 (element, GES_TIMELINE_ELEMENT_DURATION (container));
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_remove_child (GESContainer * container, GESTimelineElement * element)
+{
+ if (GES_IS_BASE_EFFECT (element)) {
+ GES_CLIP (container)->priv->nb_effects--;
+ }
+
+ GST_FIXME_OBJECT (container, "We should set other children prios");
+
+ return TRUE;
+}
+
+static void
+_child_added (GESContainer * container, GESTimelineElement * element)
+{
+ g_signal_connect (G_OBJECT (element), "notify::priority",
+ G_CALLBACK (_child_priority_changed_cb), container);
+
+ _child_priority_changed_cb (element, NULL, container);
+ _compute_height (container);
+}
+
+static void
+_child_removed (GESContainer * container, GESTimelineElement * element)
+{
+ g_signal_handlers_disconnect_by_func (element, _child_priority_changed_cb,
+ container);
+
+ if (GES_IS_BASE_EFFECT (element)) {
+ GList *tmp;
+ guint32 priority;
+ GESChildrenControlMode mode = container->children_control_mode;
+
+ GST_DEBUG_OBJECT (container, "Resyncing effects priority.");
+
+ container->children_control_mode = GES_CHILDREN_UPDATE_OFFSETS;
+ tmp = GES_CONTAINER_CHILDREN (container);
+ priority =
+ ges_timeline_element_get_priority (GES_TIMELINE_ELEMENT (element));
+ for (; tmp; tmp = tmp->next) {
+ if (ges_timeline_element_get_priority (GES_TIMELINE_ELEMENT (tmp->data)) >
+ priority) {
+ ges_timeline_element_set_priority (GES_TIMELINE_ELEMENT (tmp->data),
+ GES_TIMELINE_ELEMENT_PRIORITY (tmp->data) - 1);
+ }
+ }
+
+ container->children_control_mode = mode;
+ }
+
+ _compute_height (container);
+}
+
+static void
+add_clip_to_list (gpointer key, gpointer clip, GList ** list)
+{
+ *list = g_list_prepend (*list, gst_object_ref (clip));
+}
+
+static GList *
+_ungroup (GESContainer * container, gboolean recursive)
+{
+ GESClip *tmpclip;
+ GESTrackType track_type;
+ GESTrackElement *track_element;
+
+ gboolean first_obj = TRUE;
+ GList *tmp, *children, *ret = NULL;
+ GESClip *clip = GES_CLIP (container);
+ GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
+ GESLayer *layer = clip->priv->layer;
+ GHashTable *_tracktype_clip = g_hash_table_new (g_int_hash, g_int_equal);
+
+ /* If there is no TrackElement, just return @container in a list */
+ if (GES_CONTAINER_CHILDREN (container) == NULL) {
+ GST_DEBUG ("No TrackElement, simply returning");
+ return g_list_prepend (ret, container);
+ }
+
+ /* We need a copy of the current list of tracks */
+ children = ges_container_get_children (container, FALSE);
+ for (tmp = children; tmp; tmp = tmp->next) {
+ track_element = GES_TRACK_ELEMENT (tmp->data);
+ track_type = ges_track_element_get_track_type (track_element);
+
+ tmpclip = g_hash_table_lookup (_tracktype_clip, &track_type);
+ if (tmpclip == NULL) {
+ if (G_UNLIKELY (first_obj == TRUE)) {
+ tmpclip = clip;
+ first_obj = FALSE;
+ } else {
+ tmpclip = GES_CLIP (ges_timeline_element_copy (element, FALSE));
+ if (layer) {
+ /* Add new container to the same layer as @container */
+ ges_clip_set_moving_from_layer (tmpclip, TRUE);
+ ges_layer_add_clip (layer, tmpclip);
+ ges_clip_set_moving_from_layer (tmpclip, FALSE);
+ }
+ }
+
+ g_hash_table_insert (_tracktype_clip, &track_type, tmpclip);
+ ges_clip_set_supported_formats (tmpclip, track_type);
+ }
+
+ /* Move trackelement to the container it is supposed to land into */
+ if (tmpclip != clip) {
+ /* We need to bump the refcount to avoid the object to be destroyed */
+ gst_object_ref (track_element);
+ ges_container_remove (container, GES_TIMELINE_ELEMENT (track_element));
+ ges_container_add (GES_CONTAINER (tmpclip),
+ GES_TIMELINE_ELEMENT (track_element));
+ gst_object_unref (track_element);
+ }
+ }
+ g_list_free_full (children, gst_object_unref);
+ g_hash_table_foreach (_tracktype_clip, (GHFunc) add_clip_to_list, &ret);
+ g_hash_table_unref (_tracktype_clip);
+
+ return ret;
+}
+
+static GESContainer *
+_group (GList * containers)
+{
+ CheckTrack *tracks = NULL;
+ GESTimeline *timeline = NULL;
+ GESTrackType supported_formats;
+ GESLayer *layer = NULL;
+ GList *tmp, *tmpclip, *tmpelement;
+ GstClockTime start, inpoint, duration;
+
+ GESAsset *asset = NULL;
+ GESContainer *ret = NULL;
+ guint nb_tracks = 0, i = 0;
+
+ start = inpoint = duration = GST_CLOCK_TIME_NONE;
+
+ if (!containers)
+ return NULL;
+
+ /* First check if all the containers are clips, if they
+ * all have the same start/inpoint/duration and are in the same
+ * layer.
+ *
+ * We also need to make sure that all source have been created by the
+ * same asset, keep the information */
+ for (tmp = containers; tmp; tmp = tmp->next) {
+ GESClip *clip;
+ GESTimeline *tmptimeline;
+ GESContainer *tmpcontainer;
+ GESTimelineElement *element;
+
+ tmpcontainer = GES_CONTAINER (tmp->data);
+ element = GES_TIMELINE_ELEMENT (tmp->data);
+ if (GES_IS_CLIP (element) == FALSE) {
+ GST_DEBUG ("Can only work with clips");
+ goto done;
+ }
+ clip = GES_CLIP (tmp->data);
+ tmptimeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
+ if (!timeline) {
+ GList *tmptrack;
+
+ start = _START (tmpcontainer);
+ inpoint = _INPOINT (tmpcontainer);
+ duration = _DURATION (tmpcontainer);
+ timeline = tmptimeline;
+ layer = clip->priv->layer;
+ nb_tracks = g_list_length (GES_TIMELINE_GET_TRACKS (timeline));
+ tracks = g_new0 (CheckTrack, nb_tracks);
+
+ for (tmptrack = GES_TIMELINE_GET_TRACKS (timeline); tmptrack;
+ tmptrack = tmptrack->next) {
+ tracks[i].track = tmptrack->data;
+ i++;
+ }
+ } else {
+ if (start != _START (tmpcontainer) ||
+ inpoint != _INPOINT (tmpcontainer) ||
+ duration != _DURATION (tmpcontainer) || clip->priv->layer != layer) {
+ GST_INFO ("All children must have the same start, inpoint, duration "
+ " and be in the same layer");
+
+ goto done;
+ } else {
+ GList *tmp2;
+
+ for (tmp2 = GES_CONTAINER_CHILDREN (tmp->data); tmp2; tmp2 = tmp2->next) {
+ GESTrackElement *track_element = GES_TRACK_ELEMENT (tmp2->data);
+
+ if (GES_IS_SOURCE (track_element)) {
+ guint i;
+
+ for (i = 0; i < nb_tracks; i++) {
+ if (tracks[i].track ==
+ ges_track_element_get_track (track_element)) {
+ if (tracks[i].source) {
+ GST_INFO ("Can not link clips with various source for a "
+ "same track");
+
+ goto done;
+ }
+ tracks[i].source = track_element;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /* Then check that all sources have been created by the same asset,
+ * otherwise we can not group */
+ for (i = 0; i < nb_tracks; i++) {
+ if (tracks[i].source == NULL) {
+ GST_FIXME ("Check what to do here as we might end up having a mess");
+
+ continue;
+ }
+
+ /* FIXME Check what to do if we have source that have no assets */
+ if (!asset) {
+ asset =
+ ges_extractable_get_asset (GES_EXTRACTABLE
+ (ges_timeline_element_get_parent (GES_TIMELINE_ELEMENT (tracks
+ [i].source))));
+ continue;
+ }
+ if (asset !=
+ ges_extractable_get_asset (GES_EXTRACTABLE
+ (ges_timeline_element_get_parent (GES_TIMELINE_ELEMENT (tracks
+ [i].source))))) {
+ GST_INFO ("Can not link clips with source coming from different assets");
+
+ goto done;
+ }
+ }
+
+ /* And now pass all TrackElements to the first clip,
+ * and remove others from the layer (updating the supported formats) */
+ ret = containers->data;
+ supported_formats = GES_CLIP (ret)->priv->supportedformats;
+ for (tmpclip = containers->next; tmpclip; tmpclip = tmpclip->next) {
+ GESClip *cclip = tmpclip->data;
+ GList *children = ges_container_get_children (GES_CONTAINER (cclip), FALSE);
+
+ for (tmpelement = children; tmpelement; tmpelement = tmpelement->next) {
+ GESTimelineElement *celement = GES_TIMELINE_ELEMENT (tmpelement->data);
+
+ ges_container_remove (GES_CONTAINER (cclip), celement);
+ ges_container_add (ret, celement);
+
+ supported_formats = supported_formats |
+ ges_track_element_get_track_type (GES_TRACK_ELEMENT (celement));
+ }
+ g_list_free_full (children, gst_object_unref);
+
+ ges_layer_remove_clip (layer, tmpclip->data);
+ }
+
+ ges_clip_set_supported_formats (GES_CLIP (ret), supported_formats);
+
+done:
+ if (tracks)
+ g_free (tracks);
+
+
+ return ret;
+
+}
+
+static gboolean
+_edit (GESContainer * container, GList * layers,
+ gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
+{
+ GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (container);
+ GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
+
+ if (!G_UNLIKELY (GES_CONTAINER_CHILDREN (container))) {
+ GST_WARNING_OBJECT (container, "Trying to edit, but not containing"
+ "any TrackElement yet.");
+ return FALSE;
+ }
+
+ if (!timeline) {
+ GST_WARNING_OBJECT (container, "Trying to edit, but not in any"
+ "timeline.");
+ return FALSE;
+ }
+
+ switch (mode) {
+ case GES_EDIT_MODE_RIPPLE:
+ return timeline_ripple_object (timeline, element,
+ new_layer_priority <
+ 0 ? GES_TIMELINE_ELEMENT_LAYER_PRIORITY (container) :
+ new_layer_priority, layers, edge, position);
+ case GES_EDIT_MODE_TRIM:
+ return timeline_trim_object (timeline, element,
+ new_layer_priority <
+ 0 ? GES_TIMELINE_ELEMENT_LAYER_PRIORITY (container) :
+ new_layer_priority, layers, edge, position);
+ case GES_EDIT_MODE_NORMAL:
+ return timeline_move_object (timeline, element,
+ new_layer_priority <
+ 0 ? GES_TIMELINE_ELEMENT_LAYER_PRIORITY (container) :
+ new_layer_priority, layers, edge, position);
+ case GES_EDIT_MODE_ROLL:
+ return timeline_roll_object (timeline, element, layers, edge, position);
+ case GES_EDIT_MODE_SLIDE:
+ GST_ERROR ("Sliding not implemented.");
+ return FALSE;
+ }
+ return FALSE;
+}
+
+static void
+_deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
+{
+ GList *tmp;
+ GESClip *self = GES_CLIP (element), *ccopy = GES_CLIP (copy);
+
+ if (!self->priv->layer)
+ return;
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ ccopy->priv->copied_track_elements =
+ g_list_append (ccopy->priv->copied_track_elements,
+ ges_timeline_element_copy (tmp->data, TRUE));
+ }
+
+ ccopy->priv->copied_layer = g_object_ref (self->priv->layer);
+}
+
+static GESTimelineElement *
+_paste (GESTimelineElement * element, GESTimelineElement * ref,
+ GstClockTime paste_position)
+{
+ GList *tmp;
+ GESClip *self = GES_CLIP (element);
+ GESClip *nclip = GES_CLIP (ges_timeline_element_copy (element, FALSE));
+
+ if (self->priv->copied_layer)
+ nclip->priv->copied_layer = g_object_ref (self->priv->copied_layer);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (nclip), paste_position);
+ if (self->priv->copied_layer) {
+ if (!ges_layer_add_clip (self->priv->copied_layer, nclip)) {
+ GST_INFO ("%" GES_FORMAT " could not be pasted to %" GST_TIME_FORMAT,
+ GES_ARGS (element), GST_TIME_ARGS (paste_position));
+
+ return NULL;
+ }
+
+ }
+
+ for (tmp = self->priv->copied_track_elements; tmp; tmp = tmp->next) {
+ GESTrackElement *new_trackelement, *trackelement =
+ GES_TRACK_ELEMENT (tmp->data);
+
+ new_trackelement =
+ GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
+ (trackelement), FALSE));
+ if (new_trackelement == NULL) {
+ GST_WARNING_OBJECT (trackelement, "Could not create a copy");
+ continue;
+ }
+
+ ges_container_add (GES_CONTAINER (nclip),
+ GES_TIMELINE_ELEMENT (new_trackelement));
+
+ ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
+ GES_TIMELINE_ELEMENT (new_trackelement));
+
+ ges_track_element_copy_bindings (trackelement, new_trackelement,
+ GST_CLOCK_TIME_NONE);
+ }
+
+ return GES_TIMELINE_ELEMENT (nclip);
+}
+
+static gboolean
+_lookup_child (GESTimelineElement * self, const gchar * prop_name,
+ GObject ** child, GParamSpec ** pspec)
+{
+ GList *tmp;
+
+ if (GES_TIMELINE_ELEMENT_CLASS (ges_clip_parent_class)->lookup_child (self,
+ prop_name, child, pspec))
+ return TRUE;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ if (ges_timeline_element_lookup_child (tmp->data, prop_name, child, pspec))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/****************************************************
+ * *
+ * GObject virtual methods implementation *
+ * *
+ ****************************************************/
+static void
+ges_clip_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESClip *clip = GES_CLIP (object);
+
+ switch (property_id) {
+ case PROP_LAYER:
+ g_value_set_object (value, clip->priv->layer);
+ break;
+ case PROP_SUPPORTED_FORMATS:
+ g_value_set_flags (value, clip->priv->supportedformats);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_clip_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESClip *clip = GES_CLIP (object);
+
+ switch (property_id) {
+ case PROP_SUPPORTED_FORMATS:
+ ges_clip_set_supported_formats (clip, g_value_get_flags (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_clip_dispose (GObject * object)
+{
+ GESClip *self = GES_CLIP (object);
+
+ g_list_free_full (self->priv->copied_track_elements, g_object_unref);
+ self->priv->copied_track_elements = NULL;
+ g_clear_object (&self->priv->copied_layer);
+
+ G_OBJECT_CLASS (ges_clip_parent_class)->dispose (object);
+}
+
+
+static void
+ges_clip_class_init (GESClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESContainerClass *container_class = GES_CONTAINER_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_clip_get_property;
+ object_class->set_property = ges_clip_set_property;
+ object_class->dispose = ges_clip_dispose;
+ klass->create_track_elements = ges_clip_create_track_elements_func;
+ klass->create_track_element = NULL;
+
+ /**
+ * GESClip:supported-formats:
+ *
+ * The formats supported by the clip.
+ */
+ properties[PROP_SUPPORTED_FORMATS] = g_param_spec_flags ("supported-formats",
+ "Supported formats", "Formats supported by the file",
+ GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_AUDIO | GES_TRACK_TYPE_VIDEO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
+ properties[PROP_SUPPORTED_FORMATS]);
+
+ /**
+ * GESClip:layer:
+ *
+ * The GESLayer where this clip is being used. If you want to connect to its
+ * notify signal you should connect to it with g_signal_connect_after as the
+ * signal emission can be stop in the first fase.
+ */
+ properties[PROP_LAYER] = g_param_spec_object ("layer", "Layer",
+ "The GESLayer where this clip is being used.",
+ GES_TYPE_LAYER, G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_LAYER,
+ properties[PROP_LAYER]);
+
+ element_class->ripple = _ripple;
+ element_class->ripple_end = _ripple_end;
+ element_class->roll_start = _roll_start;
+ element_class->roll_end = _roll_end;
+ element_class->trim = _trim;
+ element_class->set_start = _set_start;
+ element_class->set_duration = _set_duration;
+ element_class->set_inpoint = _set_inpoint;
+ element_class->set_priority = _set_priority;
+ element_class->set_max_duration = _set_max_duration;
+ element_class->paste = _paste;
+ element_class->deep_copy = _deep_copy;
+ element_class->lookup_child = _lookup_child;
+ element_class->get_layer_priority = _get_layer_priority;
+
+ container_class->add_child = _add_child;
+ container_class->remove_child = _remove_child;
+ container_class->child_removed = _child_removed;
+ container_class->child_added = _child_added;
+ container_class->ungroup = _ungroup;
+ container_class->group = _group;
+ container_class->grouping_priority = G_MAXUINT;
+ container_class->edit = _edit;
+}
+
+static void
+ges_clip_init (GESClip * self)
+{
+ self->priv = ges_clip_get_instance_private (self);
+ self->priv->layer = NULL;
+ self->priv->nb_effects = 0;
+}
+
+/**
+ * ges_clip_create_track_element:
+ * @clip: The origin #GESClip
+ * @type: The #GESTrackType to create a #GESTrackElement for.
+ *
+ * Creates a #GESTrackElement for the provided @type. The clip
+ * keep a reference to the newly created trackelement, you therefore need to
+ * call @ges_container_remove when you are done with it.
+ *
+ * Returns: (transfer none) (nullable): A #GESTrackElement. Returns NULL if
+ * the #GESTrackElement could not be created.
+ */
+GESTrackElement *
+ges_clip_create_track_element (GESClip * clip, GESTrackType type)
+{
+ GESClipClass *class;
+ GESTrackElement *res;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+
+ GST_DEBUG_OBJECT (clip, "Creating track element for %s",
+ ges_track_type_name (type));
+ if (!(type & clip->priv->supportedformats)) {
+ GST_DEBUG_OBJECT (clip, "We don't support this track type %i", type);
+ return NULL;
+ }
+
+ class = GES_CLIP_GET_CLASS (clip);
+
+ if (G_UNLIKELY (class->create_track_element == NULL)) {
+ GST_ERROR ("No 'create_track_element' implementation available fo type %s",
+ G_OBJECT_TYPE_NAME (clip));
+ return NULL;
+ }
+
+ res = class->create_track_element (clip, type);
+ return res;
+
+}
+
+/**
+ * ges_clip_create_track_elements:
+ * @clip: The origin #GESClip
+ * @type: The #GESTrackType to create each #GESTrackElement for.
+ *
+ * Creates all #GESTrackElements supported by this clip for the track type.
+ *
+ * Returns: (element-type GESTrackElement) (transfer full): A #GList of
+ * newly created #GESTrackElement-s
+ */
+
+GList *
+ges_clip_create_track_elements (GESClip * clip, GESTrackType type)
+{
+ GList *result = NULL, *tmp, *children;
+ GESClipClass *klass;
+ guint max_prio, min_prio;
+ gboolean readding_effects_only = TRUE;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+
+ klass = GES_CLIP_GET_CLASS (clip);
+
+ if (!(klass->create_track_elements)) {
+ GST_WARNING ("no GESClip::create_track_elements implentation");
+ return NULL;
+ }
+
+ GST_DEBUG_OBJECT (clip, "Creating TrackElements for type: %s",
+ ges_track_type_name (type));
+ children = ges_container_get_children (GES_CONTAINER (clip), TRUE);
+ for (tmp = children; tmp; tmp = tmp->next) {
+ GESTrackElement *child = GES_TRACK_ELEMENT (tmp->data);
+
+ if (!ges_track_element_get_track (child)
+ && ges_track_element_get_track_type (child) & type) {
+
+ GST_DEBUG_OBJECT (clip, "Removing for reusage: %" GST_PTR_FORMAT, child);
+ result = g_list_append (result, g_object_ref (child));
+ ges_container_remove (GES_CONTAINER (clip), tmp->data);
+ if (!GES_IS_BASE_EFFECT (child))
+ readding_effects_only = FALSE;
+ }
+ }
+ g_list_free_full (children, gst_object_unref);
+
+ if (readding_effects_only) {
+ result = g_list_concat (result, klass->create_track_elements (clip, type));
+ }
+
+ _get_priority_range (GES_CONTAINER (clip), &min_prio, &max_prio);
+ for (tmp = result; tmp; tmp = tmp->next) {
+ GESTimelineElement *elem = tmp->data;
+
+ _set_start0 (elem, GES_TIMELINE_ELEMENT_START (clip));
+ _set_inpoint0 (elem, GES_TIMELINE_ELEMENT_INPOINT (clip));
+ _set_duration0 (elem, GES_TIMELINE_ELEMENT_DURATION (clip));
+
+ if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_MAX_DURATION (clip)))
+ ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (elem),
+ GES_TIMELINE_ELEMENT_MAX_DURATION (clip));
+
+ _set_priority0 (elem, min_prio + clip->priv->nb_effects);
+
+ ges_container_add (GES_CONTAINER (clip), elem);
+ }
+
+ return result;
+}
+
+/*
+ * default implementation of GESClipClass::create_track_elements
+ */
+GList *
+ges_clip_create_track_elements_func (GESClip * clip, GESTrackType type)
+{
+ GESTrackElement *result;
+
+ GST_DEBUG_OBJECT (clip, "Creating trackelement for track: %s",
+ ges_track_type_name (type));
+ result = ges_clip_create_track_element (clip, type);
+ if (!result) {
+ GST_DEBUG ("Did not create track element");
+ return NULL;
+ }
+
+ return g_list_append (NULL, result);
+}
+
+void
+ges_clip_set_layer (GESClip * clip, GESLayer * layer)
+{
+ if (layer == clip->priv->layer)
+ return;
+
+ clip->priv->layer = layer;
+
+ GST_DEBUG ("clip:%p, layer:%p", clip, layer);
+
+ /* We do not want to notify the setting of layer = NULL when
+ * it is actually the result of a move between layer (as we know
+ * that it will be added to another layer right after, and this
+ * is what imports here.) */
+ if (!ELEMENT_FLAG_IS_SET (clip, GES_CLIP_IS_MOVING))
+ g_object_notify_by_pspec (G_OBJECT (clip), properties[PROP_LAYER]);
+}
+
+/**
+ * ges_clip_set_moving_from_layer:
+ * @clip: a #GESClip
+ * @is_moving: %TRUE if you want to start moving @clip to another layer
+ * %FALSE when you finished moving it.
+ *
+ * Sets the clip in a moving to layer state. You might rather use the
+ * ges_clip_move_to_layer function to move #GESClip-s
+ * from a layer to another.
+ **/
+void
+ges_clip_set_moving_from_layer (GESClip * clip, gboolean is_moving)
+{
+ g_return_if_fail (GES_IS_CLIP (clip));
+
+ if (is_moving)
+ ELEMENT_SET_FLAG (clip, GES_CLIP_IS_MOVING);
+ else
+ ELEMENT_UNSET_FLAG (clip, GES_CLIP_IS_MOVING);
+}
+
+/**
+ * ges_clip_is_moving_from_layer:
+ * @clip: a #GESClip
+ *
+ * Tells you if the clip is currently moving from a layer to another.
+ * You might rather use the ges_clip_move_to_layer function to
+ * move #GESClip-s from a layer to another.
+ *
+ * Returns: %TRUE if @clip is currently moving from its current layer
+ * %FALSE otherwize
+ **/
+gboolean
+ges_clip_is_moving_from_layer (GESClip * clip)
+{
+ g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+
+ return ELEMENT_FLAG_IS_SET (clip, GES_CLIP_IS_MOVING);
+}
+
+/**
+ * ges_clip_move_to_layer:
+ * @clip: a #GESClip
+ * @layer: the new #GESLayer
+ *
+ * Moves @clip to @layer. If @clip is not in any layer, it adds it to
+ * @layer, else, it removes it from its current layer, and adds it to @layer.
+ *
+ * Returns: %TRUE if @clip could be moved %FALSE otherwize
+ */
+gboolean
+ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
+{
+ gboolean ret;
+ GESLayer *current_layer;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+
+ ELEMENT_SET_FLAG (clip, GES_CLIP_IS_MOVING);
+ if (layer->timeline
+ && !timeline_tree_can_move_element (timeline_get_tree (layer->timeline),
+ GES_TIMELINE_ELEMENT (clip),
+ ges_layer_get_priority (layer),
+ GES_TIMELINE_ELEMENT_START (clip),
+ GES_TIMELINE_ELEMENT_DURATION (clip), NULL)) {
+ GST_INFO_OBJECT (layer, "Clip %" GES_FORMAT " can't move to layer %d",
+ GES_ARGS (clip), ges_layer_get_priority (layer));
+ ELEMENT_UNSET_FLAG (clip, GES_CLIP_IS_MOVING);
+ return FALSE;
+ }
+
+ current_layer = clip->priv->layer;
+
+ if (current_layer == NULL) {
+ GST_DEBUG ("Not moving %p, only adding it to %p", clip, layer);
+
+ return ges_layer_add_clip (layer, clip);
+ }
+
+ GST_DEBUG_OBJECT (clip, "moving to layer %p, priority: %d", layer,
+ ges_layer_get_priority (layer));
+
+ gst_object_ref (clip);
+ ret = ges_layer_remove_clip (current_layer, clip);
+
+ if (!ret) {
+ ELEMENT_UNSET_FLAG (clip, GES_CLIP_IS_MOVING);
+ gst_object_unref (clip);
+ return FALSE;
+ }
+
+ ret = ges_layer_add_clip (layer, clip);
+ ELEMENT_UNSET_FLAG (clip, GES_CLIP_IS_MOVING);
+
+ gst_object_unref (clip);
+ g_object_notify_by_pspec (G_OBJECT (clip), properties[PROP_LAYER]);
+
+
+ return ret && (clip->priv->layer == layer);
+}
+
+/**
+ * ges_clip_find_track_element:
+ * @clip: a #GESClip
+ * @track: (allow-none): a #GESTrack or NULL
+ * @type: a #GType indicating the type of track element you are looking
+ * for or %G_TYPE_NONE if you do not care about the track type.
+ *
+ * Finds the #GESTrackElement controlled by @clip that is used in @track. You
+ * may optionally specify a GType to further narrow search criteria.
+ *
+ * Note: If many objects match, then the one with the highest priority will be
+ * returned.
+ *
+ * Returns: (transfer full) (nullable): The #GESTrackElement used by @track,
+ * else %NULL. Unref after usage
+ */
+
+GESTrackElement *
+ges_clip_find_track_element (GESClip * clip, GESTrack * track, GType type)
+{
+ GList *tmp;
+ GESTrackElement *otmp;
+
+ GESTrackElement *ret = NULL;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+ g_return_val_if_fail (!(track == NULL && type == G_TYPE_NONE), NULL);
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = g_list_next (tmp)) {
+ otmp = (GESTrackElement *) tmp->data;
+
+ if ((type != G_TYPE_NONE) && !G_TYPE_CHECK_INSTANCE_TYPE (tmp->data, type))
+ continue;
+
+ if ((track == NULL) || (ges_track_element_get_track (otmp) == track)) {
+ ret = GES_TRACK_ELEMENT (tmp->data);
+ gst_object_ref (ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ges_clip_get_layer:
+ * @clip: a #GESClip
+ *
+ * Get the #GESLayer to which this clip belongs.
+ *
+ * Returns: (transfer full) (nullable): The #GESLayer where this @clip is being
+ * used, or %NULL if it is not used on any layer. The caller should unref it
+ * usage.
+ */
+GESLayer *
+ges_clip_get_layer (GESClip * clip)
+{
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+
+ if (clip->priv->layer != NULL)
+ gst_object_ref (G_OBJECT (clip->priv->layer));
+
+ return clip->priv->layer;
+}
+
+/**
+ * ges_clip_get_top_effects:
+ * @clip: The origin #GESClip
+ *
+ * Get effects applied on @clip
+ *
+ * Returns: (transfer full) (element-type GESTrackElement): a #GList of the
+ * #GESBaseEffect that are applied on @clip order by ascendant priorities.
+ * The refcount of the objects will be increased. The user will have to
+ * unref each #GESBaseEffect and free the #GList.
+ */
+GList *
+ges_clip_get_top_effects (GESClip * clip)
+{
+ GList *tmp, *ret;
+ guint i;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+
+ GST_DEBUG_OBJECT (clip, "Getting the %i top effects", clip->priv->nb_effects);
+ ret = NULL;
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip), i = 0;
+ i < clip->priv->nb_effects; tmp = tmp->next, i++) {
+ ret = g_list_append (ret, gst_object_ref (tmp->data));
+ }
+
+ return g_list_sort (ret, (GCompareFunc) element_start_compare);
+}
+
+/**
+ * ges_clip_get_top_effect_index:
+ * @clip: The origin #GESClip
+ * @effect: The #GESBaseEffect we want to get the top index from
+ *
+ * Gets the index position of an effect.
+ *
+ * Returns: The top index of the effect, -1 if something went wrong.
+ */
+gint
+ges_clip_get_top_effect_index (GESClip * clip, GESBaseEffect * effect)
+{
+ guint max_prio, min_prio;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), -1);
+ g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), -1);
+
+ _get_priority_range (GES_CONTAINER (clip), &min_prio, &max_prio);
+
+ return GES_TIMELINE_ELEMENT_PRIORITY (effect) - min_prio;
+}
+
+/* TODO 2.0 remove as it is Deprecated */
+gint
+ges_clip_get_top_effect_position (GESClip * clip, GESBaseEffect * effect)
+{
+ return ges_clip_get_top_effect_index (clip, effect);
+}
+
+/* TODO 2.0 remove as it is Deprecated */
+gboolean
+ges_clip_set_top_effect_priority (GESClip * clip,
+ GESBaseEffect * effect, guint newpriority)
+{
+ return ges_clip_set_top_effect_index (clip, effect, newpriority);
+}
+
+/**
+ * ges_clip_set_top_effect_index:
+ * @clip: The origin #GESClip
+ * @effect: The #GESBaseEffect to move
+ * @newindex: the new index at which to move the @effect inside this
+ * #GESClip
+ *
+ * This is a convenience method that lets you set the index of a top effect.
+ *
+ * Returns: %TRUE if @effect was successfuly moved, %FALSE otherwise.
+ */
+gboolean
+ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
+ guint newindex)
+{
+ gint inc;
+ GList *tmp;
+ guint current_prio, min_prio, max_prio;
+ GESTrackElement *track_element;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+
+ track_element = GES_TRACK_ELEMENT (effect);
+ if (G_UNLIKELY (GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (track_element)) !=
+ clip))
+ return FALSE;
+
+ current_prio = _PRIORITY (track_element);
+
+ _get_priority_range (GES_CONTAINER (clip), &min_prio, &max_prio);
+
+ newindex = newindex + min_prio;
+ /* We don't change the priority */
+ if (current_prio == newindex)
+ return TRUE;
+
+ if (newindex > (clip->priv->nb_effects - 1 + min_prio)) {
+ GST_DEBUG ("You are trying to make %p not a top effect", effect);
+ return FALSE;
+ }
+
+ if (current_prio > clip->priv->nb_effects + min_prio) {
+ GST_ERROR ("%p is not a top effect", effect);
+ return FALSE;
+ }
+
+ _ges_container_sort_children (GES_CONTAINER (clip));
+ if (_PRIORITY (track_element) < newindex)
+ inc = -1;
+ else
+ inc = +1;
+
+ GST_DEBUG_OBJECT (clip, "Setting top effect %" GST_PTR_FORMAT "priority: %i",
+ effect, newindex);
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
+ GESTrackElement *tmpo = GES_TRACK_ELEMENT (tmp->data);
+ guint tck_priority = _PRIORITY (tmpo);
+
+ if (tmpo == track_element)
+ continue;
+
+ if ((inc == +1 && tck_priority >= newindex) ||
+ (inc == -1 && tck_priority <= newindex)) {
+ _set_priority0 (GES_TIMELINE_ELEMENT (tmpo), tck_priority + inc);
+ }
+ }
+ _set_priority0 (GES_TIMELINE_ELEMENT (track_element), newindex);
+
+ return TRUE;
+}
+
+/**
+ * ges_clip_split:
+ * @clip: the #GESClip to split
+ * @position: a #GstClockTime representing the timeline position at which to split
+ *
+ * The function modifies @clip, and creates another #GESClip so we have two
+ * clips at the end, splitted at the time specified by @position, as a position
+ * in the timeline (not in the clip to be split). For example, if
+ * ges_clip_split is called on a 4-second clip playing from 0:01.00 until
+ * 0:05.00, with a split position of 0:02.00, this will result in one clip of 1
+ * second and one clip of 3 seconds, not in two clips of 2 seconds.
+ *
+ * The newly created clip will be added to the same layer as @clip is in. This
+ * implies that @clip must be in a #GESLayer for the operation to be possible.
+ *
+ * This method supports clips playing at a different tempo than one second per
+ * second. For example, splitting a clip with a #GESEffect 'pitch tempo=1.5'
+ * four seconds after it starts, will set the inpoint of the new clip to six
+ * seconds after that of the clip to split. For this, the rate-changing
+ * property must be registered using @ges_effect_class_register_rate_property;
+ * for the 'pitch' plugin, this is already done.
+ *
+ * Returns: (transfer none) (nullable): The newly created #GESClip resulting
+ * from the splitting or %NULL if the clip can't be split.
+ */
+GESClip *
+ges_clip_split (GESClip * clip, guint64 position)
+{
+ GList *tmp;
+ GESClip *new_object;
+ GstClockTime start, inpoint, duration, old_duration, new_duration;
+ gdouble media_duration_factor;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+ g_return_val_if_fail (clip->priv->layer, NULL);
+ g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), NULL);
+
+ duration = _DURATION (clip);
+ start = _START (clip);
+ inpoint = _INPOINT (clip);
+
+ if (position >= start + duration || position <= start) {
+ GST_WARNING_OBJECT (clip, "Can not split %" GST_TIME_FORMAT
+ " out of boundaries", GST_TIME_ARGS (position));
+ return NULL;
+ }
+
+ GST_DEBUG_OBJECT (clip, "Spliting at %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (position));
+
+ /* Create the new Clip */
+ new_object = GES_CLIP (ges_timeline_element_copy (GES_TIMELINE_ELEMENT (clip),
+ FALSE));
+
+ GST_DEBUG_OBJECT (new_object, "New 'splitted' clip");
+ /* Set new timing properties on the Clip */
+ media_duration_factor =
+ ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
+ (clip));
+ new_duration = duration + start - position;
+ old_duration = position - start;
+ _set_start0 (GES_TIMELINE_ELEMENT (new_object), position);
+ _set_inpoint0 (GES_TIMELINE_ELEMENT (new_object),
+ inpoint + old_duration * media_duration_factor);
+ _set_duration0 (GES_TIMELINE_ELEMENT (new_object), new_duration);
+
+ _DURATION (clip) = old_duration;
+ g_object_notify (G_OBJECT (clip), "duration");
+
+ /* We do not want the timeline to create again TrackElement-s */
+ ges_clip_set_moving_from_layer (new_object, TRUE);
+ ges_layer_add_clip (clip->priv->layer, new_object);
+ ges_clip_set_moving_from_layer (new_object, FALSE);
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
+ GESTrackElement *new_trackelement, *trackelement =
+ GES_TRACK_ELEMENT (tmp->data);
+
+ new_trackelement =
+ GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
+ (trackelement), FALSE));
+ if (new_trackelement == NULL) {
+ GST_WARNING_OBJECT (trackelement, "Could not create a copy");
+ continue;
+ }
+
+ /* Set 'new' track element timing propeties */
+ _set_start0 (GES_TIMELINE_ELEMENT (new_trackelement), position);
+ _set_inpoint0 (GES_TIMELINE_ELEMENT (new_trackelement),
+ inpoint + old_duration * media_duration_factor);
+ _set_duration0 (GES_TIMELINE_ELEMENT (new_trackelement), new_duration);
+
+ ges_container_add (GES_CONTAINER (new_object),
+ GES_TIMELINE_ELEMENT (new_trackelement));
+ ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
+ GES_TIMELINE_ELEMENT (new_trackelement));
+
+ ges_track_element_copy_bindings (trackelement, new_trackelement,
+ position - start + inpoint);
+ }
+
+ ELEMENT_SET_FLAG (clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ _DURATION (clip) = duration;
+ _set_duration0 (GES_TIMELINE_ELEMENT (clip), old_duration);
+ ELEMENT_UNSET_FLAG (clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+
+ return new_object;
+}
+
+/**
+ * ges_clip_set_supported_formats:
+ * @clip: the #GESClip to set supported formats on
+ * @supportedformats: the #GESTrackType defining formats supported by @clip
+ *
+ * Sets the formats supported by the file.
+ */
+void
+ges_clip_set_supported_formats (GESClip * clip, GESTrackType supportedformats)
+{
+ g_return_if_fail (GES_IS_CLIP (clip));
+
+ clip->priv->supportedformats = supportedformats;
+}
+
+/**
+ * ges_clip_get_supported_formats:
+ * @clip: the #GESClip
+ *
+ * Get the formats supported by @clip.
+ *
+ * Returns: The formats supported by @clip.
+ */
+GESTrackType
+ges_clip_get_supported_formats (GESClip * clip)
+{
+ g_return_val_if_fail (GES_IS_CLIP (clip), GES_TRACK_TYPE_UNKNOWN);
+
+ return clip->priv->supportedformats;
+}
+
+gboolean
+_ripple (GESTimelineElement * element, GstClockTime start)
+{
+ return ges_container_edit (GES_CONTAINER (element), NULL,
+ ges_timeline_element_get_layer_priority (element),
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, start);
+}
+
+static gboolean
+_ripple_end (GESTimelineElement * element, GstClockTime end)
+{
+ return ges_container_edit (GES_CONTAINER (element), NULL,
+ ges_timeline_element_get_layer_priority (element),
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_END, end);
+}
+
+gboolean
+_roll_start (GESTimelineElement * element, GstClockTime start)
+{
+ return ges_container_edit (GES_CONTAINER (element), NULL,
+ ges_timeline_element_get_layer_priority (element),
+ GES_EDIT_MODE_ROLL, GES_EDGE_START, start);
+}
+
+gboolean
+_roll_end (GESTimelineElement * element, GstClockTime end)
+{
+ return ges_container_edit (GES_CONTAINER (element), NULL,
+ ges_timeline_element_get_layer_priority (element),
+ GES_EDIT_MODE_ROLL, GES_EDGE_END, end);
+}
+
+gboolean
+_trim (GESTimelineElement * element, GstClockTime start)
+{
+ return ges_container_edit (GES_CONTAINER (element), NULL, -1,
+ GES_EDIT_MODE_TRIM, GES_EDGE_START, start);
+}
+
+/**
+ * ges_clip_add_asset:
+ * @clip: a #GESClip
+ * @asset: a #GESAsset with #GES_TYPE_TRACK_ELEMENT as extractable_type
+ *
+ * Extracts a #GESTrackElement from @asset and adds it to the @clip.
+ * Should only be called in order to add operations to a #GESClip,
+ * ni other cases TrackElement are added automatically when adding the
+ * #GESClip/#GESAsset to a layer.
+ *
+ * Takes a reference on @track_element.
+ *
+ * Returns: (transfer none)(allow-none): Created #GESTrackElement or NULL
+ * if an error happened
+ */
+GESTrackElement *
+ges_clip_add_asset (GESClip * clip, GESAsset * asset)
+{
+ GESTrackElement *element;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+ g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+ g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
+ (asset), GES_TYPE_TRACK_ELEMENT), NULL);
+
+ element = GES_TRACK_ELEMENT (ges_asset_extract (asset, NULL));
+
+ if (!ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (element)))
+ return NULL;
+
+ return element;
+
+}
+
+/**
+ * ges_clip_find_track_elements:
+ * @clip: a #GESClip
+ * @track: (allow-none): a #GESTrack or NULL
+ * @track_type: a #GESTrackType indicating the type of tracks in which elements
+ * should be searched.
+ * @type: a #GType indicating the type of track element you are looking
+ * for or %G_TYPE_NONE if you do not care about the track type.
+ *
+ * Finds all the #GESTrackElement controlled by @clip that is used in @track. You
+ * may optionally specify a GType to further narrow search criteria.
+ *
+ * Returns: (transfer full) (element-type GESTrackElement): a #GList of the
+ * #GESTrackElement contained in @clip.
+ * The refcount of the objects will be increased. The user will have to
+ * unref each #GESTrackElement and free the #GList.
+ */
+
+GList *
+ges_clip_find_track_elements (GESClip * clip, GESTrack * track,
+ GESTrackType track_type, GType type)
+{
+ GList *tmp;
+ GESTrack *tmptrack;
+ GESTrackElement *otmp;
+ GESTrackElement *foundElement;
+
+ GList *ret = NULL;
+
+ g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
+ g_return_val_if_fail (!(track == NULL && type == G_TYPE_NONE &&
+ track_type == GES_TRACK_TYPE_UNKNOWN), NULL);
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = g_list_next (tmp)) {
+ otmp = (GESTrackElement *) tmp->data;
+
+ if ((type != G_TYPE_NONE) && !G_TYPE_CHECK_INSTANCE_TYPE (tmp->data, type))
+ continue;
+
+ tmptrack = ges_track_element_get_track (otmp);
+ if (((track != NULL && tmptrack == track)) ||
+ (track_type != GES_TRACK_TYPE_UNKNOWN
+ && ges_track_element_get_track_type (otmp) == track_type)) {
+
+ foundElement = GES_TRACK_ELEMENT (tmp->data);
+
+ ret = g_list_append (ret, gst_object_ref (foundElement));
+ }
+ }
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_CLIP
+#define _GES_CLIP
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-timeline-element.h>
+#include <ges/ges-container.h>
+#include <ges/ges-types.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_CLIP ges_clip_get_type()
+#define GES_CLIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_CLIP, GESClip))
+#define GES_CLIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_CLIP, GESClipClass))
+#define GES_IS_CLIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_CLIP))
+#define GES_IS_CLIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_CLIP))
+#define GES_CLIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_CLIP, GESClipClass))
+
+typedef struct _GESClipPrivate GESClipPrivate;
+
+/**
+ * GESFillTrackElementFunc:
+ * @clip: the #GESClip controlling the track elements
+ * @track_element: the #GESTrackElement
+ * @nleobj: the GNonLin object that needs to be filled.
+ *
+ * A function that will be called when the GNonLin object of a corresponding
+ * track element needs to be filled.
+ *
+ * The implementer of this function shall add the proper #GstElement to @nleobj
+ * using gst_bin_add().
+ *
+ * Returns: TRUE if the implementer succesfully filled the @nleobj, else #FALSE.
+ */
+typedef gboolean (*GESFillTrackElementFunc) (GESClip *clip, GESTrackElement *track_element,
+ GstElement *nleobj);
+
+/**
+ * GESCreateTrackElementFunc:
+ * @clip: a #GESClip
+ * @type: a #GESTrackType
+ *
+ * Creates the 'primary' track element for this @clip.
+ *
+ * Subclasses should implement this method if they only provide a
+ * single #GESTrackElement per track.
+ *
+ * If the subclass needs to create more than one #GESTrackElement for a
+ * given track, then it should implement the 'create_track_elements'
+ * method instead.
+ *
+ * The implementer of this function shall return the proper #GESTrackElement
+ * that should be controlled by @clip for the given @track.
+ *
+ * The returned #GESTrackElement will be automatically added to the list
+ * of objects controlled by the #GESClip.
+ *
+ * Returns: the #GESTrackElement to be used, or %NULL if it can't provide one
+ * for the given @track.
+ */
+typedef GESTrackElement *(*GESCreateTrackElementFunc) (GESClip * clip, GESTrackType type);
+
+/**
+ * GESCreateTrackElementsFunc:
+ * @clip: a #GESClip
+ * @type: a #GESTrackType
+ *
+ * Create all track elements this clip handles for this type of track.
+ *
+ * Subclasses should implement this method if they potentially need to
+ * return more than one #GESTrackElement(s) for a given #GESTrack.
+ *
+ * Returns: %TRUE on success %FALSE on failure.
+ */
+
+typedef GList * (*GESCreateTrackElementsFunc) (GESClip * clip, GESTrackType type);
+
+/**
+ * GESClip:
+ *
+ * The #GESClip base class.
+ */
+struct _GESClip
+{
+ GESContainer parent;
+
+ /*< private >*/
+ GESClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+/**
+ * GESClipClass:
+ * @create_track_element: method to create a single #GESTrackElement for a given #GESTrack.
+ * @create_track_elements: method to create multiple #GESTrackElements for a
+ * #GESTrack.
+ *
+ * Subclasses can override the @create_track_element.
+ */
+struct _GESClipClass
+{
+ /*< private > */
+ GESContainerClass parent_class;
+
+ /*< public > */
+ GESCreateTrackElementFunc create_track_element;
+ GESCreateTrackElementsFunc create_track_elements;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+/****************************************************
+ * Standard *
+ ****************************************************/
+GES_API
+GType ges_clip_get_type (void);
+
+/****************************************************
+ * TrackElement handling *
+ ****************************************************/
+GES_API
+GESTrackType ges_clip_get_supported_formats (GESClip *clip);
+GES_API
+void ges_clip_set_supported_formats (GESClip *clip, GESTrackType supportedformats);
+GES_API
+GESTrackElement* ges_clip_add_asset (GESClip *clip, GESAsset *asset);
+GES_API
+GESTrackElement* ges_clip_find_track_element (GESClip *clip, GESTrack *track,
+ GType type);
+GES_API
+GList * ges_clip_find_track_elements (GESClip * clip, GESTrack * track,
+ GESTrackType track_type, GType type);
+
+/****************************************************
+ * Layer *
+ ****************************************************/
+GES_API
+GESLayer* ges_clip_get_layer (GESClip *clip);
+GES_API
+gboolean ges_clip_move_to_layer (GESClip *clip, GESLayer *layer);
+
+/****************************************************
+ * Effects *
+ ****************************************************/
+GES_API
+GList* ges_clip_get_top_effects (GESClip *clip);
+GES_API
+gint ges_clip_get_top_effect_position (GESClip *clip, GESBaseEffect *effect);
+GES_API
+gint ges_clip_get_top_effect_index (GESClip *clip, GESBaseEffect *effect);
+GES_API
+gboolean ges_clip_set_top_effect_priority (GESClip *clip, GESBaseEffect *effect,
+ guint newpriority);
+GES_API
+gboolean ges_clip_set_top_effect_index (GESClip *clip, GESBaseEffect *effect,
+ guint newindex);
+
+/****************************************************
+ * Editing *
+ ****************************************************/
+GES_API
+GESClip* ges_clip_split (GESClip *clip, guint64 position);
+
+G_END_DECLS
+#endif /* _GES_CLIP */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-command-line-formatter.h"
+
+#include "ges/ges-structured-interface.h"
+#include "ges-structure-parser.h"
+#include "ges-internal.h"
+#define YY_NO_UNISTD_H
+#include "ges-parse-lex.h"
+
+struct _GESCommandLineFormatterPrivate
+{
+ gpointer dummy;
+};
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESCommandLineFormatter, ges_command_line_formatter,
+ GES_TYPE_FORMATTER);
+
+static gboolean
+_ges_command_line_formatter_add_clip (GESTimeline * timeline,
+ GstStructure * structure, GError ** error);
+static gboolean
+_ges_command_line_formatter_add_effect (GESTimeline * timeline,
+ GstStructure * structure, GError ** error);
+static gboolean
+_ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
+ GstStructure * structure, GError ** error);
+static gboolean
+_ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
+ GstStructure * structure, GError ** error);
+
+typedef struct
+{
+ const gchar *long_name;
+ const gchar *short_name;
+ GType type;
+ const gchar *new_name;
+ const gchar *desc;
+} Property;
+
+// Currently Clip has the most properties.. adapt as needed
+#define MAX_PROPERTIES 8
+typedef struct
+{
+ const gchar *long_name;
+ gchar short_name;
+ ActionFromStructureFunc callback;
+ const gchar *description;
+ /* The first property must be the ID on the command line */
+ Property properties[MAX_PROPERTIES];
+} GESCommandLineOption;
+
+/* *INDENT-OFF* */
+static GESCommandLineOption options[] = {
+ {"clip", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_clip,
+ "<clip uri> - Adds a clip in the timeline.",
+ {
+ {
+ "uri", "n", 0, "asset-id",
+ "The URI of the media file."
+ },
+ {
+ "name", "n", 0, NULL,
+ "The name of the clip, can be used as an ID later."
+ },
+ {
+ "start", "s",GST_TYPE_CLOCK_TIME, NULL,
+ "The starting position of the clip in the timeline."
+ },
+ {
+ "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
+ "The duration of the clip."
+ },
+ {
+ "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
+ "The inpoint of the clip (time in the input file to start playing from)."
+ },
+ {
+ "track-types", "tt", 0, NULL,
+ "The type of the tracks where the clip should be used (audio or video or audio+video)."
+ },
+ {
+ "layer", "l", 0, NULL,
+ "The priority of the layer into which the clip should be added."
+ },
+ {NULL, 0, 0, NULL, FALSE},
+ },
+ },
+ {"effect", 'e', (ActionFromStructureFunc) _ges_command_line_formatter_add_effect,
+ "<effect bin description> - Adds an effect as specified by 'bin-description',\n"
+ "similar to gst-launch-style pipeline description, without setting properties\n"
+ "(see `set-` for information about how to set properties).\n",
+ {
+ {
+ "bin-description", "d", 0, "asset-id",
+ "gst-launch style bin description."
+ },
+ {
+ "element-name", "e", 0, NULL,
+ "The name of the element to apply the effect on."
+ },
+ {
+ "name", "n", 0, "child-name",
+ "The name to be given to the effect."
+ },
+ {NULL, NULL, 0, NULL, FALSE},
+ },
+ },
+ {"test-clip", 0, (ActionFromStructureFunc) _ges_command_line_formatter_add_test_clip,
+ "<test clip pattern> - Add a test clip in the timeline.",
+ {
+ {
+ "pattern", "p", 0, NULL,
+ "The testsource pattern name."
+ },
+ {
+ "name", "n", 0, NULL,
+ "The name of the clip, can be used as an ID later."
+ },
+ {
+ "start", "s",GST_TYPE_CLOCK_TIME, NULL,
+ "The starting position of the clip in the timeline."
+ },
+ {
+ "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
+ "The duration of the clip."
+ },
+ {
+ "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
+ "The inpoint of the clip (time in the input file to start playing)."
+ },
+ {
+ "layer", "l", 0, NULL,
+ "The priority of the layer into which the clip should be added."
+ },
+ {NULL, 0, 0, NULL, FALSE},
+ },
+ },
+ {"title", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_title_clip,
+ "<title text> - Adds a clip in the timeline.",
+ {
+ {
+ "text", "n", 0, NULL,
+ "The text to be used as title."
+ },
+ {
+ "name", "n", 0, NULL,
+ "The name of the clip, can be used as an ID later."
+ },
+ {
+ "start", "s",GST_TYPE_CLOCK_TIME, NULL,
+ "The starting position of the clip in the timeline."
+ },
+ {
+ "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
+ "The duration of the clip."
+ },
+ {
+ "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
+ "The inpoint of the clip (time in the input file to start playing from)."
+ },
+ {
+ "track-types", "tt", 0, NULL,
+ "The type of the tracks where the clip should be used (audio or video or audio+video)."
+ },
+ {
+ "layer", "l", 0, NULL,
+ "The priority of the layer into which the clip should be added."
+ },
+ {NULL, 0, 0, NULL, FALSE},
+ },
+ },
+ {
+ "set-", 0, NULL,
+ "<property name> <value> - Set a property on the last added element.\n"
+ "Any child property that exists on the previously added element\n"
+ "can be used as <property name>",
+ {
+ {NULL, NULL, 0, NULL, FALSE},
+ },
+ },
+};
+/* *INDENT-ON* */
+
+/* Should always be in the same order as the options */
+typedef enum
+{
+ CLIP,
+ EFFECT,
+ TEST_CLIP,
+ TITLE,
+ SET,
+} GESCommandLineOptionType;
+
+static gint /* -1: not present, 0: failure, 1: OK */
+_convert_to_clocktime (GstStructure * structure, const gchar * name,
+ GstClockTime default_value)
+{
+ gint res = 1;
+ gdouble val;
+ GValue d_val = { 0 };
+ GstClockTime timestamp;
+ const GValue *gvalue = gst_structure_get_value (structure, name);
+
+ if (gvalue == NULL) {
+ timestamp = default_value;
+
+ res = -1;
+
+ goto done;
+ }
+
+ if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME)
+ return 1;
+
+ g_value_init (&d_val, G_TYPE_DOUBLE);
+ if (!g_value_transform (gvalue, &d_val)) {
+ GST_ERROR ("Could not get timestamp for %s", name);
+
+ return 0;
+ }
+ val = g_value_get_double ((const GValue *) &d_val);
+
+ if (val == -1.0)
+ timestamp = GST_CLOCK_TIME_NONE;
+ else
+ timestamp = val * GST_SECOND;
+
+done:
+ gst_structure_set (structure, name, G_TYPE_UINT64, timestamp, NULL);
+
+ return res;
+}
+
+static gboolean
+_cleanup_fields (const Property * field_names, GstStructure * structure,
+ GError ** error)
+{
+ guint i;
+
+ for (i = 0; field_names[i].long_name; i++) {
+ gboolean exists = FALSE;
+
+ /* Move shortly named fields to longname variante */
+ if (gst_structure_has_field (structure, field_names[i].short_name)) {
+ exists = TRUE;
+
+ if (gst_structure_has_field (structure, field_names[i].long_name)) {
+ *error = g_error_new (GES_ERROR, 0, "Using short and long name"
+ " at the same time for property: %s, which one should I use?!",
+ field_names[i].long_name);
+
+ return FALSE;
+ } else {
+ const GValue *val =
+ gst_structure_get_value (structure, field_names[i].short_name);
+
+ gst_structure_set_value (structure, field_names[i].long_name, val);
+ gst_structure_remove_field (structure, field_names[i].short_name);
+ }
+ } else if (gst_structure_has_field (structure, field_names[i].long_name)) {
+ exists = TRUE;
+ }
+
+ if (exists) {
+ if (field_names[i].type == GST_TYPE_CLOCK_TIME) {
+ if (_convert_to_clocktime (structure, field_names[i].long_name, 0) == 0) {
+ *error = g_error_new (GES_ERROR, 0, "Could not convert"
+ " %s to GstClockTime", field_names[i].long_name);
+
+ return FALSE;
+ }
+ }
+ }
+
+ if (field_names[i].new_name
+ && gst_structure_has_field (structure, field_names[i].long_name)) {
+ const GValue *val =
+ gst_structure_get_value (structure, field_names[i].long_name);
+
+ gst_structure_set_value (structure, field_names[i].new_name, val);
+ gst_structure_remove_field (structure, field_names[i].long_name);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_ges_command_line_formatter_add_clip (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ if (!_cleanup_fields (options[CLIP].properties, structure, error))
+ return FALSE;
+
+ gst_structure_set (structure, "type", G_TYPE_STRING, "GESUriClip", NULL);
+
+ return _ges_add_clip_from_struct (timeline, structure, error);
+}
+
+static gboolean
+_ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
+ return FALSE;
+
+ gst_structure_set (structure, "type", G_TYPE_STRING, "GESTestClip", NULL);
+ gst_structure_set (structure, "asset-id", G_TYPE_STRING,
+ gst_structure_get_string (structure, "pattern"), NULL);
+
+ return _ges_add_clip_from_struct (timeline, structure, error);
+}
+
+static gboolean
+_ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
+ return FALSE;
+
+ gst_structure_set (structure, "type", G_TYPE_STRING, "GESTitleClip", NULL);
+ gst_structure_set (structure, "asset-id", G_TYPE_STRING, "GESTitleClip",
+ NULL);
+ GST_ERROR ("Structure: %" GST_PTR_FORMAT, structure);
+
+ return _ges_add_clip_from_struct (timeline, structure, error);
+}
+
+static gboolean
+_ges_command_line_formatter_add_effect (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ if (!_cleanup_fields (options[EFFECT].properties, structure, error))
+ return FALSE;
+
+ gst_structure_set (structure, "child-type", G_TYPE_STRING, "GESEffect", NULL);
+
+ return _ges_container_add_child_from_struct (timeline, structure, error);
+}
+
+gchar *
+ges_command_line_formatter_get_help (gint nargs, gchar ** commands)
+{
+ gint i;
+ GString *help = g_string_new (NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (options); i++) {
+ gboolean print = nargs == 0;
+ GESCommandLineOption option = options[i];
+
+ if (!print) {
+ gint j;
+
+ for (j = 0; j < nargs; j++) {
+ gchar *cname = commands[j][0] == '+' ? &commands[j][1] : commands[j];
+
+ if (!g_strcmp0 (cname, option.long_name)) {
+ print = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (print) {
+ g_string_append_printf (help, "%s%s %s\n",
+ option.properties[0].long_name ? "+" : "",
+ option.long_name, option.description);
+
+ if (option.properties[0].long_name) {
+ gint j;
+
+ g_string_append (help, " Properties:\n");
+
+ for (j = 1; option.properties[j].long_name; j++) {
+ Property prop = option.properties[j];
+ g_string_append_printf (help, " * %s: %s\n", prop.long_name,
+ prop.desc);
+ }
+ }
+
+ g_string_append (help, "\n");
+ }
+ }
+
+ return g_string_free (help, FALSE);
+}
+
+
+static gboolean
+_set_child_property (GESTimeline * timeline, GstStructure * structure,
+ GError ** error)
+{
+ return _ges_set_child_property_from_struct (timeline, structure, error);
+}
+
+#define EXEC(func,structure,error) G_STMT_START { \
+ gboolean res = ((ActionFromStructureFunc)func)(timeline, structure, error); \
+ if (!res) {\
+ GST_ERROR ("Could not execute: %" GST_PTR_FORMAT ", error: %s", structure, (*error)->message); \
+ goto fail; \
+ } \
+} G_STMT_END
+
+
+static GESStructureParser *
+_parse_structures (const gchar * string)
+{
+ yyscan_t scanner;
+ GESStructureParser *parser = ges_structure_parser_new ();
+
+ priv_ges_parse_yylex_init_extra (parser, &scanner);
+ priv_ges_parse_yy_scan_string (string, scanner);
+ priv_ges_parse_yylex (scanner);
+ priv_ges_parse_yylex_destroy (scanner);
+
+ ges_structure_parser_end_of_file (parser);
+ return parser;
+}
+
+static gboolean
+_can_load (GESFormatter * dummy_formatter, const gchar * string,
+ GError ** error)
+{
+ gboolean res = FALSE;
+ GESStructureParser *parser;
+
+ if (string == NULL)
+ return FALSE;
+
+ parser = _parse_structures (string);
+
+ if (parser->structures)
+ res = TRUE;
+
+ gst_object_unref (parser);
+
+ return res;
+}
+
+static gboolean
+_load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
+ GError ** error)
+{
+ guint i;
+ GList *tmp;
+ GError *err;
+ GESStructureParser *parser = _parse_structures (string);
+
+ err = ges_structure_parser_get_error (parser);
+
+ if (err) {
+ if (error)
+ *error = err;
+
+ return FALSE;
+ }
+
+ g_object_set (timeline, "auto-transition", TRUE, NULL);
+ if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
+ goto fail;
+
+ if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
+ goto fail;
+
+ /* Here we've finished initializing our timeline, we're
+ * ready to start using it... by solely working with the layer !*/
+ for (tmp = parser->structures; tmp; tmp = tmp->next) {
+ const gchar *name = gst_structure_get_name (tmp->data);
+ if (g_str_has_prefix (name, "set-")) {
+ EXEC (_set_child_property, tmp->data, &err);
+ continue;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (options); i++) {
+ if (gst_structure_has_name (tmp->data, options[i].long_name)
+ || (strlen (name) == 1 && *name == options[i].short_name)) {
+ EXEC (((ActionFromStructureFunc) options[i].callback), tmp->data, &err);
+ }
+ }
+ }
+
+ gst_object_unref (parser);
+
+ return TRUE;
+
+fail:
+ gst_object_unref (parser);
+ if (err) {
+ if (error)
+ *error = err;
+ }
+
+ return FALSE;
+}
+
+static void
+ges_command_line_formatter_init (GESCommandLineFormatter * formatter)
+{
+ formatter->priv = ges_command_line_formatter_get_instance_private (formatter);
+}
+
+static void
+ges_command_line_formatter_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ges_command_line_formatter_parent_class)->finalize (object);
+}
+
+static void
+ges_command_line_formatter_class_init (GESCommandLineFormatterClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (klass);
+
+ object_class->finalize = ges_command_line_formatter_finalize;
+
+ formatter_klass->can_load_uri = _can_load;
+ formatter_klass->load_from_uri = _load;
+ formatter_klass->rank = GST_RANK_MARGINAL;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GES_COMMAND_LINE_FORMATTER_H_
+#define _GES_COMMAND_LINE_FORMATTER_H_
+
+#include <glib-object.h>
+#include "ges-formatter.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_COMMAND_LINE_FORMATTER (ges_command_line_formatter_get_type ())
+#define GES_COMMAND_LINE_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_COMMAND_LINE_FORMATTER, GESCommandLineFormatter))
+#define GES_COMMAND_LINE_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_COMMAND_LINE_FORMATTER, GESCommandLineFormatterClass))
+#define GES_IS_COMMAND_LINE_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_COMMAND_LINE_FORMATTER))
+#define GES_IS_COMMAND_LINE_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_COMMAND_LINE_FORMATTER))
+#define GES_COMMAND_LINE_FORMATTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_COMMAND_LINE_FORMATTER, GESCommandLineFormatterClass))
+
+typedef struct _GESCommandLineFormatterClass GESCommandLineFormatterClass;
+typedef struct _GESCommandLineFormatter GESCommandLineFormatter;
+typedef struct _GESCommandLineFormatterPrivate GESCommandLineFormatterPrivate;
+
+
+struct _GESCommandLineFormatterClass
+{
+ GESFormatterClass parent_class;
+};
+
+struct _GESCommandLineFormatter
+{
+ GESFormatter parent_instance;
+
+ GESCommandLineFormatterPrivate *priv;
+};
+
+GES_API
+GType ges_command_line_formatter_get_type (void);
+GES_API
+gchar * ges_command_line_formatter_get_help (gint nargs, gchar ** commands);
+
+G_END_DECLS
+
+#endif /* _GES_COMMAND_LINE_FORMATTER_H_ */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
+ * <2013> Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gescontainer
+ * @title: GESContainer
+ * @short_description: Base Class for objects responsible for controlling other
+ * GESTimelineElement-s
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-container.h"
+#include "ges.h"
+#include "ges-internal.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY_STATIC (ges_container_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT ges_container_debug
+
+/* Mapping of relationship between a Container and the TimelineElements
+ * it controls
+ *
+ * NOTE : Does it make sense to make it public in the future ?
+ */
+typedef struct
+{
+ GESTimelineElement *child;
+
+ GstClockTime start_offset;
+ GstClockTime duration_offset;
+ GstClockTime inpoint_offset;
+ gint32 priority_offset;
+
+ guint start_notifyid;
+ guint duration_notifyid;
+ guint inpoint_notifyid;
+} ChildMapping;
+
+enum
+{
+ CHILD_ADDED_SIGNAL,
+ CHILD_REMOVED_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint ges_container_signals[LAST_SIGNAL] = { 0 };
+
+struct _GESContainerPrivate
+{
+ /*< public > */
+ GESLayer *layer;
+
+ /*< private > */
+ /* Set to TRUE when the container is doing updates of track object
+ * properties so we don't end up in infinite property update loops
+ */
+ GHashTable *mappings;
+
+ /* List of GESTimelineElement being in the "child-added" signal
+ * emission stage */
+ GList *adding_children;
+
+ GList *copied_children;
+};
+
+enum
+{
+ PROP_0,
+ PROP_HEIGHT,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESContainer, ges_container,
+ GES_TYPE_TIMELINE_ELEMENT);
+
+/************************
+ * Private methods *
+ ************************/
+static void
+_free_mapping (ChildMapping * mapping)
+{
+ GESTimelineElement *child = mapping->child;
+
+ /* Disconnect all notify listeners */
+ if (mapping->start_notifyid)
+ g_signal_handler_disconnect (child, mapping->start_notifyid);
+ if (mapping->duration_notifyid)
+ g_signal_handler_disconnect (child, mapping->duration_notifyid);
+ if (mapping->inpoint_notifyid)
+ g_signal_handler_disconnect (child, mapping->inpoint_notifyid);
+
+ ges_timeline_element_set_parent (child, NULL);
+ g_slice_free (ChildMapping, mapping);
+}
+
+static gint
+compare_grouping_prio (GType * a, GType * b)
+{
+ gint ret = 0;
+ GObjectClass *aclass = g_type_class_ref (*a);
+ GObjectClass *bclass = g_type_class_ref (*b);
+
+ /* We want higher prios to be first */
+ if (GES_CONTAINER_CLASS (aclass)->grouping_priority <
+ GES_CONTAINER_CLASS (bclass)->grouping_priority)
+ ret = 1;
+ else if (GES_CONTAINER_CLASS (aclass)->grouping_priority >
+ GES_CONTAINER_CLASS (bclass)->grouping_priority)
+ ret = -1;
+
+ g_type_class_unref (aclass);
+ g_type_class_unref (bclass);
+ return ret;
+}
+
+static void
+_resync_start_offsets (GESTimelineElement * child,
+ ChildMapping * map, GESContainer * container)
+{
+ map->start_offset = _START (container) - _START (child);
+}
+
+/*****************************************************
+ * *
+ * GESTimelineElement virtual methods implementation *
+ * *
+ *****************************************************/
+static gboolean
+_set_start (GESTimelineElement * element, GstClockTime start)
+{
+ GList *tmp;
+ ChildMapping *map;
+ GESContainer *container = GES_CONTAINER (element);
+ GESContainerPrivate *priv = container->priv;
+
+ GST_DEBUG_OBJECT (element, "Updating children offsets, (initiated_move: %"
+ GST_PTR_FORMAT ")", container->initiated_move);
+
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+
+ map = g_hash_table_lookup (priv->mappings, child);
+ map->start_offset = start - _START (child);
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
+{
+ GList *tmp;
+ GESContainer *container = GES_CONTAINER (element);
+
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+ ChildMapping *map = g_hash_table_lookup (container->priv->mappings, child);
+
+ map->inpoint_offset = inpoint - _INPOINT (child);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_set_duration (GESTimelineElement * element, GstClockTime duration)
+{
+ GList *tmp;
+ GESContainer *container = GES_CONTAINER (element);
+ GESContainerPrivate *priv = container->priv;
+
+ for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+ ChildMapping *map = g_hash_table_lookup (priv->mappings, child);
+
+ map->duration_offset = duration - _DURATION (child);
+ }
+
+ return TRUE;
+}
+
+static void
+_ges_container_add_child_properties (GESContainer * container,
+ GESTimelineElement * child)
+{
+ guint n_props, i;
+
+ GParamSpec **child_props =
+ ges_timeline_element_list_children_properties (child,
+ &n_props);
+
+ for (i = 0; i < n_props; i++) {
+ GObject *prop_child;
+ gchar *prop_name = g_strdup_printf ("%s::%s",
+ g_type_name (child_props[i]->owner_type),
+ child_props[i]->name);
+
+ if (ges_timeline_element_lookup_child (child, prop_name, &prop_child, NULL)) {
+ ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (container),
+ child_props[i], prop_child);
+ gst_object_unref (prop_child);
+
+ }
+ g_free (prop_name);
+ g_param_spec_unref (child_props[i]);
+ }
+
+ g_free (child_props);
+}
+
+static void
+_ges_container_remove_child_properties (GESContainer * container,
+ GESTimelineElement * child)
+{
+ guint n_props, i;
+
+ GParamSpec **child_props =
+ ges_timeline_element_list_children_properties (child,
+ &n_props);
+
+ for (i = 0; i < n_props; i++) {
+ GObject *prop_child;
+ gchar *prop_name = g_strdup_printf ("%s::%s",
+ g_type_name (child_props[i]->owner_type),
+ child_props[i]->name);
+
+ if (ges_timeline_element_lookup_child (child, prop_name, &prop_child, NULL)) {
+ ges_timeline_element_remove_child_property (GES_TIMELINE_ELEMENT
+ (container), child_props[i]);
+ gst_object_unref (prop_child);
+
+ }
+
+ g_free (prop_name);
+ g_param_spec_unref (child_props[i]);
+ }
+
+ g_free (child_props);
+}
+
+static GParamSpec **
+_list_children_properties (GESTimelineElement * self, guint * n_properties)
+{
+ GList *tmp;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next)
+ _ges_container_add_child_properties (GES_CONTAINER (self), tmp->data);
+
+ return
+ GES_TIMELINE_ELEMENT_CLASS
+ (ges_container_parent_class)->list_children_properties (self,
+ n_properties);
+}
+
+static gboolean
+_lookup_child (GESTimelineElement * self, const gchar * prop_name,
+ GObject ** child, GParamSpec ** pspec)
+{
+ GList *tmp;
+
+ /* FIXME Implement a syntax to precisely get properties by path */
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ if (ges_timeline_element_lookup_child (tmp->data, prop_name, child, pspec))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GESTrackType
+_get_track_types (GESTimelineElement * object)
+{
+ GESTrackType types = GES_TRACK_TYPE_UNKNOWN;
+ GList *tmp, *children = ges_container_get_children (GES_CONTAINER (object),
+ TRUE);
+
+ for (tmp = children; tmp; tmp = tmp->next) {
+ if (GES_IS_TRACK_ELEMENT (tmp->data)) {
+ types |= ges_timeline_element_get_track_types (tmp->data);
+ }
+ }
+
+ g_list_free_full (children, gst_object_unref);
+
+ return types ^ GES_TRACK_TYPE_UNKNOWN;
+}
+
+static void
+_deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
+{
+ GList *tmp;
+ GESContainer *self = GES_CONTAINER (element), *ccopy = GES_CONTAINER (copy);
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ ChildMapping *map;
+
+ map =
+ g_slice_dup (ChildMapping, g_hash_table_lookup (self->priv->mappings,
+ tmp->data));
+ map->child = ges_timeline_element_copy (tmp->data, TRUE);
+ map->start_notifyid = 0;
+ map->inpoint_notifyid = 0;
+ map->duration_notifyid = 0;
+
+ ccopy->priv->copied_children = g_list_prepend (ccopy->priv->copied_children,
+ map);
+ }
+}
+
+static GESTimelineElement *
+_paste (GESTimelineElement * element, GESTimelineElement * ref,
+ GstClockTime paste_position)
+{
+ GList *tmp;
+ ChildMapping *map;
+ GESContainer *ncontainer =
+ GES_CONTAINER (ges_timeline_element_copy (element, FALSE));
+ GESContainer *self = GES_CONTAINER (element);
+
+ for (tmp = self->priv->copied_children; tmp; tmp = tmp->next) {
+ GESTimelineElement *nchild;
+
+ map = tmp->data;
+ nchild =
+ ges_timeline_element_paste (map->child,
+ paste_position - map->start_offset);
+
+ if (!nchild) {
+ while (ncontainer->children)
+ ges_container_remove (ncontainer, ncontainer->children->data);
+
+ g_object_unref (ncontainer);
+ return NULL;
+ }
+
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (ncontainer),
+ GES_TIMELINE_ELEMENT_TIMELINE (ref));
+ ges_container_add (ncontainer, nchild);
+ }
+
+ return GES_TIMELINE_ELEMENT (ncontainer);
+}
+
+
+/******************************************
+ * *
+ * GObject virtual methods implementation *
+ * *
+ ******************************************/
+static void
+_dispose (GObject * object)
+{
+ GList *tmp;
+ GESContainer *self = GES_CONTAINER (object);
+ GList *children;
+
+ _ges_container_sort_children (self);
+ children = ges_container_get_children (self, FALSE);
+
+ for (tmp = g_list_last (children); tmp; tmp = tmp->prev)
+ ges_container_remove (self, tmp->data);
+
+ g_list_free_full (children, gst_object_unref);
+ self->children = NULL;
+
+ G_OBJECT_CLASS (ges_container_parent_class)->dispose (object);
+}
+
+static void
+_finalize (GObject * object)
+{
+ GESContainer *self = GES_CONTAINER (object);
+
+ g_list_free_full (self->priv->copied_children,
+ (GDestroyNotify) _free_mapping);
+
+ if (self->priv->mappings)
+ g_hash_table_destroy (self->priv->mappings);
+
+ G_OBJECT_CLASS (ges_container_parent_class)->finalize (object);
+}
+
+static void
+_get_property (GObject * container, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESContainer *tobj = GES_CONTAINER (container);
+
+ switch (property_id) {
+ case PROP_HEIGHT:
+ g_value_set_uint (value, tobj->height);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
+ }
+}
+
+static void
+_set_property (GObject * container, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
+ }
+}
+
+static void
+ges_container_class_init (GESContainerClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ GST_DEBUG_CATEGORY_INIT (ges_container_debug, "gescontainer",
+ GST_DEBUG_FG_YELLOW, "ges container");
+
+ object_class->get_property = _get_property;
+ object_class->set_property = _set_property;
+ object_class->dispose = _dispose;
+ object_class->finalize = _finalize;
+
+ /**
+ * GESContainer:height:
+ *
+ * The span of priorities which this container occupies.
+ */
+ properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
+ "The span of priorities this container occupies", 0, G_MAXUINT, 1,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_HEIGHT,
+ properties[PROP_HEIGHT]);
+
+ /**
+ * GESContainer::child-added:
+ * @container: the #GESContainer
+ * @element: the #GESTimelineElement that was added.
+ *
+ * Will be emitted after a child was added to @container.
+ * Usually you should connect with #g_signal_connect_after
+ * as in the first emission stage, the signal emission might
+ * get stopped internally.
+ */
+ ges_container_signals[CHILD_ADDED_SIGNAL] =
+ g_signal_new ("child-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESContainerClass, child_added),
+ NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GES_TYPE_TIMELINE_ELEMENT);
+
+ /**
+ * GESContainer::child-removed:
+ * @container: the #GESContainer
+ * @element: the #GESTimelineElement that was removed.
+ *
+ * Will be emitted after a child was removed from @container.
+ */
+ ges_container_signals[CHILD_REMOVED_SIGNAL] =
+ g_signal_new ("child-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESContainerClass, child_removed),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
+ GES_TYPE_TIMELINE_ELEMENT);
+
+
+ element_class->set_start = _set_start;
+ element_class->set_duration = _set_duration;
+ element_class->set_inpoint = _set_inpoint;
+ element_class->list_children_properties = _list_children_properties;
+ element_class->lookup_child = _lookup_child;
+ element_class->get_track_types = _get_track_types;
+ element_class->paste = _paste;
+ element_class->deep_copy = _deep_copy;
+
+ /* No default implementations */
+ klass->remove_child = NULL;
+ klass->add_child = NULL;
+ klass->ungroup = NULL;
+ klass->group = NULL;
+ klass->grouping_priority = 0;
+ klass->edit = NULL;
+}
+
+static void
+ges_container_init (GESContainer * self)
+{
+ self->priv = ges_container_get_instance_private (self);
+
+ /* FIXME, check why default was GST_SECOND? (before the existend of
+ * ges-container)
+ *
+ * _DURATION (self) = GST_SECOND; */
+ self->height = 1; /* FIXME Why 1 and not 0? */
+ self->children = NULL;
+
+ self->priv->mappings = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) _free_mapping);
+}
+
+/**********************************************
+ * *
+ * Property notifications from Children *
+ * *
+ **********************************************/
+static void
+_child_start_changed_cb (GESTimelineElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
+{
+ ChildMapping *map;
+ GstClockTime start;
+
+ GESContainerPrivate *priv = container->priv;
+ GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
+ GESChildrenControlMode pmode = container->children_control_mode;
+
+ map = g_hash_table_lookup (priv->mappings, child);
+ g_assert (map);
+
+ if (ELEMENT_FLAG_IS_SET (child, GES_TIMELINE_ELEMENT_SET_SIMPLE))
+ container->children_control_mode = GES_CHILDREN_UPDATE_ALL_VALUES;
+
+ switch (container->children_control_mode) {
+ case GES_CHILDREN_IGNORE_NOTIFIES:
+ return;
+ case GES_CHILDREN_UPDATE_ALL_VALUES:
+ _ges_container_sort_children (container);
+ start = container->children ?
+ _START (container->children->data) : _START (container);
+
+ if (start != _START (container)) {
+ _DURATION (container) = _END (container) - start;
+ _START (container) = start;
+
+ GST_DEBUG_OBJECT (container, "Child move made us move %" GES_FORMAT,
+ GES_ARGS (container));
+
+ g_object_notify (G_OBJECT (container), "start");
+ }
+
+ /* Falltrough! */
+ case GES_CHILDREN_UPDATE_OFFSETS:
+ map->start_offset = _START (container) - _START (child);
+ break;
+
+ case GES_CHILDREN_UPDATE:
+ /* We update all the children calling our set_start method */
+ container->initiated_move = child;
+ _set_start0 (element, _START (child) + map->start_offset);
+ container->initiated_move = NULL;
+ break;
+ default:
+ break;
+ }
+
+ if (ELEMENT_FLAG_IS_SET (child, GES_TIMELINE_ELEMENT_SET_SIMPLE))
+ container->children_control_mode = pmode;
+}
+
+static void
+_child_inpoint_changed_cb (GESTimelineElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
+{
+ ChildMapping *map;
+
+ GESContainerPrivate *priv = container->priv;
+ GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
+
+ if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
+ return;
+
+ map = g_hash_table_lookup (priv->mappings, child);
+ g_assert (map);
+
+ if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS
+ || ELEMENT_FLAG_IS_SET (child, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ map->inpoint_offset = _START (container) - _START (child);
+
+ return;
+ }
+
+ /* We update all the children calling our set_inpoint method */
+ container->initiated_move = child;
+ _set_inpoint0 (element, _INPOINT (child) + map->inpoint_offset);
+ container->initiated_move = NULL;
+}
+
+static void
+_child_duration_changed_cb (GESTimelineElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
+{
+ ChildMapping *map;
+
+ GList *tmp;
+ GstClockTime end = 0;
+ GESContainerPrivate *priv = container->priv;
+ GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
+ GESChildrenControlMode pmode = container->children_control_mode;
+
+ if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
+ return;
+
+ if (ELEMENT_FLAG_IS_SET (child, GES_TIMELINE_ELEMENT_SET_SIMPLE))
+ container->children_control_mode = GES_CHILDREN_UPDATE_ALL_VALUES;
+
+ map = g_hash_table_lookup (priv->mappings, child);
+ g_assert (map);
+
+ switch (container->children_control_mode) {
+ case GES_CHILDREN_IGNORE_NOTIFIES:
+ break;
+ case GES_CHILDREN_UPDATE_ALL_VALUES:
+ _ges_container_sort_children_by_end (container);
+
+ for (tmp = container->children; tmp; tmp = tmp->next)
+ end = MAX (end, _END (tmp->data));
+
+ if (end != _END (container)) {
+ _DURATION (container) = end - _START (container);
+ g_object_notify (G_OBJECT (container), "duration");
+ }
+ /* Falltrough */
+ case GES_CHILDREN_UPDATE_OFFSETS:
+ map->inpoint_offset = _START (container) - _START (child);
+ break;
+ case GES_CHILDREN_UPDATE:
+ /* We update all the children calling our set_duration method */
+ container->initiated_move = child;
+ _set_duration0 (element, _DURATION (child) + map->duration_offset);
+ container->initiated_move = NULL;
+ break;
+ default:
+ break;
+ }
+
+ if (ELEMENT_FLAG_IS_SET (child, GES_TIMELINE_ELEMENT_SET_SIMPLE))
+ container->children_control_mode = pmode;
+}
+
+/****************************************************
+ * *
+ * Internal methods implementation *
+ * *
+ ****************************************************/
+
+void
+_ges_container_sort_children (GESContainer * container)
+{
+ container->children = g_list_sort (container->children,
+ (GCompareFunc) element_start_compare);
+}
+
+void
+_ges_container_sort_children_by_end (GESContainer * container)
+{
+ container->children = g_list_sort (container->children,
+ (GCompareFunc) element_end_compare);
+}
+
+void
+_ges_container_set_height (GESContainer * container, guint32 height)
+{
+ if (container->height != height) {
+ container->height = height;
+ GST_DEBUG_OBJECT (container, "Updating height %i", container->height);
+ g_object_notify (G_OBJECT (container), "height");
+ }
+}
+
+gint
+_ges_container_get_priority_offset (GESContainer * container,
+ GESTimelineElement * elem)
+{
+ ChildMapping *map = g_hash_table_lookup (container->priv->mappings, elem);
+
+ g_return_val_if_fail (map, 0);
+
+ return map->priority_offset;
+}
+
+void
+_ges_container_set_priority_offset (GESContainer * container,
+ GESTimelineElement * elem, gint32 priority_offset)
+{
+ ChildMapping *map = g_hash_table_lookup (container->priv->mappings, elem);
+
+ g_return_if_fail (map);
+
+ map->priority_offset = priority_offset;
+}
+
+/**********************************************
+ * *
+ * API implementation *
+ * *
+ **********************************************/
+
+/**
+ * ges_container_add:
+ * @container: a #GESContainer
+ * @child: the #GESTimelineElement
+ *
+ * Add the #GESTimelineElement to the container.
+ *
+ * Returns: %TRUE on success, %FALSE on failure.
+ */
+gboolean
+ges_container_add (GESContainer * container, GESTimelineElement * child)
+{
+ ChildMapping *mapping;
+ gboolean notify_start = FALSE;
+ GESContainerClass *class;
+ GESContainerPrivate *priv;
+
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
+ g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (child) == NULL, FALSE);
+
+ class = GES_CONTAINER_GET_CLASS (container);
+ priv = container->priv;
+
+ GST_DEBUG_OBJECT (container, "adding timeline element %" GST_PTR_FORMAT,
+ child);
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ if (class->add_child) {
+ if (class->add_child (container, child) == FALSE) {
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+ GST_WARNING_OBJECT (container, "Erreur adding child %p", child);
+ return FALSE;
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ if (_START (container) > _START (child)) {
+ _START (container) = _START (child);
+
+ g_hash_table_foreach (priv->mappings, (GHFunc) _resync_start_offsets,
+ container);
+ notify_start = TRUE;
+ }
+
+ mapping = g_slice_new0 (ChildMapping);
+ mapping->child = gst_object_ref (child);
+ mapping->start_offset = _START (container) - _START (child);
+ mapping->duration_offset = _DURATION (container) - _DURATION (child);
+ mapping->inpoint_offset = _INPOINT (container) - _INPOINT (child);
+
+ g_hash_table_insert (priv->mappings, child, mapping);
+
+ container->children = g_list_prepend (container->children, child);
+
+ _ges_container_sort_children (container);
+
+ /* Listen to all property changes */
+ mapping->start_notifyid =
+ g_signal_connect (G_OBJECT (child), "notify::start",
+ G_CALLBACK (_child_start_changed_cb), container);
+ mapping->duration_notifyid =
+ g_signal_connect (G_OBJECT (child), "notify::duration",
+ G_CALLBACK (_child_duration_changed_cb), container);
+ mapping->inpoint_notifyid =
+ g_signal_connect (G_OBJECT (child), "notify::in-point",
+ G_CALLBACK (_child_inpoint_changed_cb), container);
+
+ if (ges_timeline_element_set_parent (child, GES_TIMELINE_ELEMENT (container))
+ == FALSE) {
+ if (class->remove_child)
+ class->remove_child (container, child);
+
+ g_hash_table_remove (priv->mappings, child);
+ container->children = g_list_remove (container->children, child);
+ _ges_container_sort_children (container);
+
+ return FALSE;
+ }
+
+ _ges_container_add_child_properties (container, child);
+
+ priv->adding_children = g_list_prepend (priv->adding_children, child);
+ g_signal_emit (container, ges_container_signals[CHILD_ADDED_SIGNAL], 0,
+ child);
+ priv->adding_children = g_list_remove (priv->adding_children, child);
+
+ if (notify_start)
+ g_object_notify (G_OBJECT (container), "start");
+
+ return TRUE;
+}
+
+/**
+ * ges_container_remove:
+ * @container: a #GESContainer
+ * @child: the #GESTimelineElement to release
+ *
+ * Release the @child from the control of @container.
+ *
+ * Returns: %TRUE if the @child was properly released, else %FALSE.
+ */
+gboolean
+ges_container_remove (GESContainer * container, GESTimelineElement * child)
+{
+ GESContainerClass *klass;
+ GESContainerPrivate *priv;
+
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
+
+ GST_DEBUG_OBJECT (container, "removing child: %" GST_PTR_FORMAT, child);
+
+ klass = GES_CONTAINER_GET_CLASS (container);
+ priv = container->priv;
+
+ if (!(g_hash_table_lookup (priv->mappings, child))) {
+ GST_WARNING_OBJECT (container, "Element isn't controlled by this "
+ "container");
+ return FALSE;
+ }
+
+ if (klass->remove_child) {
+ if (klass->remove_child (container, child) == FALSE)
+ return FALSE;
+ }
+
+ container->children = g_list_remove (container->children, child);
+ /* Let it live removing from our mappings */
+ g_hash_table_remove (priv->mappings, child);
+
+ _ges_container_remove_child_properties (container, child);
+
+ if (!g_list_find (container->priv->adding_children, child)) {
+ g_signal_emit (container, ges_container_signals[CHILD_REMOVED_SIGNAL], 0,
+ child);
+ } else {
+ GST_INFO_OBJECT (container, "Not emitting 'child-removed' signal as child"
+ " removal happend during 'child-added' signal emission");
+ }
+ gst_object_unref (child);
+
+ return TRUE;
+}
+
+static void
+_get_children_recursively (GESContainer * container, GList ** children)
+{
+ GList *tmp;
+
+ *children =
+ g_list_concat (*children, g_list_copy_deep (container->children,
+ (GCopyFunc) gst_object_ref, NULL));
+
+ for (tmp = container->children; tmp; tmp = tmp->next) {
+ GESTimelineElement *element = tmp->data;
+
+ if (GES_IS_CONTAINER (element))
+ _get_children_recursively (tmp->data, children);
+ }
+}
+
+/**
+ * ges_container_get_children:
+ * @container: a #GESContainer
+ * @recursive: Whether to recursively get children in @container
+ *
+ * Get the list of #GESTimelineElement contained in @container
+ * The user is responsible for unreffing the contained objects
+ * and freeing the list.
+ *
+ * Returns: (transfer full) (element-type GESTimelineElement): The list of
+ * timeline element contained in @container.
+ */
+GList *
+ges_container_get_children (GESContainer * container, gboolean recursive)
+{
+ GList *children = NULL;
+
+ g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
+
+ if (!recursive)
+ return g_list_copy_deep (container->children, (GCopyFunc) gst_object_ref,
+ NULL);
+
+ _get_children_recursively (container, &children);
+ return children;
+}
+
+/**
+ * ges_container_ungroup:
+ * @container: (transfer full): The #GESContainer to ungroup
+ * @recursive: Wether to recursively ungroup @container
+ *
+ * Ungroups the #GESTimelineElement contained in this GESContainer,
+ * creating new #GESContainer containing those #GESTimelineElement
+ * apropriately.
+ *
+ * Returns: (transfer full) (element-type GESContainer): The list of
+ * #GESContainer resulting from the ungrouping operation
+ * The user is responsible for unreffing the contained objects
+ * and freeing the list.
+ */
+GList *
+ges_container_ungroup (GESContainer * container, gboolean recursive)
+{
+ GESContainerClass *klass;
+
+ g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
+
+ GST_DEBUG_OBJECT (container, "Ungrouping container %s recursively",
+ recursive ? "" : "not");
+
+ klass = GES_CONTAINER_GET_CLASS (container);
+ if (klass->ungroup == NULL) {
+ GST_INFO_OBJECT (container, "No ungoup virtual method, doint nothing");
+ return NULL;
+ }
+
+ return klass->ungroup (container, recursive);
+}
+
+/**
+ * ges_container_group:
+ * @containers: (transfer none)(element-type GESContainer) (allow-none): The
+ * #GESContainer to group, they must all be in a same #GESTimeline
+ *
+ * Groups the #GESContainer-s provided in @containers. It creates a subclass
+ * of #GESContainer, depending on the containers provided in @containers.
+ * Basically, if all the containers in @containers should be contained in a same
+ * clip (all the #GESTrackElement they contain have the exact same
+ * start/inpoint/duration and are in the same layer), it will create a #GESClip
+ * otherwise a #GESGroup will be created
+ *
+ * Returns: (transfer none): The #GESContainer (subclass) resulting of the
+ * grouping
+ */
+GESContainer *
+ges_container_group (GList * containers)
+{
+ GList *tmp;
+ guint n_children;
+ GType *children_types;
+ GESTimelineElement *element;
+ GObjectClass *clip_class;
+
+ guint i = 0;
+ GESContainer *ret = NULL;
+ GESTimeline *timeline = NULL;
+
+ if (containers) {
+ element = GES_TIMELINE_ELEMENT (containers->data);
+ timeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
+ g_return_val_if_fail (timeline, NULL);
+ }
+
+ if (g_list_length (containers) == 1)
+ return containers->data;
+
+ for (tmp = containers; tmp; tmp = tmp->next) {
+ g_return_val_if_fail (GES_IS_CONTAINER (tmp->data), NULL);
+ g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (tmp->data) == NULL,
+ NULL);
+ g_return_val_if_fail (GES_TIMELINE_ELEMENT_TIMELINE (tmp->data) == timeline,
+ NULL);
+ }
+
+ children_types = g_type_children (GES_TYPE_CONTAINER, &n_children);
+ g_qsort_with_data (children_types, n_children, sizeof (GType),
+ (GCompareDataFunc) compare_grouping_prio, NULL);
+
+ for (i = 0; i < n_children; i++) {
+ clip_class = g_type_class_peek (children_types[i]);
+ ret = GES_CONTAINER_CLASS (clip_class)->group (containers);
+
+ if (ret)
+ break;
+ }
+
+ g_free (children_types);
+ return ret;
+}
+
+/**
+ * ges_container_edit:
+ * @container: the #GESClip to edit
+ * @layers: (element-type GESLayer): The layers you want the edit to
+ * happen in, %NULL means that the edition is done in all the
+ * #GESLayers contained in the current timeline.
+ * @new_layer_priority: The priority of the layer @container should land in.
+ * If the layer you're trying to move the container to doesn't exist, it will
+ * be created automatically. -1 means no move.
+ * @mode: The #GESEditMode in which the editition will happen.
+ * @edge: The #GESEdge the edit should happen on.
+ * @position: The position at which to edit @container (in nanosecond)
+ *
+ * Edit @container in the different exisiting #GESEditMode modes. In the case of
+ * slide, and roll, you need to specify a #GESEdge
+ *
+ * Returns: %TRUE if the container as been edited properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_container_edit (GESContainer * container, GList * layers,
+ gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
+{
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+
+ if (G_UNLIKELY (GES_CONTAINER_GET_CLASS (container)->edit == NULL)) {
+ GST_WARNING_OBJECT (container, "No edit vmethod implementation");
+ return FALSE;
+ }
+
+ return GES_CONTAINER_GET_CLASS (container)->edit (container, layers,
+ new_layer_priority, mode, edge, position);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_CONTAINER
+#define _GES_CONTAINER
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-timeline-element.h>
+#include <ges/ges-types.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_CONTAINER ges_container_get_type()
+#define GES_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_CONTAINER, GESContainer))
+#define GES_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_CONTAINER, GESContainerClass))
+#define GES_IS_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_CONTAINER))
+#define GES_IS_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_CONTAINER))
+#define GES_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_CONTAINER, GESContainerClass))
+
+typedef struct _GESContainerPrivate GESContainerPrivate;
+
+/* To be used by sublcasses only */
+typedef enum
+{
+ GES_CHILDREN_UPDATE,
+ GES_CHILDREN_IGNORE_NOTIFIES,
+ GES_CHILDREN_UPDATE_OFFSETS,
+ GES_CHILDREN_UPDATE_ALL_VALUES,
+ GES_CHILDREN_LAST
+} GESChildrenControlMode;
+
+/**
+ * GES_CONTAINER_HEIGHT:
+ * @obj: a #GESContainer
+ *
+ * The span of priorities this object occupies.
+ */
+#define GES_CONTAINER_HEIGHT(obj) (((GESContainer*)obj)->height)
+
+/**
+ * GES_CONTAINER_CHILDREN:
+ * @obj: a #GESContainer
+ *
+ * A #GList containing the children of @object
+ */
+#define GES_CONTAINER_CHILDREN(obj) (((GESContainer*)obj)->children)
+
+/**
+ * GESContainer:
+ * @children: (element-type GES.TimelineElement): A list of TimelineElement
+ * controlled by this Container. NOTE: Do not modify.
+ * @height: The span of priorities this container occupies
+ *
+ * The #GESContainer base class.
+ */
+struct _GESContainer
+{
+ GESTimelineElement parent;
+
+ /*< public > */
+ /*< readonly >*/
+ GList *children;
+
+ /* We don't add those properties to the priv struct for optimization and code
+ * readability purposes */
+ guint32 height; /* the span of priorities this object needs */
+
+ /* <protected> */
+ GESChildrenControlMode children_control_mode;
+ /*< readonly >*/
+ GESTimelineElement *initiated_move;
+
+ /*< private >*/
+ GESContainerPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+/**
+ * GESContainerClass:
+ * @child_added: Virtual method that is called right after a #GESTimelineElement is added
+ * @child_removed: Virtual method that is called right after a #GESTimelineElement is removed
+ * @remove_child: Virtual method to remove a child
+ * @add_child: Virtual method to add a child
+ * @ungroup: Ungroups the #GESTimelineElement contained in this #GESContainer, creating new
+ * @group: Groups the #GESContainers together
+ * #GESContainer containing those #GESTimelineElement apropriately.
+ */
+struct _GESContainerClass
+{
+ /*< private > */
+ GESTimelineElementClass parent_class;
+
+ /*< public > */
+ /* signals */
+ void (*child_added) (GESContainer *container, GESTimelineElement *element);
+ void (*child_removed) (GESContainer *container, GESTimelineElement *element);
+ gboolean (*add_child) (GESContainer *container, GESTimelineElement *element);
+ gboolean (*remove_child) (GESContainer *container, GESTimelineElement *element);
+ GList* (*ungroup) (GESContainer *container, gboolean recursive);
+ GESContainer * (*group) (GList *containers);
+ gboolean (*edit) (GESContainer * container,
+ GList * layers, gint new_layer_priority,
+ GESEditMode mode,
+ GESEdge edge,
+ guint64 position);
+
+
+
+ /*< private >*/
+ guint grouping_priority;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+GES_API
+GType ges_container_get_type (void);
+
+/* Children handling */
+GES_API
+GList* ges_container_get_children (GESContainer *container, gboolean recursive);
+GES_API
+gboolean ges_container_add (GESContainer *container, GESTimelineElement *child);
+GES_API
+gboolean ges_container_remove (GESContainer *container, GESTimelineElement *child);
+GES_API
+GList * ges_container_ungroup (GESContainer * container, gboolean recursive);
+GES_API
+GESContainer *ges_container_group (GList *containers);
+
+GES_API
+gboolean ges_container_edit (GESContainer * container,
+ GList * layers, gint new_layer_priority,
+ GESEditMode mode,
+ GESEdge edge,
+ guint64 position);
+
+G_END_DECLS
+#endif /* _GES_CONTAINER */
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+
+/* SECTION: geseffectasset
+ * @short_description: A GESAsset subclass specialized in GESEffect extraction
+ *
+ * This is internal, and implementation details, so we are not showing it in the
+ * documentation
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-effect-asset.h"
+#include "ges-track-element.h"
+#include "ges-internal.h"
+
+struct _GESEffectAssetPrivate
+{
+ gpointer nothing;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESEffectAsset, ges_effect_asset,
+ GES_TYPE_TRACK_ELEMENT_ASSET);
+
+static void
+_fill_track_type (GESAsset * asset)
+{
+ GESTrackType ttype;
+ gchar *bin_desc;
+ const gchar *id = ges_asset_get_id (asset);
+
+ bin_desc = ges_effect_assect_id_get_type_and_bindesc (id, &ttype, NULL);
+
+ if (bin_desc) {
+ ges_track_element_asset_set_track_type (GES_TRACK_ELEMENT_ASSET (asset),
+ ttype);
+ g_free (bin_desc);
+ } else {
+ GST_WARNING_OBJECT (asset, "No track type set, you should"
+ " specify one in [audio, video] as first component" " in the asset id");
+ }
+}
+
+/* GESAsset virtual methods implementation */
+static GESExtractable *
+_extract (GESAsset * asset, GError ** error)
+{
+ GESExtractable *effect;
+
+ effect = GES_ASSET_CLASS (ges_effect_asset_parent_class)->extract (asset,
+ error);
+
+ if (effect == NULL || (error && *error)) {
+ effect = NULL;
+
+ return NULL;
+ }
+
+ return effect;
+}
+
+static void
+ges_effect_asset_init (GESEffectAsset * self)
+{
+ self->priv = ges_effect_asset_get_instance_private (self);
+}
+
+static void
+ges_effect_asset_constructed (GObject * object)
+{
+ _fill_track_type (GES_ASSET (object));
+}
+
+static void
+ges_effect_asset_finalize (GObject * object)
+{
+ /* TODO: Add deinitalization code here */
+
+ G_OBJECT_CLASS (ges_effect_asset_parent_class)->finalize (object);
+}
+
+static void
+ges_effect_asset_class_init (GESEffectAssetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESAssetClass *asset_class = GES_ASSET_CLASS (klass);
+
+ object_class->finalize = ges_effect_asset_finalize;
+ object_class->constructed = ges_effect_asset_constructed;
+ asset_class->extract = _extract;
+}
+
+gchar *
+ges_effect_assect_id_get_type_and_bindesc (const char *id,
+ GESTrackType * track_type, GError ** error)
+{
+ GList *tmp;
+ GstElement *effect;
+ gchar **typebin_desc = NULL;
+ gchar *bindesc = NULL;
+
+ *track_type = GES_TRACK_TYPE_UNKNOWN;
+ typebin_desc = g_strsplit (id, " ", 2);
+ if (!g_strcmp0 (typebin_desc[0], "audio")) {
+ *track_type = GES_TRACK_TYPE_AUDIO;
+ bindesc = g_strdup (typebin_desc[1]);
+ } else if (!g_strcmp0 (typebin_desc[0], "video")) {
+ *track_type = GES_TRACK_TYPE_VIDEO;
+ bindesc = g_strdup (typebin_desc[1]);
+ } else {
+ bindesc = g_strdup (id);
+ }
+
+ g_strfreev (typebin_desc);
+
+ effect = gst_parse_bin_from_description (bindesc, TRUE, error);
+ if (effect == NULL) {
+ g_free (bindesc);
+
+ GST_ERROR ("Could not create element from: %s", id);
+
+ return NULL;
+ }
+
+ if (*track_type != GES_TRACK_TYPE_UNKNOWN) {
+ gst_object_unref (effect);
+
+ return bindesc;
+ }
+
+ for (tmp = GST_BIN_CHILDREN (effect); tmp; tmp = tmp->next) {
+ GstElementFactory *factory =
+ gst_element_get_factory (GST_ELEMENT (tmp->data));
+ const gchar *klass =
+ gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
+
+ if (g_strrstr (klass, "Effect") || g_strrstr (klass, "Filter")) {
+ if (g_strrstr (klass, "Audio")) {
+ *track_type = GES_TRACK_TYPE_AUDIO;
+ break;
+ } else if (g_strrstr (klass, "Video")) {
+ *track_type = GES_TRACK_TYPE_VIDEO;
+ break;
+ }
+ }
+ }
+
+ gst_object_unref (effect);
+
+ if (*track_type == GES_TRACK_TYPE_UNKNOWN) {
+ *track_type = GES_TRACK_TYPE_VIDEO;
+ GST_ERROR ("Could not determine track type for %s, defaulting to video",
+ id);
+ }
+
+ return bindesc;
+}
--- /dev/null
+
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GES_EFFECT_ASSET_H_
+#define _GES_EFFECT_ASSET_H_
+
+#include <glib-object.h>
+#include "ges-track-element-asset.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_EFFECT_ASSET (ges_effect_asset_get_type ())
+#define GES_EFFECT_ASSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_EFFECT_ASSET, GESEffectAsset))
+#define GES_EFFECT_ASSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_EFFECT_ASSET, GESEffectAssetClass))
+#define GES_IS_EFFECT_ASSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_EFFECT_ASSET))
+#define GES_IS_EFFECT_ASSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_EFFECT_ASSET))
+#define GES_EFFECT_ASSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_EFFECT_ASSET, GESEffectAssetClass))
+
+typedef struct _GESEffectAssetClass GESEffectAssetClass;
+typedef struct _GESEffectAsset GESEffectAsset;
+typedef struct _GESEffectAssetPrivate GESEffectAssetPrivate;
+
+
+struct _GESEffectAssetClass
+{
+ GESTrackElementAssetClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESEffectAsset
+{
+ GESTrackElementAsset parent_instance;
+
+ GESEffectAssetPrivate *priv;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_effect_asset_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* _GES_EFFECT_ASSET_H_ */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2011 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: geseffectclip
+ * @title: GESEffectClip
+ * @short_description: An effect created by parse-launch style bin descriptions
+ * in a GESLayer
+ *
+ * The effect will be applied on the sources that have lower priorities
+ * (higher number) between the inpoint and the end of it.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+#include "ges-internal.h"
+#include "ges-types.h"
+
+struct _GESEffectClipPrivate
+{
+ gchar *video_bin_description;
+ gchar *audio_bin_description;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESEffectClip, ges_effect_clip,
+ GES_TYPE_BASE_EFFECT_CLIP);
+
+enum
+{
+ PROP_0,
+ PROP_VIDEO_BIN_DESCRIPTION,
+ PROP_AUDIO_BIN_DESCRIPTION,
+};
+
+static void ges_effect_clip_finalize (GObject * object);
+static GESTrackElement *_create_track_element (GESClip * self,
+ GESTrackType type);
+
+static void
+ges_effect_clip_finalize (GObject * object)
+{
+ GESEffectClipPrivate *priv = GES_EFFECT_CLIP (object)->priv;
+
+ g_free (priv->audio_bin_description);
+ g_free (priv->video_bin_description);
+
+ G_OBJECT_CLASS (ges_effect_clip_parent_class)->finalize (object);
+}
+
+static void
+ges_effect_clip_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ GESEffectClipPrivate *priv = GES_EFFECT_CLIP (object)->priv;
+
+ switch (property_id) {
+ case PROP_VIDEO_BIN_DESCRIPTION:
+ g_value_set_string (value, priv->video_bin_description);
+ break;
+ case PROP_AUDIO_BIN_DESCRIPTION:
+ g_value_set_string (value, priv->audio_bin_description);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_effect_clip_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ GESEffectClip *self = GES_EFFECT_CLIP (object);
+
+ switch (property_id) {
+ case PROP_VIDEO_BIN_DESCRIPTION:
+ self->priv->video_bin_description = g_value_dup_string (value);
+ break;
+ case PROP_AUDIO_BIN_DESCRIPTION:
+ self->priv->audio_bin_description = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_effect_clip_class_init (GESEffectClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESClipClass *timobj_class = GES_CLIP_CLASS (klass);
+
+ object_class->get_property = ges_effect_clip_get_property;
+ object_class->set_property = ges_effect_clip_set_property;
+ object_class->finalize = ges_effect_clip_finalize;
+
+ /**
+ * GESEffectClip:video-bin-description:
+ *
+ * The description of the video track of the effect bin with a gst-launch-style
+ * pipeline description. This should be used for test purposes.
+ *
+ * Example: "videobalance saturation=1.5 hue=+0.5"
+ */
+ g_object_class_install_property (object_class, PROP_VIDEO_BIN_DESCRIPTION,
+ g_param_spec_string ("video-bin-description",
+ "Video bin description",
+ "Description of the video track of the effect",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GESEffectClip:audio-bin-description:
+ *
+ * The description of the audio track of the effect bin with a gst-launch-style
+ * pipeline description. This should be used for test purposes.
+ *
+ * Example: "audiopanorama panorama=1.0"
+ */
+ g_object_class_install_property (object_class, PROP_AUDIO_BIN_DESCRIPTION,
+ g_param_spec_string ("audio-bin-description",
+ "bin description",
+ "Bin description of the audio track of the effect",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ timobj_class->create_track_element = _create_track_element;
+}
+
+static void
+ges_effect_clip_init (GESEffectClip * self)
+{
+ self->priv = ges_effect_clip_get_instance_private (self);
+}
+
+static GESTrackElement *
+_create_track_element (GESClip * self, GESTrackType type)
+{
+ const gchar *bin_description = NULL;
+ GESEffectClip *effect = GES_EFFECT_CLIP (self);
+
+ if (type == GES_TRACK_TYPE_VIDEO) {
+ bin_description = effect->priv->video_bin_description;
+ } else if (type == GES_TRACK_TYPE_AUDIO) {
+ bin_description = effect->priv->audio_bin_description;
+ }
+
+ if (bin_description) {
+ /* FIXME Work with a GESAsset here! */
+ return g_object_new (GES_TYPE_EFFECT, "bin-description",
+ bin_description, "track-type", type, NULL);
+ }
+
+ GST_WARNING ("Effect doesn't handle this track type");
+ return NULL;
+}
+
+/**
+ * ges_effect_clip_new:
+ * @video_bin_description: The gst-launch like bin description of the effect
+ * @audio_bin_description: The gst-launch like bin description of the effect
+ *
+ * Creates a new #GESEffectClip from the description of the bin.
+ *
+ * Returns: (transfer floating) (nullable): a newly created #GESEffectClip, or
+ * %NULL if something went wrong.
+ */
+GESEffectClip *
+ges_effect_clip_new (const gchar * video_bin_description,
+ const gchar * audio_bin_description)
+{
+ /* FIXME Handle GESAsset! */
+ return g_object_new (GES_TYPE_EFFECT_CLIP,
+ "video-bin-description", video_bin_description,
+ "audio-bin-description", audio_bin_description, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2011 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_EFFECT_CLIP
+#define _GES_EFFECT_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_EFFECT_CLIP ges_effect_clip_get_type()
+
+#define GES_EFFECT_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_EFFECT_CLIP, GESEffectClip))
+
+#define GES_EFFECT_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_EFFECT_CLIP, GESEffectClipClass))
+
+#define GES_IS_EFFECT_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_EFFECT_CLIP))
+
+#define GES_IS_EFFECT_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_EFFECT_CLIP))
+
+#define GES_EFFECT_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_EFFECT_CLIP, GESEffectClipClass))
+
+typedef struct _GESEffectClipPrivate GESEffectClipPrivate;
+
+/**
+ * GESEffectClip:
+ */
+struct _GESEffectClip {
+ /*< private >*/
+ GESBaseEffectClip parent;
+
+ GESEffectClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESEffectClipClass:
+ *
+ */
+
+struct _GESEffectClipClass {
+ /*< private >*/
+ GESBaseEffectClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_effect_clip_get_type (void);
+
+GES_API GESEffectClip *
+ges_effect_clip_new (const gchar * video_bin_description,
+ const gchar * audio_bin_description);
+
+G_END_DECLS
+#endif /* _GES_EFFECT_CLIP */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:geseffect
+ * @title: GESEffect
+ * @short_description: adds an effect build from a parse-launch style
+ * bin description to a stream in a GESSourceClip or a GESLayer
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-extractable.h"
+#include "ges-track-element.h"
+#include "ges-base-effect.h"
+#include "ges-effect-asset.h"
+#include "ges-effect.h"
+
+static void ges_extractable_interface_init (GESExtractableInterface * iface);
+
+
+static void ges_effect_dispose (GObject * object);
+static void ges_effect_finalize (GObject * object);
+static GstElement *ges_effect_create_element (GESTrackElement * self);
+
+struct _GESEffectPrivate
+{
+ gchar *bin_description;
+};
+
+enum
+{
+ PROP_0,
+ PROP_BIN_DESCRIPTION,
+};
+
+G_DEFINE_TYPE_WITH_CODE (GESEffect,
+ ges_effect, GES_TYPE_BASE_EFFECT, G_ADD_PRIVATE (GESEffect)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+static gchar *
+extractable_check_id (GType type, const gchar * id, GError ** error)
+{
+ gchar *bin_desc, *real_id;
+ GESTrackType ttype;
+
+ bin_desc = ges_effect_assect_id_get_type_and_bindesc (id, &ttype, error);
+
+ if (bin_desc == NULL)
+ return NULL;
+
+ if (ttype == GES_TRACK_TYPE_AUDIO)
+ real_id = g_strdup_printf ("audio %s", bin_desc);
+ else if (ttype == GES_TRACK_TYPE_VIDEO)
+ real_id = g_strdup_printf ("video %s", bin_desc);
+ else
+ g_assert_not_reached ();
+
+ g_free (bin_desc);
+
+ return real_id;
+}
+
+static GParameter *
+extractable_get_parameters_from_id (const gchar * id, guint * n_params)
+{
+ GParameter *params = g_new0 (GParameter, 3);
+ gchar *bin_desc;
+ GESTrackType ttype;
+
+ bin_desc = ges_effect_assect_id_get_type_and_bindesc (id, &ttype, NULL);
+
+ params[0].name = "bin-description";
+ g_value_init (¶ms[0].value, G_TYPE_STRING);
+ g_value_set_string (¶ms[0].value, bin_desc);
+
+ params[1].name = "track-type";
+ g_value_init (¶ms[1].value, GES_TYPE_TRACK_TYPE);
+ g_value_set_flags (¶ms[1].value, ttype);
+
+ *n_params = 2;
+
+ g_free (bin_desc);
+ return params;
+}
+
+static gchar *
+extractable_get_id (GESExtractable * self)
+{
+ return g_strdup (GES_EFFECT (self)->priv->bin_description);
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_EFFECT_ASSET;
+ iface->check_id = (GESExtractableCheckId) extractable_check_id;
+ iface->get_parameters_from_id = extractable_get_parameters_from_id;
+ iface->get_id = extractable_get_id;
+}
+
+static int
+property_name_compare (gconstpointer s1, gconstpointer s2)
+{
+ return g_strcmp0 ((const gchar *) s1, (const gchar *) s2);
+}
+
+static void
+_set_child_property (GESTimelineElement * self G_GNUC_UNUSED, GObject * child,
+ GParamSpec * pspec, GValue * value)
+{
+ GESEffectClass *klass = GES_EFFECT_GET_CLASS (self);
+ gchar *full_property_name;
+
+ GES_TIMELINE_ELEMENT_CLASS
+ (ges_effect_parent_class)->set_child_property (self, child, pspec, value);
+
+ full_property_name = g_strdup_printf ("%s::%s", G_OBJECT_TYPE_NAME (child),
+ pspec->name);
+
+ if (g_list_find_custom (klass->rate_properties, full_property_name,
+ property_name_compare)) {
+ GstElement *nleobject =
+ ges_track_element_get_nleobject (GES_TRACK_ELEMENT (self));
+ gdouble media_duration_factor =
+ ges_timeline_element_get_media_duration_factor (self);
+
+ g_object_set (nleobject, "media-duration-factor", media_duration_factor,
+ NULL);
+ }
+
+ g_free (full_property_name);
+}
+
+static void
+ges_effect_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ GESEffectPrivate *priv = GES_EFFECT (object)->priv;
+
+ switch (property_id) {
+ case PROP_BIN_DESCRIPTION:
+ g_value_set_string (value, priv->bin_description);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_effect_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ GESEffect *self = GES_EFFECT (object);
+
+ switch (property_id) {
+ case PROP_BIN_DESCRIPTION:
+ self->priv->bin_description = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_effect_class_init (GESEffectClass * klass)
+{
+ GObjectClass *object_class;
+ GESTrackElementClass *obj_bg_class;
+ GESTimelineElementClass *element_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ obj_bg_class = GES_TRACK_ELEMENT_CLASS (klass);
+ element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_effect_get_property;
+ object_class->set_property = ges_effect_set_property;
+ object_class->dispose = ges_effect_dispose;
+ object_class->finalize = ges_effect_finalize;
+
+ obj_bg_class->create_element = ges_effect_create_element;
+ element_class->set_child_property = _set_child_property;
+
+ klass->rate_properties = NULL;
+ ges_effect_class_register_rate_property (klass, "scaletempo", "rate");
+ ges_effect_class_register_rate_property (klass, "pitch", "tempo");
+ ges_effect_class_register_rate_property (klass, "pitch", "rate");
+ ges_effect_class_register_rate_property (klass, "videorate", "rate");
+
+ /**
+ * GESEffect:bin-description:
+ *
+ * The description of the effect bin with a gst-launch-style
+ * pipeline description.
+ *
+ * Example: "videobalance saturation=1.5 hue=+0.5"
+ */
+ g_object_class_install_property (object_class, PROP_BIN_DESCRIPTION,
+ g_param_spec_string ("bin-description",
+ "bin description",
+ "Bin description of the effect",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ges_effect_init (GESEffect * self)
+{
+ self->priv = ges_effect_get_instance_private (self);
+}
+
+static void
+ges_effect_dispose (GObject * object)
+{
+ G_OBJECT_CLASS (ges_effect_parent_class)->dispose (object);
+}
+
+static void
+ges_effect_finalize (GObject * object)
+{
+ GESEffect *self = GES_EFFECT (object);
+
+ if (self->priv->bin_description)
+ g_free (self->priv->bin_description);
+
+ G_OBJECT_CLASS (ges_effect_parent_class)->finalize (object);
+}
+
+static void
+ghost_compatible_pads (GstElement * bin, GstElement * child,
+ GstCaps * valid_caps, gint * n_src, gint * n_sink)
+{
+ GList *tmp;
+
+ for (tmp = child->pads; tmp; tmp = tmp->next) {
+ GstCaps *caps;
+ GstPad *pad = tmp->data;
+
+ if (GST_PAD_PEER (pad))
+ continue;
+
+ caps = gst_pad_query_caps (pad, NULL);
+
+ if (gst_caps_can_intersect (caps, valid_caps)) {
+ gchar *name =
+ g_strdup_printf ("%s_%d", GST_PAD_IS_SINK (pad) ? "sink" : "src",
+ GST_PAD_IS_SINK (pad) ? *n_sink++ : *n_src++);
+
+ GST_DEBUG_OBJECT (bin, "Ghosting pad: %" GST_PTR_FORMAT, pad);
+ gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new (name, pad));
+ g_free (name);
+ } else {
+ GST_DEBUG_OBJECT (pad, "Can't ghost pad %" GST_PTR_FORMAT, caps);
+ }
+
+ gst_caps_unref (caps);
+ }
+}
+
+static GstElement *
+ges_effect_create_element (GESTrackElement * object)
+{
+ GList *tmp;
+ GstElement *effect;
+ gchar *bin_desc;
+ GstCaps *valid_caps;
+ gint n_src = 0, n_sink = 0;
+
+ GError *error = NULL;
+ GESEffect *self = GES_EFFECT (object);
+ const gchar *blacklisted_factories[] =
+ { "audioconvert", "audioresample", "videoconvert", NULL };
+
+ GESTrackType type = ges_track_element_get_track_type (object);
+
+ if (type == GES_TRACK_TYPE_VIDEO) {
+ bin_desc = g_strconcat ("videoconvert name=pre_video_convert ! ",
+ self->priv->bin_description, " ! videoconvert name=post_video_convert",
+ NULL);
+ valid_caps = gst_caps_from_string ("video/x-raw(ANY)");
+ } else if (type == GES_TRACK_TYPE_AUDIO) {
+ bin_desc =
+ g_strconcat ("audioconvert ! audioresample !",
+ self->priv->bin_description, NULL);
+ valid_caps = gst_caps_from_string ("audio/x-raw(ANY)");
+ } else {
+ g_assert_not_reached ();
+ }
+
+ effect = gst_parse_bin_from_description (bin_desc, FALSE, &error);
+ g_free (bin_desc);
+ if (error != NULL) {
+ GST_ERROR ("An error occured while creating the GstElement: %s",
+ error->message);
+ g_error_free (error);
+ goto fail;
+ }
+
+ for (tmp = GST_BIN_CHILDREN (effect); tmp; tmp = tmp->next) {
+ ghost_compatible_pads (effect, tmp->data, valid_caps, &n_src, &n_sink);
+
+ if (n_src > 1) {
+ GST_ERROR ("More than 1 source pad in the effect, that is not possible");
+
+ goto fail;
+ }
+ }
+
+ ges_track_element_add_children_props (object, effect, NULL,
+ blacklisted_factories, NULL);
+
+done:
+ gst_clear_caps (&valid_caps);
+
+ return effect;
+
+fail:
+ gst_clear_object (&effect);
+
+ goto done;
+}
+
+/**
+ * ges_effect_new:
+ * @bin_description: The gst-launch like bin description of the effect
+ *
+ * Creates a new #GESEffect from the description of the bin. It should be
+ * possible to determine the type of the effect through the element
+ * 'klass' metadata of the GstElements that will be created.
+ * In that corner case, you should use:
+ * #ges_asset_request (GES_TYPE_EFFECT, "audio your ! bin ! description", NULL);
+ * and extract that asset to be in full control.
+ *
+ * Returns: (nullable): a newly created #GESEffect, or %NULL if something went
+ * wrong.
+ */
+GESEffect *
+ges_effect_new (const gchar * bin_description)
+{
+ GESEffect *effect;
+ GESAsset *asset = ges_asset_request (GES_TYPE_EFFECT,
+ bin_description, NULL);
+
+ g_return_val_if_fail (asset, NULL);
+
+ effect = GES_EFFECT (ges_asset_extract (asset, NULL));
+
+ gst_object_unref (asset);
+
+ return effect;
+}
+
+/**
+ * ges_effect_class_register_rate_property:
+ * @klass: Instance of the GESEffectClass
+ * @element_name: Name of the GstElement that changes the rate
+ * @property_name: Name of the property that changes the rate
+ *
+ * Register an element that can change the rate at which media is playing. The
+ * property type must be float or double, and must be a factor of the rate,
+ * i.e. a value of 2.0 must mean that the media plays twice as fast. For
+ * example, this is true for the properties 'rate' and 'tempo' of the element
+ * 'pitch', which is already registered by default. By registering the element,
+ * timeline duration can be correctly converted into media duration, allowing
+ * the right segment seeks to be sent to the sources.
+ *
+ * A reference to the GESEffectClass can be obtained as follows:
+ * GES_EFFECT_CLASS (g_type_class_ref (GES_TYPE_EFFECT));
+ *
+ * Returns: whether the rate property was succesfully registered. When this
+ * method returns false, a warning is emitted with more information.
+ */
+gboolean
+ges_effect_class_register_rate_property (GESEffectClass * klass,
+ const gchar * element_name, const gchar * property_name)
+{
+ GstElementFactory *element_factory = NULL;
+ GstElement *element = NULL;
+ GParamSpec *pspec = NULL;
+ gchar *full_property_name = NULL;
+ GType param_type;
+ gboolean res = FALSE;
+
+ element_factory = gst_element_factory_find (element_name);
+ if (element_factory == NULL) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the element factory could not be found",
+ property_name, element_name);
+ goto fail;
+ }
+
+ element = gst_element_factory_create (element_factory, NULL);
+ if (element == NULL) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the element could not be constructed",
+ property_name, element_name);
+ goto fail;
+ }
+
+ pspec =
+ g_object_class_find_property (G_OBJECT_GET_CLASS (element),
+ property_name);
+ if (pspec == NULL) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the element did not have the property name specified",
+ property_name, element_name);
+ goto fail;
+ }
+
+ param_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ if (param_type != G_TYPE_FLOAT && param_type != G_TYPE_DOUBLE) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the property is not of float or double type",
+ property_name, element_name);
+ goto fail;
+ }
+
+ full_property_name = g_strdup_printf ("%s::%s",
+ g_type_name (gst_element_factory_get_element_type (element_factory)),
+ property_name);
+
+ if (g_list_find_custom (klass->rate_properties, full_property_name,
+ property_name_compare) == NULL) {
+ klass->rate_properties =
+ g_list_append (klass->rate_properties, full_property_name);
+ GST_DEBUG ("Added rate property %s", full_property_name);
+ } else {
+ g_free (full_property_name);
+ }
+
+ res = TRUE;
+
+fail:
+ if (element_factory != NULL)
+ gst_object_unref (element_factory);
+ if (element != NULL)
+ gst_object_unref (element);
+ if (pspec != NULL)
+ g_param_spec_unref (pspec);
+
+ return res;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Thibault Saunier <thibault.saunier@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_EFFECT
+#define _GES_EFFECT
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-base-effect.h>
+
+G_BEGIN_DECLS
+#define GES_TYPE_EFFECT ges_effect_get_type()
+
+#define GES_EFFECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_EFFECT, GESEffect))
+
+#define GES_EFFECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_EFFECT, GESEffectClass))
+
+#define GES_IS_EFFECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_EFFECT))
+
+#define GES_IS_EFFECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_EFFECT))
+
+#define GES_EFFECT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_EFFECT, GESEffectClass))
+
+
+typedef struct _GESEffectPrivate GESEffectPrivate;
+
+/**
+ * GESEffect:
+ *
+ */
+struct _GESEffect
+{
+ /*< private > */
+ GESBaseEffect parent;
+ GESEffectPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESEffectClass:
+ * @parent_class: parent class
+ */
+
+struct _GESEffectClass
+{
+ /*< private > */
+ GESBaseEffectClass parent_class;
+
+ GList *rate_properties;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+
+};
+
+GES_API
+GType ges_effect_get_type (void);
+
+GES_API GESEffect*
+ges_effect_new (const gchar * bin_description);
+
+GES_API gboolean
+ges_effect_class_register_rate_property (GESEffectClass *klass, const gchar *element_name, const gchar *property_name);
+
+G_END_DECLS
+#endif /* _GES_EFFECT */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:ges-enums
+ * @title: GES Enumerations
+ * @short_description: Various enums for the Gstreamer Editing Services
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-enums.h"
+#include "ges-internal.h"
+#include "ges-asset.h"
+#include "ges-meta-container.h"
+#include "ges-transition-clip.h"
+
+#define C_ENUM(v) ((guint) v)
+
+static const GFlagsValue track_types_values[] = {
+ {C_ENUM (GES_TRACK_TYPE_UNKNOWN), "GES_TRACK_TYPE_UNKNOWN", "unknown"},
+ {C_ENUM (GES_TRACK_TYPE_AUDIO), "GES_TRACK_TYPE_AUDIO", "audio"},
+ {C_ENUM (GES_TRACK_TYPE_VIDEO), "GES_TRACK_TYPE_VIDEO", "video"},
+ {C_ENUM (GES_TRACK_TYPE_TEXT), "GES_TRACK_TYPE_TEXT", "text"},
+ {C_ENUM (GES_TRACK_TYPE_CUSTOM), "GES_TRACK_TYPE_CUSTOM", "custom"},
+ {0, NULL, NULL}
+};
+
+static void
+register_ges_track_type_select_result (GType * id)
+{
+ *id = g_flags_register_static ("GESTrackType", track_types_values);
+}
+
+const gchar *
+ges_track_type_name (GESTrackType type)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (track_types_values); i++) {
+ if (type == track_types_values[i].value)
+ return track_types_values[i].value_nick;
+ }
+
+ return "Unknown (mixed?) ";
+}
+
+GType
+ges_track_type_get_type (void)
+{
+ static GType id;
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) register_ges_track_type_select_result, &id);
+ return id;
+}
+
+static void
+register_ges_pipeline_flags (GType * id)
+{
+ static const GFlagsValue values[] = {
+ {C_ENUM (GES_PIPELINE_MODE_PREVIEW_AUDIO),
+ "GES_PIPELINE_MODE_PREVIEW_AUDIO",
+ "audio_preview"},
+ {C_ENUM (GES_PIPELINE_MODE_PREVIEW_VIDEO),
+ "GES_PIPELINE_MODE_PREVIEW_VIDEO",
+ "video_preview"},
+ {C_ENUM (GES_PIPELINE_MODE_PREVIEW), "GES_PIPELINE_MODE_PREVIEW",
+ "full_preview"},
+ {C_ENUM (GES_PIPELINE_MODE_RENDER), "GES_PIPELINE_MODE_RENDER", "render"},
+ {C_ENUM (GES_PIPELINE_MODE_SMART_RENDER), "GES_PIPELINE_MODE_SMART_RENDER",
+ "smart_render"},
+ {0, NULL, NULL}
+ };
+
+ *id = g_flags_register_static ("GESPipelineFlags", values);
+}
+
+GType
+ges_pipeline_flags_get_type (void)
+{
+ static GType id;
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) register_ges_pipeline_flags, &id);
+ return id;
+}
+
+static void
+register_ges_edit_mode (GType * id)
+{
+ static const GEnumValue edit_mode[] = {
+ {C_ENUM (GES_EDIT_MODE_NORMAL), "GES_EDIT_MODE_NORMAL",
+ "edit_normal"},
+
+ {C_ENUM (GES_EDIT_MODE_RIPPLE), "GES_EDIT_MODE_RIPPLE",
+ "edit_ripple"},
+
+ {C_ENUM (GES_EDIT_MODE_ROLL), "GES_EDIT_MODE_ROLL",
+ "edit_roll"},
+
+ {C_ENUM (GES_EDIT_MODE_TRIM), "GES_EDIT_MODE_TRIM",
+ "edit_trim"},
+
+ {C_ENUM (GES_EDIT_MODE_SLIDE), "GES_EDIT_MODE_SLIDE",
+ "edit_slide"},
+
+ {0, NULL, NULL}
+ };
+
+ *id = g_enum_register_static ("GESEditMode", edit_mode);
+}
+
+GType
+ges_edit_mode_get_type (void)
+{
+ static GType id;
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) register_ges_edit_mode, &id);
+ return id;
+}
+
+static void
+register_ges_edge (GType * id)
+{
+ static const GEnumValue edges[] = {
+ {C_ENUM (GES_EDGE_START), "GES_EDGE_START", "edge_start"},
+ {C_ENUM (GES_EDGE_END), "GES_EDGE_END", "edge_end"},
+ {C_ENUM (GES_EDGE_NONE), "GES_EDGE_NONE", "edge_none"},
+ {0, NULL, NULL}
+ };
+
+ *id = g_enum_register_static ("GESEdge", edges);
+}
+
+const gchar *
+ges_edge_name (GESEdge edge)
+{
+ switch (edge) {
+ case GES_EDGE_START:
+ return "start";
+ case GES_EDGE_END:
+ return "end";
+ default:
+ return "none";
+ }
+}
+
+GType
+ges_edge_get_type (void)
+{
+ static GType id;
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) register_ges_edge, &id);
+ return id;
+}
+
+static GEnumValue transition_types[] = {
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE",
+ "none"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR",
+ "bar-wipe-lr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_TB,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_TB",
+ "bar-wipe-tb"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TL",
+ "box-wipe-tl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TR",
+ "box-wipe-tr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BR",
+ "box-wipe-br"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BL",
+ "box-wipe-bl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CI,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CI",
+ "four-box-wipe-ci"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CO,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CO",
+ "four-box-wipe-co"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_V,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_V",
+ "barndoor-v"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_H,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_H",
+ "barndoor-h"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TC,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TC",
+ "box-wipe-tc"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_RC,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_RC",
+ "box-wipe-rc"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BC,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BC",
+ "box-wipe-bc"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_LC,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_LC",
+ "box-wipe-lc"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TL",
+ "diagonal-tl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TR",
+ "diagonal-tr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_V,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_V",
+ "bowtie-v"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_H,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_H",
+ "bowtie-h"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DBL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DBL",
+ "barndoor-dbl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DTL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DTL",
+ "barndoor-dtl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DBD,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DBD",
+ "misc-diagonal-dbd"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DD,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DD",
+ "misc-diagonal-dd"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_D,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_D",
+ "vee-d"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_L,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_L",
+ "vee-l"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_U,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_U",
+ "vee-u"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_R,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_R",
+ "vee-r"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_D,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_D",
+ "barnvee-d"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_L,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_L",
+ "barnvee-l"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_U,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_U",
+ "barnvee-u"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_R,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_R",
+ "barnvee-r"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_IRIS_RECT,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_IRIS_RECT",
+ "iris-rect"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW12,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW12",
+ "clock-cw12"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW3,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW3",
+ "clock-cw3"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW6,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW6",
+ "clock-cw6"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW9,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW9",
+ "clock-cw9"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBV,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBV",
+ "pinwheel-tbv"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBH,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBH",
+ "pinwheel-tbh"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_FB,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_FB",
+ "pinwheel-fb"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CT,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CT",
+ "fan-ct"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CR",
+ "fan-cr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOV,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOV",
+ "doublefan-fov"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOH,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOH",
+ "doublefan-foh"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWT,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWT",
+ "singlesweep-cwt"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWR",
+ "singlesweep-cwr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWB,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWB",
+ "singlesweep-cwb"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWL",
+ "singlesweep-cwl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PV,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PV",
+ "doublesweep-pv"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PD,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PD",
+ "doublesweep-pd"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OV,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OV",
+ "doublesweep-ov"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OH,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OH",
+ "doublesweep-oh"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_T,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_T",
+ "fan-t"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_R,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_R",
+ "fan-r"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_B,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_B",
+ "fan-b"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_L,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_L",
+ "fan-l"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIV,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIV",
+ "doublefan-fiv"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIH,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIH",
+ "doublefan-fih"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTL",
+ "singlesweep-cwtl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBL",
+ "singlesweep-cwbl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBR",
+ "singlesweep-cwbr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTR,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTR",
+ "singlesweep-cwtr"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDTL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDTL",
+ "doublesweep-pdtl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDBL,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDBL",
+ "doublesweep-pdbl"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_T,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_T",
+ "saloondoor-t"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_L,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_L",
+ "saloondoor-l"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_B,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_B",
+ "saloondoor-b"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_R,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_R",
+ "saloondoor-r"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_R,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_R",
+ "windshield-r"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_U,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_U",
+ "windshield-u"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_V,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_V",
+ "windshield-v"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_H,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_H",
+ "windshield-h"},
+ {GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE,
+ "GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE",
+ "crossfade"},
+ {0, NULL, NULL}
+};
+
+void
+_init_standard_transition_assets (void)
+{
+ guint i;
+
+ for (i = 1; i < G_N_ELEMENTS (transition_types) - 1; i++) {
+ GESAsset *asset = ges_asset_request (GES_TYPE_TRANSITION_CLIP,
+ transition_types[i].value_nick, NULL);
+
+ ges_meta_container_register_meta_string (GES_META_CONTAINER (asset),
+ GES_META_READABLE, GES_META_DESCRIPTION,
+ transition_types[i].value_name);
+
+ gst_object_unref (asset);
+ }
+
+}
+
+GType
+ges_video_standard_transition_type_get_type (void)
+{
+ static GType the_type = 0;
+ static gsize once = 0;
+
+ if (g_once_init_enter (&once)) {
+ g_assert (!once);
+
+ the_type = g_enum_register_static ("GESVideoStandardTransitionType",
+ transition_types);
+ g_once_init_leave (&once, 1);
+ }
+
+ return the_type;
+}
+
+GType
+ges_text_valign_get_type (void)
+{
+ static GType text_overlay_valign_type = 0;
+ static gsize initialized = 0;
+ static const GEnumValue text_overlay_valign[] = {
+ {GES_TEXT_VALIGN_BASELINE, "GES_TEXT_VALIGN_BASELINE", "baseline"},
+ {GES_TEXT_VALIGN_BOTTOM, "GES_TEXT_VALIGN_BOTTOM", "bottom"},
+ {GES_TEXT_VALIGN_TOP, "GES_TEXT_VALIGN_TOP", "top"},
+ {GES_TEXT_VALIGN_POSITION, "GES_TEXT_VALIGN_POSITION", "position"},
+ {GES_TEXT_VALIGN_CENTER, "GES_TEXT_VALIGN_CENTER", "center"},
+ {GES_TEXT_VALIGN_ABSOLUTE, "GES_TEXT_VALIGN_ABSOLUTE", "absolute"},
+ {0, NULL, NULL},
+ };
+
+ if (g_once_init_enter (&initialized)) {
+ text_overlay_valign_type =
+ g_enum_register_static ("GESTextVAlign", text_overlay_valign);
+ g_once_init_leave (&initialized, 1);
+ }
+ return text_overlay_valign_type;
+}
+
+GType
+ges_text_halign_get_type (void)
+{
+ static GType text_overlay_halign_type = 0;
+ static gsize initialized = 0;
+ static const GEnumValue text_overlay_halign[] = {
+ {GES_TEXT_HALIGN_LEFT, "GES_TEXT_HALIGN_LEFT", "left"},
+ {GES_TEXT_HALIGN_CENTER, "GES_TEXT_HALIGN_CENTER", "center"},
+ {GES_TEXT_HALIGN_RIGHT, "GES_TEXT_HALIGN_RIGHT", "right"},
+ {GES_TEXT_HALIGN_POSITION, "GES_TEXT_HALIGN_POSITION", "position"},
+ {GES_TEXT_HALIGN_ABSOLUTE, "GES_TEXT_HALIGN_ABSOLUTE", "absolute"},
+ {0, NULL, NULL},
+ };
+
+ if (g_once_init_enter (&initialized)) {
+ text_overlay_halign_type =
+ g_enum_register_static ("GESTextHAlign", text_overlay_halign);
+ g_once_init_leave (&initialized, 1);
+ }
+ return text_overlay_halign_type;
+}
+
+/* table more-or-less copied from gstvideotestsrc.c */
+static GEnumValue vpattern_enum_values[] = {
+ {GES_VIDEO_TEST_PATTERN_SMPTE, "GES_VIDEO_TEST_PATTERN_SMPTE", "smpte"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_SNOW, "GES_VIDEO_TEST_PATTERN_SNOW", "snow"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_BLACK, "GES_VIDEO_TEST_PATTERN_BLACK", "black"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_WHITE, "GES_VIDEO_TEST_PATTERN_WHITE", "white"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_RED, "GES_VIDEO_TEST_PATTERN_RED", "red"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_GREEN, "GES_VIDEO_TEST_PATTERN_GREEN", "green"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_BLUE, "GES_VIDEO_TEST_PATTERN_BLUE", "blue"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_CHECKERS1,
+ "GES_VIDEO_TEST_PATTERN_CHECKERS1", "checkers-1"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_CHECKERS2,
+ "GES_VIDEO_TEST_PATTERN_CHECKERS2", "checkers-2"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_CHECKERS4,
+ "GES_VIDEO_TEST_PATTERN_CHECKERS4", "checkers-4"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_CHECKERS8,
+ "GES_VIDEO_TEST_PATTERN_CHECKERS8", "checkers-8"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_CIRCULAR,
+ "GES_VIDEO_TEST_PATTERN_CIRCULAR", "circular"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_BLINK, "GES_VIDEO_TEST_PATTERN_BLINK", "blink"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_SMPTE75, "GES_VIDEO_TEST_PATTERN_SMPTE75", "smpte75"}
+ ,
+ {GES_VIDEO_TEST_ZONE_PLATE, "GES_VIDEO_TEST_ZONE_PLATE", "zone-plate"}
+ ,
+ {GES_VIDEO_TEST_GAMUT, "GES_VIDEO_TEST_GAMUT", "gamut"}
+ ,
+ {GES_VIDEO_TEST_CHROMA_ZONE_PLATE, "GES_VIDEO_TEST_CHROMA_ZONE_PLATE",
+ "chroma-zone-plate"}
+ ,
+ {GES_VIDEO_TEST_PATTERN_SOLID, "GES_VIDEO_TEST_PATTERN_SOLID", "solid-color"}
+ ,
+ {0, NULL, NULL}
+};
+
+GType
+ges_video_test_pattern_get_type (void)
+{
+
+ static gsize once = 0;
+ static GType theType = 0;
+
+ if (g_once_init_enter (&once)) {
+ theType = g_enum_register_static ("GESVideoTestPattern",
+ vpattern_enum_values);
+ g_once_init_leave (&once, 1);
+ };
+
+ return theType;
+}
+
+static void
+register_ges_meta_flag (GType * id)
+{
+ static const GFlagsValue values[] = {
+ {C_ENUM (GES_META_READABLE), "GES_META_READABLE", "readable"},
+ {C_ENUM (GES_META_WRITABLE), "GES_META_WRITABLE", "writable"},
+ {C_ENUM (GES_META_READ_WRITE), "GES_META_READ_WRITE", "readwrite"},
+ {0, NULL, NULL}
+ };
+
+ *id = g_flags_register_static ("GESMetaFlag", values);
+}
+
+GType
+ges_meta_flag_get_type (void)
+{
+ static GType id;
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) register_ges_meta_flag, &id);
+ return id;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 __GES_ENUMS_H__
+#define __GES_ENUMS_H__
+
+#include <gst/gst.h>
+#include <ges/ges-prelude.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TRACK_TYPE (ges_track_type_get_type ())
+GES_API
+GType ges_track_type_get_type (void);
+
+/**
+ * GESTrackType:
+ * @GES_TRACK_TYPE_UNKNOWN: A track of unknown type (i.e. invalid)
+ * @GES_TRACK_TYPE_AUDIO: An audio track
+ * @GES_TRACK_TYPE_VIDEO: A video track
+ * @GES_TRACK_TYPE_TEXT: A text (subtitle) track
+ * @GES_TRACK_TYPE_CUSTOM: A custom-content track
+ *
+ * Types of content handled by a track. If the content is not one of
+ * @GES_TRACK_TYPE_AUDIO, @GES_TRACK_TYPE_VIDEO or @GES_TRACK_TYPE_TEXT,
+ * the user of the #GESTrack must set the type to @GES_TRACK_TYPE_CUSTOM.
+ *
+ * @GES_TRACK_TYPE_UNKNOWN is for internal purposes and should not be used
+ * by users
+ */
+
+typedef enum {
+ GES_TRACK_TYPE_UNKNOWN = 1 << 0,
+ GES_TRACK_TYPE_AUDIO = 1 << 1,
+ GES_TRACK_TYPE_VIDEO = 1 << 2,
+ GES_TRACK_TYPE_TEXT = 1 << 3,
+ GES_TRACK_TYPE_CUSTOM = 1 << 4,
+} GESTrackType;
+
+#define GES_META_FLAG_TYPE (ges_meta_flag_get_type ())
+GES_API
+GType ges_meta_flag_get_type (void);
+
+/**
+ * GESMetaFlag:
+ * @GES_META_READABLE: The metadata is readable
+ * @GES_META_WRITABLE: The metadata is writable
+ * @GES_META_READ_WRITE: The metadata is readable and writable
+ */
+typedef enum {
+ GES_META_READABLE = 1 << 0,
+ GES_META_WRITABLE = 1 << 1,
+ GES_META_READ_WRITE = GES_META_READABLE | GES_META_WRITABLE
+} GESMetaFlag;
+
+/**
+ * GESVideoStandardTransitionType:
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE: Transition type has not been set,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR: A bar moves from left to right,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_TB: A bar moves from top to bottom,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TL: A box expands from the upper-left corner to the lower-right corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TR: A box expands from the upper-right corner to the lower-left corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BR: A box expands from the lower-right corner to the upper-left corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BL: A box expands from the lower-left corner to the upper-right corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CI: A box shape expands from each of the four corners toward the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CO: A box shape expands from the center of each quadrant toward the corners of each quadrant,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_V: A central, vertical line splits and expands toward the left and right edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_H: A central, horizontal line splits and expands toward the top and bottom edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TC: A box expands from the top edge's midpoint to the bottom corners,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_RC: A box expands from the right edge's midpoint to the left corners,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BC: A box expands from the bottom edge's midpoint to the top corners,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_LC: A box expands from the left edge's midpoint to the right corners,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TL: A diagonal line moves from the upper-left corner to the lower-right corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TR: A diagonal line moves from the upper right corner to the lower-left corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_V: Two wedge shapes slide in from the top and bottom edges toward the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_H: Two wedge shapes slide in from the left and right edges toward the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DBL: A diagonal line from the lower-left to upper-right corners splits and expands toward the opposite corners,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DTL: A diagonal line from upper-left to lower-right corners splits and expands toward the opposite corners,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DBD: Four wedge shapes split from the center and retract toward the four edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DD: A diamond connecting the four edge midpoints simultaneously contracts toward the center and expands toward the edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_D: A wedge shape moves from top to bottom,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_L: A wedge shape moves from right to left,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_U: A wedge shape moves from bottom to top,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_R: A wedge shape moves from left to right,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_D: A 'V' shape extending from the bottom edge's midpoint to the opposite corners contracts toward the center and expands toward the edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_L: A 'V' shape extending from the left edge's midpoint to the opposite corners contracts toward the center and expands toward the edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_U: A 'V' shape extending from the top edge's midpoint to the opposite corners contracts toward the center and expands toward the edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_R: A 'V' shape extending from the right edge's midpoint to the opposite corners contracts toward the center and expands toward the edges,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_IRIS_RECT: A rectangle expands from the center.,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW12: A radial hand sweeps clockwise from the twelve o'clock position,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW3: A radial hand sweeps clockwise from the three o'clock position,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW6: A radial hand sweeps clockwise from the six o'clock position,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW9: A radial hand sweeps clockwise from the nine o'clock position,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBV: Two radial hands sweep clockwise from the twelve and six o'clock positions,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBH: Two radial hands sweep clockwise from the nine and three o'clock positions,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_FB: Four radial hands sweep clockwise,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CT: A fan unfolds from the top edge, the fan axis at the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CR: A fan unfolds from the right edge, the fan axis at the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOV: Two fans, their axes at the center, unfold from the top and bottom,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOH: Two fans, their axes at the center, unfold from the left and right,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWT: A radial hand sweeps clockwise from the top edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWR: A radial hand sweeps clockwise from the right edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWB: A radial hand sweeps clockwise from the bottom edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWL: A radial hand sweeps clockwise from the left edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PV: Two radial hands sweep clockwise and counter-clockwise from the top and bottom edges' midpoints,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PD: Two radial hands sweep clockwise and counter-clockwise from the left and right edges' midpoints,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OV: Two radial hands attached at the top and bottom edges' midpoints sweep from right to left,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OH: Two radial hands attached at the left and right edges' midpoints sweep from top to bottom,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_T: A fan unfolds from the bottom, the fan axis at the top edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_R: A fan unfolds from the left, the fan axis at the right edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_B: A fan unfolds from the top, the fan axis at the bottom edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_L: A fan unfolds from the right, the fan axis at the left edge's midpoint,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIV: Two fans, their axes at the top and bottom, unfold from the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIH: Two fans, their axes at the left and right, unfold from the center,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTL: A radial hand sweeps clockwise from the upper-left corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBL: A radial hand sweeps counter-clockwise from the lower-left corner.,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBR: A radial hand sweeps clockwise from the lower-right corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTR: A radial hand sweeps counter-clockwise from the upper-right corner,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDTL: Two radial hands attached at the upper-left and lower-right corners sweep down and up,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDBL: Two radial hands attached at the lower-left and upper-right corners sweep down and up,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_T: Two radial hands attached at the upper-left and upper-right corners sweep down,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_L: Two radial hands attached at the upper-left and lower-left corners sweep to the right,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_B: Two radial hands attached at the lower-left and lower-right corners sweep up,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_R: Two radial hands attached at the upper-right and lower-right corners sweep to the left,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_R: Two radial hands attached at the midpoints of the top and bottom halves sweep from right to left,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_U: Two radial hands attached at the midpoints of the left and right halves sweep from top to bottom,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_V: Two sets of radial hands attached at the midpoints of the top and bottom halves sweep from top to bottom and bottom to top,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_H: Two sets of radial hands attached at the midpoints of the left and right halves sweep from left to right and right to left,
+ * @GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE: Crossfade
+ *
+ */
+
+typedef enum {
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE = 0,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR = 1,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_TB = 2,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TL = 3,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TR = 4,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BR = 5,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BL = 6,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CI = 7,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FOUR_BOX_WIPE_CO = 8,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_V = 21,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_H = 22,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_TC = 23,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_RC = 24,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_BC = 25,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOX_WIPE_LC = 26,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TL = 41,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DIAGONAL_TR = 42,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_V = 43,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BOWTIE_H = 44,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DBL = 45,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNDOOR_DTL = 46,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DBD = 47,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_MISC_DIAGONAL_DD = 48,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_D = 61,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_L = 62,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_U = 63,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_VEE_R = 64,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_D = 65,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_L = 66,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_U = 67,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_BARNVEE_R = 68,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_IRIS_RECT = 101,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW12 = 201,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW3 = 202,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW6 = 203,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CLOCK_CW9 = 204,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBV = 205,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_TBH = 206,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_PINWHEEL_FB = 207,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CT = 211,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_CR = 212,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOV = 213,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FOH = 214,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWT = 221,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWR = 222,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWB = 223,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWL = 224,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PV = 225,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PD = 226,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OV = 227,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_OH = 228,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_T = 231,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_R = 232,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_B = 233,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_FAN_L = 234,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIV = 235,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLEFAN_FIH = 236,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTL = 241,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBL = 242,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWBR = 243,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SINGLESWEEP_CWTR = 244,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDTL = 245,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_DOUBLESWEEP_PDBL = 246,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_T = 251,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_L = 252,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_B = 253,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_SALOONDOOR_R = 254,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_R = 261,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_U = 262,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_V = 263,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_WINDSHIELD_H = 264,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE = 512
+} GESVideoStandardTransitionType;
+
+#define GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE \
+ (ges_video_standard_transition_type_get_type())
+
+GES_API
+GType ges_video_standard_transition_type_get_type (void);
+
+/**
+ * GESTextVAlign:
+ * @GES_TEXT_VALIGN_BASELINE: draw text on the baseline
+ * @GES_TEXT_VALIGN_BOTTOM: draw text on the bottom
+ * @GES_TEXT_VALIGN_TOP: draw text on top
+ * @GES_TEXT_VALIGN_POSITION: draw text on ypos position
+ * @GES_TEXT_VALIGN_CENTER: draw text on the center
+ *
+ * Vertical alignment of the text.
+ */
+typedef enum {
+ GES_TEXT_VALIGN_BASELINE,
+ GES_TEXT_VALIGN_BOTTOM,
+ GES_TEXT_VALIGN_TOP,
+ GES_TEXT_VALIGN_POSITION,
+ GES_TEXT_VALIGN_CENTER,
+ GES_TEXT_VALIGN_ABSOLUTE
+} GESTextVAlign;
+
+#define DEFAULT_VALIGNMENT GES_TEXT_VALIGN_BASELINE
+
+#define GES_TEXT_VALIGN_TYPE\
+ (ges_text_valign_get_type ())
+
+GES_API
+GType ges_text_valign_get_type (void);
+
+/**
+ * GESTextHAlign:
+ * @GES_TEXT_HALIGN_LEFT: align text left
+ * @GES_TEXT_HALIGN_CENTER: align text center
+ * @GES_TEXT_HALIGN_RIGHT: align text right
+ * @GES_TEXT_HALIGN_POSITION: align text on xpos position
+ *
+ * Horizontal alignment of the text.
+ */
+typedef enum {
+ GES_TEXT_HALIGN_LEFT = 0,
+ GES_TEXT_HALIGN_CENTER = 1,
+ GES_TEXT_HALIGN_RIGHT = 2,
+ GES_TEXT_HALIGN_POSITION = 4,
+ GES_TEXT_HALIGN_ABSOLUTE = 5
+} GESTextHAlign;
+
+#define DEFAULT_HALIGNMENT GES_TEXT_HALIGN_CENTER
+
+#define GES_TEXT_HALIGN_TYPE\
+ (ges_text_halign_get_type ())
+
+GES_API
+GType ges_text_halign_get_type (void);
+
+/**
+ * GESVideoTestPattern:
+ * @GES_VIDEO_TEST_PATTERN_SMPTE: A standard SMPTE test pattern
+ * @GES_VIDEO_TEST_PATTERN_SNOW: Random noise
+ * @GES_VIDEO_TEST_PATTERN_BLACK: A black image
+ * @GES_VIDEO_TEST_PATTERN_WHITE: A white image
+ * @GES_VIDEO_TEST_PATTERN_RED: A red image
+ * @GES_VIDEO_TEST_PATTERN_GREEN: A green image
+ * @GES_VIDEO_TEST_PATTERN_BLUE: A blue image
+ * @GES_VIDEO_TEST_PATTERN_CHECKERS1: Checkers pattern (1px)
+ * @GES_VIDEO_TEST_PATTERN_CHECKERS2: Checkers pattern (2px)
+ * @GES_VIDEO_TEST_PATTERN_CHECKERS4: Checkers pattern (4px)
+ * @GES_VIDEO_TEST_PATTERN_CHECKERS8: Checkers pattern (8px)
+ * @GES_VIDEO_TEST_PATTERN_CIRCULAR: Circular pattern
+ * @GES_VIDEO_TEST_PATTERN_SOLID: Solid color
+ * @GES_VIDEO_TEST_PATTERN_BLINK: Alternate between black and white
+ * @GES_VIDEO_TEST_ZONE_PLATE: Zone plate
+ * @GES_VIDEO_TEST_GAMUT: Gamut checkers
+ * @GES_VIDEO_TEST_CHROMA_ZONE_PLATE: Chroma zone plate
+ * @GES_VIDEO_TEST_PATTERN_SMPTE75: SMPTE test pattern (75% color bars)
+ *
+ * The test pattern to produce
+ */
+
+typedef enum {
+ GES_VIDEO_TEST_PATTERN_SMPTE,
+ GES_VIDEO_TEST_PATTERN_SNOW,
+ GES_VIDEO_TEST_PATTERN_BLACK,
+ GES_VIDEO_TEST_PATTERN_WHITE,
+ GES_VIDEO_TEST_PATTERN_RED,
+ GES_VIDEO_TEST_PATTERN_GREEN,
+ GES_VIDEO_TEST_PATTERN_BLUE,
+ GES_VIDEO_TEST_PATTERN_CHECKERS1,
+ GES_VIDEO_TEST_PATTERN_CHECKERS2,
+ GES_VIDEO_TEST_PATTERN_CHECKERS4,
+ GES_VIDEO_TEST_PATTERN_CHECKERS8,
+ GES_VIDEO_TEST_PATTERN_CIRCULAR,
+ GES_VIDEO_TEST_PATTERN_BLINK,
+ GES_VIDEO_TEST_PATTERN_SMPTE75,
+ GES_VIDEO_TEST_ZONE_PLATE,
+ GES_VIDEO_TEST_GAMUT,
+ GES_VIDEO_TEST_CHROMA_ZONE_PLATE,
+ GES_VIDEO_TEST_PATTERN_SOLID,
+} GESVideoTestPattern;
+
+
+#define GES_VIDEO_TEST_PATTERN_TYPE\
+ ges_video_test_pattern_get_type()
+
+GES_API
+GType ges_video_test_pattern_get_type (void);
+
+/**
+ * GESPipelineFlags:
+ * @GES_PIPELINE_MODE_PREVIEW_AUDIO: output audio to the soundcard
+ * @GES_PIPELINE_MODE_PREVIEW_VIDEO: output video to the screen
+ * @GES_PIPELINE_MODE_PREVIEW: output audio/video to soundcard/screen (default)
+ * @GES_PIPELINE_MODE_RENDER: render timeline (forces decoding)
+ * @GES_PIPELINE_MODE_SMART_RENDER: render timeline (tries to avoid decoding/reencoding)
+ *
+ * The various modes the #GESPipeline can be configured to.
+ */
+typedef enum {
+ GES_PIPELINE_MODE_PREVIEW_AUDIO = 1 << 0,
+ GES_PIPELINE_MODE_PREVIEW_VIDEO = 1 << 1,
+ GES_PIPELINE_MODE_PREVIEW = GES_PIPELINE_MODE_PREVIEW_AUDIO | GES_PIPELINE_MODE_PREVIEW_VIDEO,
+ GES_PIPELINE_MODE_RENDER = 1 << 2,
+ GES_PIPELINE_MODE_SMART_RENDER = 1 << 3
+} GESPipelineFlags;
+
+#define GES_TYPE_PIPELINE_FLAGS\
+ ges_pipeline_flags_get_type()
+
+GES_API
+GType ges_pipeline_flags_get_type (void);
+
+/**
+ * GESEditMode:
+ * @GES_EDIT_MODE_NORMAL: The object is edited the normal way (default).
+ * @GES_EDIT_MODE_RIPPLE: The objects are edited in ripple mode.
+ * The Ripple mode allows you to modify the beginning/end of a clip
+ * and move the neighbours accordingly. This will change the overall
+ * timeline duration. In the case of ripple end, the duration of the
+ * clip being rippled can't be superior to its max_duration - inpoint
+ * otherwise the action won't be executed.
+ * @GES_EDIT_MODE_ROLL: The object is edited in roll mode.
+ * The Roll mode allows you to modify the position of an editing point
+ * between two clips without modifying the inpoint of the first clip
+ * nor the out-point of the second clip. This will not change the
+ * overall timeline duration.
+ * @GES_EDIT_MODE_TRIM: The object is edited in trim mode.
+ * The Trim mode allows you to modify the in-point/duration of a clip
+ * without modifying its position in the timeline.
+ * @GES_EDIT_MODE_SLIDE: The object is edited in slide mode.
+ * The Slide mode allows you to modify the position of a clip in a
+ * timeline without modifying its duration or its in-point, but will
+ * modify the duration of the previous clip and in-point of the
+ * following clip so does not modify the overall timeline duration.
+ * (not implemented yet)
+ *
+ * You can also find more explanation about the behaviour of those modes at:
+ * <ulink url="http://pitivi.org/manual/trimming.html"> trim, ripple and roll</ulink>
+ * and <ulink url="http://pitivi.org/manual/usingclips.html">clip management</ulink>.
+ */
+typedef enum {
+ GES_EDIT_MODE_NORMAL,
+ GES_EDIT_MODE_RIPPLE,
+ GES_EDIT_MODE_ROLL,
+ GES_EDIT_MODE_TRIM,
+ GES_EDIT_MODE_SLIDE
+} GESEditMode;
+
+#define GES_TYPE_EDIT_MODE ges_edit_mode_get_type()
+
+GES_API
+GType ges_edit_mode_get_type (void);
+
+/**
+ * GESEdge:
+ * @GES_EDGE_START: Represents the start of an object.
+ * @GES_EDGE_END: Represents the end of an object.
+ * @GES_EDGE_NONE: Represent the fact we are not workin with any edge of an
+ * object.
+ *
+ * The edges of an object contain in a #GESTimeline or #GESTrack
+ */
+typedef enum {
+ GES_EDGE_START,
+ GES_EDGE_END,
+ GES_EDGE_NONE
+} GESEdge;
+
+GES_API
+const gchar * ges_edge_name (GESEdge edge);
+
+#define GES_TYPE_EDGE ges_edge_get_type()
+
+GES_API
+GType ges_edge_get_type (void);
+
+
+GES_API
+const gchar * ges_track_type_name (GESTrackType type);
+G_END_DECLS
+
+#endif /* __GES_ENUMS_H__ */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION: gesextractable
+ * @title: GESExtractable Interface
+ * @short_description: An interface for objects which can be extracted from a GESAsset
+ *
+ * FIXME: Long description needed
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-asset.h"
+#include "ges-internal.h"
+#include "ges-extractable.h"
+#include "ges-uri-clip.h"
+
+static GQuark ges_asset_key;
+
+G_DEFINE_INTERFACE_WITH_CODE (GESExtractable, ges_extractable,
+ G_TYPE_INITIALLY_UNOWNED,
+ ges_asset_key = g_quark_from_static_string ("ges-extractable-data"));
+
+static gchar *
+ges_extractable_check_id_default (GType type, const gchar * id, GError ** error)
+{
+ return g_strdup (g_type_name (type));
+}
+
+static GType
+ges_extractable_get_real_extractable_type_default (GType type, const gchar * id)
+{
+ return type;
+}
+
+static GParameter *
+extractable_get_parameters_from_id (const gchar * id, guint * n_params)
+{
+ *n_params = 0;
+
+ return NULL;
+}
+
+static gchar *
+extractable_get_id (GESExtractable * self)
+{
+ return g_strdup (g_type_name (G_OBJECT_TYPE (self)));
+
+}
+
+static void
+ges_extractable_default_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_ASSET;
+ iface->check_id = ges_extractable_check_id_default;
+ iface->get_real_extractable_type =
+ ges_extractable_get_real_extractable_type_default;
+ iface->get_parameters_from_id = extractable_get_parameters_from_id;
+ iface->set_asset = NULL;
+ iface->set_asset_full = NULL;
+ iface->get_id = extractable_get_id;
+ iface->register_metas = NULL;
+ iface->can_update_asset = FALSE;
+}
+
+/**
+ * ges_extractable_get_asset:
+ * @self: The #GESExtractable from which to retrieve a #GESAsset
+ *
+ * Method for getting an asset from a #GESExtractable
+ *
+ * Returns: (transfer none) (nullable): The #GESAsset or %NULL if none has
+ * been set
+ */
+GESAsset *
+ges_extractable_get_asset (GESExtractable * self)
+{
+ g_return_val_if_fail (GES_IS_EXTRACTABLE (self), NULL);
+
+ return g_object_get_qdata (G_OBJECT (self), ges_asset_key);;
+}
+
+/**
+ * ges_extractable_set_asset:
+ * @self: Target object
+ * @asset: (transfer none): The #GESAsset to set
+ *
+ * Method to set the asset which instantiated the specified object
+ *
+ * Return: %TRUE if @asset could be set %FALSE otherwize
+ */
+gboolean
+ges_extractable_set_asset (GESExtractable * self, GESAsset * asset)
+{
+ GESExtractableInterface *iface;
+
+ g_return_val_if_fail (GES_IS_EXTRACTABLE (self), FALSE);
+
+ iface = GES_EXTRACTABLE_GET_INTERFACE (self);
+ GST_DEBUG_OBJECT (self, "Setting asset to %" GST_PTR_FORMAT, asset);
+
+ if (iface->can_update_asset == FALSE &&
+ g_object_get_qdata (G_OBJECT (self), ges_asset_key)) {
+ GST_WARNING_OBJECT (self, "Can not reset asset on object");
+
+ return FALSE;
+ }
+
+ g_object_set_qdata_full (G_OBJECT (self), ges_asset_key,
+ gst_object_ref (asset), gst_object_unref);
+
+ /* Let classes that implement the interface know that a asset has been set */
+ if (iface->set_asset_full)
+ return iface->set_asset_full (self, asset);
+
+ if (iface->set_asset)
+ iface->set_asset (self, asset);
+
+ return TRUE;
+}
+
+/**
+ * ges_extractable_get_id:
+ * @self: The #GESExtractable
+ *
+ * Returns: (transfer full): The #id of the associated #GESAsset, free with #g_free
+ */
+gchar *
+ges_extractable_get_id (GESExtractable * self)
+{
+ g_return_val_if_fail (GES_IS_EXTRACTABLE (self), NULL);
+
+ return GES_EXTRACTABLE_GET_INTERFACE (self)->get_id (self);
+}
+
+/**
+ * ges_extractable_type_get_parameters_for_id:
+ * @type: The #GType implementing #GESExtractable
+ * @id: The ID of the Extractable
+ * @n_params: (out): Return location for the returned array
+ *
+ * Returns: (transfer full) (array length=n_params): an array of #GParameter
+ * needed to extract the #GESExtractable from a #GESAsset of @id
+ */
+GParameter *
+ges_extractable_type_get_parameters_from_id (GType type, const gchar * id,
+ guint * n_params)
+{
+ GObjectClass *klass;
+ GESExtractableInterface *iface;
+
+ GParameter *ret = NULL;
+
+ g_return_val_if_fail (g_type_is_a (type, G_TYPE_OBJECT), NULL);
+ g_return_val_if_fail (g_type_is_a (type, GES_TYPE_EXTRACTABLE), NULL);
+
+ klass = g_type_class_ref (type);
+ iface = g_type_interface_peek (klass, GES_TYPE_EXTRACTABLE);
+
+ ret = iface->get_parameters_from_id (id, n_params);
+
+ g_type_class_unref (klass);
+
+ return ret;
+}
+
+/**
+ * ges_extractable_type_get_asset_type:
+ * @type: The #GType implementing #GESExtractable
+ *
+ * Get the #GType, subclass of #GES_TYPE_ASSET to instanciate
+ * to be able to extract a @type
+ *
+ * Returns: the #GType to use to create a asset to extract @type
+ */
+GType
+ges_extractable_type_get_asset_type (GType type)
+{
+ GObjectClass *klass;
+ GESExtractableInterface *iface;
+
+ g_return_val_if_fail (g_type_is_a (type, G_TYPE_OBJECT), G_TYPE_INVALID);
+ g_return_val_if_fail (g_type_is_a (type, GES_TYPE_EXTRACTABLE),
+ G_TYPE_INVALID);
+
+ klass = g_type_class_ref (type);
+
+ iface = g_type_interface_peek (klass, GES_TYPE_EXTRACTABLE);
+
+ g_type_class_unref (klass);
+
+ return iface->asset_type;
+}
+
+/**
+ * ges_extractable_type_check_id:
+ * @type: The #GType implementing #GESExtractable
+ * @id: The ID to check
+ *
+ * Check if @id is valid for @type
+ *
+ * Returns: (transfer full) (nullable): A newly allocated string containing
+ * the actual ID (after some processing) or %NULL if the ID is wrong.
+ */
+gchar *
+ges_extractable_type_check_id (GType type, const gchar * id, GError ** error)
+{
+ GObjectClass *klass;
+ GESExtractableInterface *iface;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ g_return_val_if_fail (g_type_is_a (type, G_TYPE_OBJECT), NULL);
+ g_return_val_if_fail (g_type_is_a (type, GES_TYPE_EXTRACTABLE), NULL);
+
+ klass = g_type_class_ref (type);
+
+ iface = g_type_interface_peek (klass, GES_TYPE_EXTRACTABLE);
+
+ g_type_class_unref (klass);
+
+ return iface->check_id (type, id, error);
+}
+
+/**
+ * ges_extractable_get_real_extractable_type:
+ * @type: The #GType implementing #GESExtractable
+ * @id: The ID to check
+ *
+ * Get the #GType that should be used as extractable_type for @type and
+ * @id. Usually this will be the same as @type but in some cases they can
+ * be some subclasses of @type. For example, in the case of #GESFormatter,
+ * the returned #GType will be a subclass of #GESFormatter that can be used
+ * to load the file pointed by @id.
+ *
+ * Returns: Return the #GESExtractable type that should be used for @id
+ */
+GType
+ges_extractable_get_real_extractable_type_for_id (GType type, const gchar * id)
+{
+ GType ret;
+ GObjectClass *klass;
+ GESExtractableInterface *iface;
+
+ klass = g_type_class_ref (type);
+ iface = g_type_interface_peek (klass, GES_TYPE_EXTRACTABLE);
+ g_type_class_unref (klass);
+
+ ret = iface->get_real_extractable_type (type, id);
+
+ GST_DEBUG ("Extractable type for id %s and wanted type %s is: %s",
+ id, g_type_name (type), g_type_name (ret));
+
+ return ret;
+}
+
+/**
+ * ges_extractable_register_metas:
+ * @self: A #GESExtractable
+ * @asset: The #GESAsset on which metadatas should be registered
+ *
+ * Lets you register standard method for @extractable_type on @asset
+ *
+ * Returns: %TRUE if metas could be register %FALSE otherwize
+ */
+gboolean
+ges_extractable_register_metas (GType extractable_type, GESAsset * asset)
+{
+ GObjectClass *klass;
+ gboolean ret = FALSE;
+ GESExtractableInterface *iface;
+
+ g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
+ FALSE);
+
+ klass = g_type_class_ref (extractable_type);
+ iface = g_type_interface_peek (klass, GES_TYPE_EXTRACTABLE);
+
+ if (iface->register_metas)
+ ret = iface->register_metas (iface, klass, asset);
+
+ g_type_class_unref (klass);
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef _GES_EXTRACTABLE_
+#define _GES_EXTRACTABLE_
+
+typedef struct _GESExtractable GESExtractable;
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-asset.h>
+
+G_BEGIN_DECLS
+
+/* GESExtractable interface declarations */
+#define GES_TYPE_EXTRACTABLE (ges_extractable_get_type ())
+#define GES_EXTRACTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_EXTRACTABLE, GESExtractable))
+#define GES_IS_EXTRACTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_EXTRACTABLE))
+#define GES_EXTRACTABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GES_TYPE_EXTRACTABLE, GESExtractableInterface))
+
+GES_API
+GType ges_extractable_get_type (void);
+
+/**
+ * GESExtractableCheckId:
+ * @type: The #GType to check @id for:
+ * @id: The id to check
+ * @error: An error that can be set if needed
+ *
+ * Returns: The ID to use for the asset or %NULL if @id is not valid
+ */
+
+typedef gchar* (*GESExtractableCheckId) (GType type, const gchar *id,
+ GError **error);
+
+/**
+ * GESExtractable:
+ */
+struct _GESExtractableInterface
+{
+ GTypeInterface parent;
+
+ GType asset_type;
+
+ GESExtractableCheckId check_id;
+ gboolean can_update_asset;
+
+ void (*set_asset) (GESExtractable *self,
+ GESAsset *asset);
+
+ gboolean (*set_asset_full) (GESExtractable *self,
+ GESAsset *asset);
+
+ GParameter *(*get_parameters_from_id) (const gchar *id,
+ guint *n_params);
+
+ gchar * (*get_id) (GESExtractable *self);
+
+ GType (*get_real_extractable_type) (GType wanted_type,
+ const gchar *id);
+
+ gboolean (*register_metas) (GESExtractableInterface *self,
+ GObjectClass *klass,
+ GESAsset *asset);
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GESAsset* ges_extractable_get_asset (GESExtractable *self);
+GES_API
+gboolean ges_extractable_set_asset (GESExtractable *self,
+ GESAsset *asset);
+
+GES_API
+gchar * ges_extractable_get_id (GESExtractable *self);
+
+G_END_DECLS
+#endif /* _GES_EXTRACTABLE_ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesformatter
+ * @title: GESFormatter
+ * @short_description: Timeline saving and loading.
+ *
+ **/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ges-formatter.h"
+#include "ges-internal.h"
+#include "ges.h"
+
+/* TODO Add a GCancellable somewhere in the API */
+static void ges_extractable_interface_init (GESExtractableInterface * iface);
+
+struct _GESFormatterPrivate
+{
+ gpointer nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESFormatter, ges_formatter,
+ G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (GESFormatter)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+static void ges_formatter_dispose (GObject * object);
+static gboolean default_can_load_uri (GESFormatter * dummy_instance,
+ const gchar * uri, GError ** error);
+
+/* GESExtractable implementation */
+static gchar *
+extractable_check_id (GType type, const gchar * id)
+{
+ GESFormatterClass *class;
+
+ if (id)
+ return g_strdup (id);
+
+ class = g_type_class_peek (type);
+ return g_strdup (class->name);
+}
+
+static gchar *
+extractable_get_id (GESExtractable * self)
+{
+ GESAsset *asset;
+
+ if (!(asset = ges_extractable_get_asset (self)))
+ return NULL;
+
+ return g_strdup (ges_asset_get_id (asset));
+}
+
+static gboolean
+_register_metas (GESExtractableInterface * iface, GObjectClass * class,
+ GESAsset * asset)
+{
+ GESFormatterClass *fclass = GES_FORMATTER_CLASS (class);
+ GESMetaContainer *container = GES_META_CONTAINER (asset);
+
+ ges_meta_container_register_meta_string (container, GES_META_READABLE,
+ GES_META_FORMATTER_NAME, fclass->name);
+ ges_meta_container_register_meta_string (container, GES_META_READABLE,
+ GES_META_DESCRIPTION, fclass->description);
+ ges_meta_container_register_meta_string (container, GES_META_READABLE,
+ GES_META_FORMATTER_MIMETYPE, fclass->mimetype);
+ ges_meta_container_register_meta_string (container, GES_META_READABLE,
+ GES_META_FORMATTER_EXTENSION, fclass->extension);
+ ges_meta_container_register_meta_double (container, GES_META_READABLE,
+ GES_META_FORMATTER_VERSION, fclass->version);
+ ges_meta_container_register_meta_uint (container, GES_META_READABLE,
+ GES_META_FORMATTER_RANK, fclass->rank);
+ ges_meta_container_register_meta_string (container, GES_META_READ_WRITE,
+ GES_META_FORMAT_VERSION, NULL);
+
+ return TRUE;
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->check_id = (GESExtractableCheckId) extractable_check_id;
+ iface->get_id = extractable_get_id;
+ iface->asset_type = GES_TYPE_ASSET;
+ iface->register_metas = _register_metas;
+}
+
+static void
+ges_formatter_class_init (GESFormatterClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ges_formatter_dispose;
+
+ klass->can_load_uri = default_can_load_uri;
+ klass->load_from_uri = NULL;
+ klass->save_to_uri = NULL;
+
+ /* We set dummy metas */
+ klass->name = "base-formatter";
+ klass->extension = "noextension";
+ klass->description = "Formatter base class, you should give"
+ " a name to your formatter";
+ klass->mimetype = "No mimetype";
+ klass->version = 0.0;
+ klass->rank = GST_RANK_NONE;
+}
+
+static void
+ges_formatter_init (GESFormatter * object)
+{
+ object->priv = ges_formatter_get_instance_private (object);
+ object->project = NULL;
+}
+
+static void
+ges_formatter_dispose (GObject * object)
+{
+ ges_formatter_set_project (GES_FORMATTER (object), NULL);
+
+ G_OBJECT_CLASS (ges_formatter_parent_class)->dispose (object);
+}
+
+static gboolean
+default_can_load_uri (GESFormatter * dummy_instance, const gchar * uri,
+ GError ** error)
+{
+ GST_DEBUG ("%s: no 'can_load_uri' vmethod implementation",
+ G_OBJECT_TYPE_NAME (dummy_instance));
+
+ return FALSE;
+}
+
+static gchar *
+_get_extension (const gchar * uri)
+{
+ gchar *result;
+ gsize len;
+ gint find;
+
+ GST_DEBUG ("finding extension of %s", uri);
+
+ if (uri == NULL)
+ goto no_uri;
+
+ /* find the extension on the uri, this is everything after a '.' */
+ len = strlen (uri);
+ find = len - 1;
+
+ while (find >= 0) {
+ if (uri[find] == '.')
+ break;
+ find--;
+ }
+ if (find < 0)
+ goto no_extension;
+
+ result = g_strdup (&uri[find + 1]);
+
+ GST_DEBUG ("found extension %s", result);
+
+ return result;
+
+ /* ERRORS */
+no_uri:
+ {
+ GST_WARNING ("could not parse the peer uri");
+ return NULL;
+ }
+no_extension:
+ {
+ GST_WARNING ("could not find uri extension in %s", uri);
+ return NULL;
+ }
+}
+
+/**
+ * ges_formatter_can_load_uri:
+ * @uri: a #gchar * pointing to the URI
+ * @error: A #GError that will be set in case of error
+ *
+ * Checks if there is a #GESFormatter available which can load a #GESTimeline
+ * from the given URI.
+ *
+ * Returns: TRUE if there is a #GESFormatter that can support the given uri
+ * or FALSE if not.
+ */
+
+gboolean
+ges_formatter_can_load_uri (const gchar * uri, GError ** error)
+{
+ gboolean ret = FALSE;
+ gchar *extension;
+ GList *formatter_assets, *tmp;
+ GESFormatterClass *class = NULL;
+
+ if (!(gst_uri_is_valid (uri))) {
+ GST_ERROR ("Invalid uri!");
+ return FALSE;
+ }
+
+ extension = _get_extension (uri);
+
+ formatter_assets = ges_list_assets (GES_TYPE_FORMATTER);
+ for (tmp = formatter_assets; tmp; tmp = tmp->next) {
+ GESAsset *asset = GES_ASSET (tmp->data);
+ GESFormatter *dummy_instance;
+
+ if (extension
+ && g_strcmp0 (extension,
+ ges_meta_container_get_string (GES_META_CONTAINER (asset),
+ GES_META_FORMATTER_EXTENSION)))
+ continue;
+
+ class = g_type_class_ref (ges_asset_get_extractable_type (asset));
+ dummy_instance =
+ g_object_ref_sink (g_object_new (ges_asset_get_extractable_type (asset),
+ NULL));
+ if (class->can_load_uri (dummy_instance, uri, error)) {
+ g_type_class_unref (class);
+ gst_object_unref (dummy_instance);
+ ret = TRUE;
+ break;
+ }
+ g_type_class_unref (class);
+ gst_object_unref (dummy_instance);
+ }
+
+ g_list_free (formatter_assets);
+ return ret;
+}
+
+/**
+ * ges_formatter_can_save_uri:
+ * @uri: a #gchar * pointing to a URI
+ * @error: A #GError that will be set in case of error
+ *
+ * Returns TRUE if there is a #GESFormatter available which can save a
+ * #GESTimeline to the given URI.
+ *
+ * Returns: TRUE if the given @uri is supported, else FALSE.
+ */
+
+gboolean
+ges_formatter_can_save_uri (const gchar * uri, GError ** error)
+{
+ GFile *file = NULL;
+ GFile *dir = NULL;
+ gboolean ret = TRUE;
+ GFileInfo *info = NULL;
+
+ if (!(gst_uri_is_valid (uri))) {
+ GST_ERROR ("%s invalid uri!", uri);
+ return FALSE;
+ }
+
+ if (!(gst_uri_has_protocol (uri, "file"))) {
+ gchar *proto = gst_uri_get_protocol (uri);
+ GST_ERROR ("Unspported protocol '%s'", proto);
+ g_free (proto);
+ return FALSE;
+ }
+
+ /* Check if URI or parent directory is writeable */
+ file = g_file_new_for_uri (uri);
+ if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL)
+ == G_FILE_TYPE_DIRECTORY) {
+ dir = g_object_ref (file);
+ } else {
+ dir = g_file_get_parent (file);
+
+ if (dir == NULL)
+ goto error;
+ }
+
+ info = g_file_query_info (dir, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+ G_FILE_QUERY_INFO_NONE, NULL, error);
+
+ if (error && *error != NULL) {
+ GST_ERROR ("Unable to write to directory: %s", (*error)->message);
+
+ goto error;
+ } else {
+ gboolean writeable = g_file_info_get_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+ if (!writeable) {
+ GST_ERROR ("Unable to write to directory");
+ goto error;
+ }
+ }
+
+done:
+ if (file)
+ g_object_unref (file);
+
+ if (dir)
+ g_object_unref (dir);
+
+ if (info)
+ g_object_unref (info);
+
+ /* TODO: implement file format registry */
+ /* TODO: search through the registry and chose a GESFormatter class that can
+ * handle the URI.*/
+
+ return ret;
+error:
+ ret = FALSE;
+ goto done;
+}
+
+/**
+ * ges_formatter_load_from_uri:
+ * @formatter: a #GESFormatter
+ * @timeline: a #GESTimeline
+ * @uri: a #gchar * pointing to a URI
+ * @error: A #GError that will be set in case of error
+ *
+ * Load data from the given URI into timeline.
+ *
+ * Returns: TRUE if the timeline data was successfully loaded from the URI,
+ * else FALSE.
+ */
+
+gboolean
+ges_formatter_load_from_uri (GESFormatter * formatter,
+ GESTimeline * timeline, const gchar * uri, GError ** error)
+{
+ gboolean ret = FALSE;
+ GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
+
+ g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+
+ if (klass->load_from_uri) {
+ formatter->timeline = timeline;
+ ret = klass->load_from_uri (formatter, timeline, uri, error);
+ }
+
+ return ret;
+}
+
+/**
+ * ges_formatter_save_to_uri:
+ * @formatter: a #GESFormatter
+ * @timeline: a #GESTimeline
+ * @uri: a #gchar * pointing to a URI
+ * @overwrite: %TRUE to overwrite file if it exists
+ * @error: A #GError that will be set in case of error
+ *
+ * Save data from timeline to the given URI.
+ *
+ * Returns: TRUE if the timeline data was successfully saved to the URI
+ * else FALSE.
+ */
+
+gboolean
+ges_formatter_save_to_uri (GESFormatter * formatter, GESTimeline *
+ timeline, const gchar * uri, gboolean overwrite, GError ** error)
+{
+ GError *lerr = NULL;
+ gboolean ret = FALSE;
+ GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
+
+ GST_DEBUG_OBJECT (formatter, "Saving %" GST_PTR_FORMAT " to %s",
+ timeline, uri);
+ if (klass->save_to_uri)
+ ret = klass->save_to_uri (formatter, timeline, uri, overwrite, &lerr);
+ else
+ GST_ERROR_OBJECT (formatter, "save_to_uri not implemented!");
+
+ if (lerr) {
+ GST_WARNING_OBJECT (formatter, "%" GST_PTR_FORMAT
+ " not saved to %s error: %s", timeline, uri, lerr->message);
+ g_propagate_error (error, lerr);
+ } else
+ GST_INFO_OBJECT (formatter, "%" GST_PTR_FORMAT
+ " saved to %s", timeline, uri);
+
+ return ret;
+}
+
+/**
+ * ges_formatter_get_default:
+ *
+ * Get the default #GESAsset to use as formatter. It will return
+ * the asset for the #GESFormatter that has the highest @rank
+ *
+ * Returns: (transfer none): The #GESAsset for the formatter with highest @rank
+ */
+GESAsset *
+ges_formatter_get_default (void)
+{
+ GList *assets, *tmp;
+ GESAsset *ret = NULL;
+ guint tmprank, rank = GST_RANK_NONE;
+
+ assets = ges_list_assets (GES_TYPE_FORMATTER);
+ for (tmp = assets; tmp; tmp = tmp->next) {
+ tmprank = GST_RANK_NONE;
+ ges_meta_container_get_uint (GES_META_CONTAINER (tmp->data),
+ GES_META_FORMATTER_RANK, &tmprank);
+
+ if (tmprank > rank) {
+ rank = tmprank;
+ ret = GES_ASSET (tmp->data);
+ }
+ }
+ g_list_free (assets);
+
+ return ret;
+}
+
+void
+ges_formatter_class_register_metas (GESFormatterClass * class,
+ const gchar * name, const gchar * description, const gchar * extension,
+ const gchar * mimetype, gdouble version, GstRank rank)
+{
+ class->name = name;
+ class->description = description;
+ class->extension = extension;
+ class->mimetype = mimetype;
+ class->version = version;
+ class->rank = rank;
+
+ if (ges_is_initialized () && g_type_class_peek (G_OBJECT_CLASS_TYPE (class)))
+ gst_object_unref (ges_asset_request (G_OBJECT_CLASS_TYPE (class), NULL,
+ NULL));
+}
+
+/* Main Formatter methods */
+
+/*< protected >*/
+void
+ges_formatter_set_project (GESFormatter * formatter, GESProject * project)
+{
+ formatter->project = project;
+}
+
+GESProject *
+ges_formatter_get_project (GESFormatter * formatter)
+{
+ return formatter->project;
+}
+
+static void
+_list_formatters (GType * formatters, guint n_formatters)
+{
+ GType *tmptypes, type;
+ guint tmp_n_types, i;
+
+ for (i = 0; i < n_formatters; i++) {
+ type = formatters[i];
+ tmptypes = g_type_children (type, &tmp_n_types);
+ if (tmp_n_types) {
+ /* Recurse as g_type_children does not */
+ _list_formatters (tmptypes, tmp_n_types);
+ }
+ g_free (tmptypes);
+
+ if (G_TYPE_IS_ABSTRACT (type)) {
+ GST_DEBUG ("%s is abstract, not using", g_type_name (type));
+ } else {
+ gst_object_unref (ges_asset_request (type, NULL, NULL));
+ }
+ }
+}
+
+void
+_init_formatter_assets (void)
+{
+ GType *formatters;
+ guint n_formatters;
+
+ formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
+ _list_formatters (formatters, n_formatters);
+ g_free (formatters);
+}
+
+static gint
+_sort_formatters (GESAsset * asset, GESAsset * asset1)
+{
+ GESFormatterClass *class =
+ g_type_class_peek (ges_asset_get_extractable_type (asset));
+ GESFormatterClass *class1 =
+ g_type_class_peek (ges_asset_get_extractable_type (asset1));
+
+ /* We want the highest ranks to be first! */
+ if (class->rank > class1->rank)
+ return -1;
+ else if (class->rank < class1->rank)
+ return 1;
+
+ return 0;
+}
+
+GESAsset *
+_find_formatter_asset_for_id (const gchar * id)
+{
+ GESFormatterClass *class = NULL;
+ GList *formatter_assets, *tmp;
+ GESAsset *asset = NULL;
+
+ formatter_assets = g_list_sort (ges_list_assets (GES_TYPE_FORMATTER),
+ (GCompareFunc) _sort_formatters);
+ for (tmp = formatter_assets; tmp; tmp = tmp->next) {
+ GESFormatter *dummy_instance;
+
+ asset = GES_ASSET (tmp->data);
+ class = g_type_class_ref (ges_asset_get_extractable_type (asset));
+ dummy_instance =
+ g_object_ref_sink (g_object_new (ges_asset_get_extractable_type (asset),
+ NULL));
+ if (class->can_load_uri (dummy_instance, id, NULL)) {
+ g_type_class_unref (class);
+ asset = gst_object_ref (asset);
+ gst_object_unref (dummy_instance);
+ break;
+ }
+
+ asset = NULL;
+ g_type_class_unref (class);
+ gst_object_unref (dummy_instance);
+ }
+
+ g_list_free (formatter_assets);
+
+ return asset;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_FORMATTER
+#define _GES_FORMATTER
+
+#include <glib-object.h>
+#include <ges/ges-timeline.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_FORMATTER ges_formatter_get_type()
+
+#define GES_FORMATTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_FORMATTER, GESFormatter))
+
+#define GES_FORMATTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_FORMATTER, GESFormatterClass))
+
+#define GES_IS_FORMATTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_FORMATTER))
+
+#define GES_IS_FORMATTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_FORMATTER))
+
+#define GES_FORMATTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_FORMATTER, GESFormatterClass))
+
+typedef struct _GESFormatterPrivate GESFormatterPrivate;
+
+/**
+ * GESFormatter:
+ *
+ * Base class for timeline data serialization and deserialization.
+ */
+
+struct _GESFormatter {
+ GInitiallyUnowned parent;
+
+ /*< private >*/
+ GESFormatterPrivate *priv;
+
+ /*< protected >*/
+ GESProject *project;
+ GESTimeline *timeline;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+typedef gboolean (*GESFormatterCanLoadURIMethod) (GESFormatter *dummy_instance, const gchar * uri, GError **error);
+
+/**
+ * GESFormatterLoadFromURIMethod:
+ * @formatter: a #GESFormatter
+ * @timeline: a #GESTimeline
+ * @uri: the URI to load from
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Virtual method for loading a timeline from a given URI.
+ *
+ * Every #GESFormatter subclass needs to implement this method.
+ *
+ * Returns: TRUE if the @timeline was properly loaded from the given @uri,
+ * else FALSE.
+ *
+ * Deprecated: 1.16: Use @ges_timeline_load_from_uri
+ **/
+typedef gboolean (*GESFormatterLoadFromURIMethod) (GESFormatter *formatter,
+ GESTimeline *timeline,
+ const gchar * uri,
+ GError **error);
+
+/**
+ * GESFormatterSaveToURIMethod:
+ * @formatter: a #GESFormatter
+ * @timeline: a #GESTimeline
+ * @uri: the URI to save to
+ * @overwrite: Whether the file should be overwritten in case it exists
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Virtual method for saving a timeline to a uri.
+ *
+ * Every #GESFormatter subclass needs to implement this method.
+ *
+ * Returns: TRUE if the @timeline was properly stored to the given @uri,
+ * else FALSE.
+ *
+ * Deprecated: 1.16: Use @ges_timeline_save_to_uri
+ */
+typedef gboolean (*GESFormatterSaveToURIMethod) (GESFormatter *formatter,
+ GESTimeline *timeline, const gchar * uri, gboolean overwrite,
+ GError **error);
+
+/**
+ * GESFormatterClass:
+ * @parent_class: the parent class structure
+ * @can_load_uri: Whether the URI can be loaded
+ * @load_from_uri: class method to deserialize data from a URI
+ * @save_to_uri: class method to serialize data to a URI
+ *
+ * GES Formatter class. Override the vmethods to implement the formatter functionnality.
+ */
+
+struct _GESFormatterClass {
+ GInitiallyUnownedClass parent_class;
+
+ /* TODO 2.0: Rename the loading method to can_load and load.
+ * Technically we just pass data to load, it should not necessarily
+ * be a URI */
+ GESFormatterCanLoadURIMethod can_load_uri;
+ GESFormatterLoadFromURIMethod load_from_uri;
+ GESFormatterSaveToURIMethod save_to_uri;
+
+ /* < private > */
+ const gchar *name;
+ const gchar *description;
+ const gchar *extension;
+ const gchar *mimetype;
+ gdouble version;
+ GstRank rank;
+
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_formatter_get_type (void);
+
+GES_API
+void ges_formatter_class_register_metas (GESFormatterClass * klass,
+ const gchar *name,
+ const gchar *description,
+ const gchar *extension,
+ const gchar *mimetype,
+ gdouble version,
+ GstRank rank);
+
+GES_API
+gboolean ges_formatter_can_load_uri (const gchar * uri, GError **error);
+GES_API
+gboolean ges_formatter_can_save_uri (const gchar * uri, GError **error);
+
+GES_API
+gboolean ges_formatter_load_from_uri (GESFormatter * formatter,
+ GESTimeline *timeline,
+ const gchar *uri,
+ GError **error);
+
+GES_API
+gboolean ges_formatter_save_to_uri (GESFormatter * formatter,
+ GESTimeline *timeline,
+ const gchar *uri,
+ gboolean overwrite,
+ GError **error);
+
+GES_API
+GESAsset *ges_formatter_get_default (void);
+
+G_END_DECLS
+
+#endif /* _GES_FORMATTER */
--- /dev/null
+/* GStreamer Editing Services
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GES_ERROR_H__
+#define __GES_ERROR_H__
+
+/**
+ * SECTION: ges-gerror
+ * @title: GESErrors
+ * @short_description: GError — Categorized error messages
+ */
+
+G_BEGIN_DECLS
+
+/**
+ * GES_ERROR:
+ *
+ * An error happened in GES
+ */
+#define GES_ERROR g_quark_from_static_string("GES_ERROR")
+
+/**
+ * GESError:
+ * @GES_ERROR_ASSET_WRONG_ID: The ID passed is malformed
+ * @GES_ERROR_ASSET_LOADING: An error happened while loading the asset
+ * @GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE: The formatted files was malformed
+ */
+typedef enum
+{
+ GES_ERROR_ASSET_WRONG_ID,
+ GES_ERROR_ASSET_LOADING,
+ GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE,
+} GESError;
+
+G_END_DECLS
+#endif /* __GES_ERROR_H__ */
--- /dev/null
+/* GStreamer Editing Services
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesgroup
+ * @title: GESGroup
+ * @short_description: Class that permits to group GESClip-s in a timeline,
+ * letting the user manage it a single GESTimelineElement
+ *
+ * A #GESGroup is an object which controls one or more
+ * #GESClips in one or more #GESLayer(s).
+ *
+ * To instanciate a group, you should use the ges_container_group method,
+ * this will be responsible for deciding what subclass of #GESContainer
+ * should be instaciated to group the various #GESTimelineElement passed
+ * in parametter.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-group.h"
+#include "ges.h"
+#include "ges-internal.h"
+
+#include <string.h>
+
+#define parent_class ges_group_parent_class
+#define GES_CHILDREN_INIBIT_SIGNAL_EMISSION (GES_CHILDREN_LAST + 1)
+#define GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT "ges-group-signals-ids-%p"
+
+struct _GESGroupPrivate
+{
+ gboolean reseting_start;
+
+ guint32 max_layer_prio;
+
+ /* This is used while were are setting ourselve a proper timing value,
+ * in this case the value should always be kept */
+ gboolean setting_value;
+};
+
+typedef struct
+{
+ GESLayer *layer;
+ gulong child_clip_changed_layer_sid;
+ gulong child_priority_changed_sid;
+ gulong child_group_priority_changed_sid;
+} ChildSignalIds;
+
+enum
+{
+ PROP_0,
+ PROP_START,
+ PROP_INPOINT,
+ PROP_DURATION,
+ PROP_MAX_DURATION,
+ PROP_PRIORITY,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESGroup, ges_group, GES_TYPE_CONTAINER);
+
+/****************************************************
+ * Our listening of children *
+ ****************************************************/
+static void
+_update_our_values (GESGroup * group)
+{
+ GList *tmp;
+ GESContainer *container = GES_CONTAINER (group);
+ guint32 min_layer_prio = G_MAXINT32, max_layer_prio = 0;
+
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ GESContainer *child = tmp->data;
+
+ if (GES_IS_CLIP (child)) {
+ GESLayer *layer = ges_clip_get_layer (GES_CLIP (child));
+ gint32 prio;
+
+ if (!layer)
+ continue;
+
+ prio = ges_layer_get_priority (layer);
+
+ min_layer_prio = MIN (prio, min_layer_prio);
+ max_layer_prio = MAX (prio, max_layer_prio);
+ } else if (GES_IS_GROUP (child)) {
+ gint32 prio = _PRIORITY (child), height = GES_CONTAINER_HEIGHT (child);
+
+ min_layer_prio = MIN (prio, min_layer_prio);
+ max_layer_prio = MAX ((prio + height), max_layer_prio);
+ }
+ }
+
+ if (min_layer_prio != _PRIORITY (group)) {
+ group->priv->setting_value = TRUE;
+ _set_priority0 (GES_TIMELINE_ELEMENT (group), min_layer_prio);
+ group->priv->setting_value = FALSE;
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+ guint32 child_prio = GES_IS_CLIP (child) ?
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (child) : _PRIORITY (child);
+
+ _ges_container_set_priority_offset (container,
+ child, min_layer_prio - child_prio);
+ }
+ }
+
+ group->priv->max_layer_prio = max_layer_prio;
+ _ges_container_set_height (GES_CONTAINER (group),
+ max_layer_prio - min_layer_prio + 1);
+}
+
+static void
+_child_priority_changed_cb (GESLayer * layer,
+ GParamSpec * arg G_GNUC_UNUSED, GESTimelineElement * clip)
+{
+ GESContainer *container = GES_CONTAINER (GES_TIMELINE_ELEMENT_PARENT (clip));
+
+ gint layer_prio = ges_layer_get_priority (layer);
+ gint offset = _ges_container_get_priority_offset (container, clip);
+
+ if (container->children_control_mode != GES_CHILDREN_UPDATE) {
+ GST_DEBUG_OBJECT (container, "Ignoring updated");
+ return;
+ }
+
+ if (layer_prio + offset == _PRIORITY (container))
+ return;
+
+ container->initiated_move = clip;
+ _set_priority0 (GES_TIMELINE_ELEMENT (container), layer_prio + offset);
+ container->initiated_move = NULL;
+}
+
+static void
+_child_clip_changed_layer_cb (GESTimelineElement * clip,
+ GParamSpec * arg G_GNUC_UNUSED, GESGroup * group)
+{
+ ChildSignalIds *sigids;
+ gchar *signals_ids_key;
+ GESLayer *old_layer, *new_layer;
+ gint offset, layer_prio = GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip);
+ GESContainer *container = GES_CONTAINER (group);
+
+ offset = _ges_container_get_priority_offset (container, clip);
+ signals_ids_key =
+ g_strdup_printf (GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT, clip);
+ sigids = g_object_get_data (G_OBJECT (group), signals_ids_key);
+ g_free (signals_ids_key);
+ old_layer = sigids->layer;
+
+ new_layer = ges_clip_get_layer (GES_CLIP (clip));
+
+ if (sigids->child_priority_changed_sid) {
+ g_signal_handler_disconnect (old_layer, sigids->child_priority_changed_sid);
+ sigids->child_priority_changed_sid = 0;
+ }
+
+ if (new_layer) {
+ sigids->child_priority_changed_sid =
+ g_signal_connect (new_layer, "notify::priority",
+ (GCallback) _child_priority_changed_cb, clip);
+ }
+ sigids->layer = new_layer;
+
+ if (container->children_control_mode != GES_CHILDREN_UPDATE) {
+ if (container->children_control_mode == GES_CHILDREN_INIBIT_SIGNAL_EMISSION) {
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+ g_signal_stop_emission_by_name (clip, "notify::layer");
+ }
+ return;
+ }
+
+ if (new_layer && old_layer && (layer_prio + offset < 0 ||
+ (GES_TIMELINE_ELEMENT_TIMELINE (group) &&
+ layer_prio + offset + GES_CONTAINER_HEIGHT (group) - 1 >
+ g_list_length (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers)))) {
+
+ GST_INFO_OBJECT (container,
+ "Trying to move to a layer %" GST_PTR_FORMAT " outside of"
+ "the timeline layers, moving back to old layer (prio %i)", new_layer,
+ _PRIORITY (group) - offset);
+
+ container->children_control_mode = GES_CHILDREN_INIBIT_SIGNAL_EMISSION;
+ ges_clip_move_to_layer (GES_CLIP (clip), old_layer);
+ g_signal_stop_emission_by_name (clip, "notify::layer");
+
+ return;
+ }
+
+ if (!new_layer || !old_layer) {
+ _update_our_values (group);
+
+ return;
+ }
+
+ container->initiated_move = clip;
+ _set_priority0 (GES_TIMELINE_ELEMENT (group), layer_prio + offset);
+ container->initiated_move = NULL;
+}
+
+static void
+_child_group_priority_changed (GESTimelineElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESGroup * group)
+{
+ gint offset;
+ GESContainer *container = GES_CONTAINER (group);
+
+ if (container->children_control_mode != GES_CHILDREN_UPDATE) {
+ GST_DEBUG_OBJECT (group, "Ignoring updated");
+ return;
+ }
+
+ offset = _ges_container_get_priority_offset (container, child);
+
+ if (_PRIORITY (group) < offset ||
+ (GES_TIMELINE_ELEMENT_TIMELINE (group) &&
+ _PRIORITY (group) + offset + GES_CONTAINER_HEIGHT (group) >
+ g_list_length (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers))) {
+
+ GST_WARNING_OBJECT (container, "Trying to move to a layer outside of"
+ "the timeline layers");
+
+ return;
+ }
+
+ container->initiated_move = child;
+ _set_priority0 (GES_TIMELINE_ELEMENT (group), _PRIORITY (child) + offset);
+ container->initiated_move = NULL;
+}
+
+/****************************************************
+ * GESTimelineElement vmethods *
+ ****************************************************/
+static gboolean
+_trim (GESTimelineElement * group, GstClockTime start)
+{
+ GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (group);
+
+ if (timeline == NULL) {
+ GST_DEBUG ("Not in a timeline yet");
+
+ return FALSE;
+ }
+
+ return timeline_tree_trim (timeline_get_tree (timeline), group,
+ 0, GST_CLOCK_DIFF (start, _START (group)), GES_EDGE_START,
+ ges_timeline_get_snapping_distance (timeline));
+}
+
+static gboolean
+_set_priority (GESTimelineElement * element, guint32 priority)
+{
+ GList *tmp, *layers;
+ gint diff = priority - _PRIORITY (element);
+ GESContainer *container = GES_CONTAINER (element);
+ GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
+
+ if (GES_GROUP (element)->priv->setting_value == TRUE)
+ return TRUE;
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+
+ layers = GES_TIMELINE_ELEMENT_TIMELINE (element) ?
+ GES_TIMELINE_ELEMENT_TIMELINE (element)->layers : NULL;
+ if (layers == NULL) {
+ GST_WARNING_OBJECT (element, "Not any layer in the timeline, not doing"
+ "anything, timeline: %" GST_PTR_FORMAT,
+ GES_TIMELINE_ELEMENT_TIMELINE (element));
+
+ return FALSE;
+ }
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+
+ if (child != container->initiated_move
+ || ELEMENT_FLAG_IS_SET (container, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ if (GES_IS_CLIP (child)) {
+ guint32 layer_prio = GES_TIMELINE_ELEMENT_LAYER_PRIORITY (child) + diff;
+ GESLayer *layer =
+ g_list_nth_data (GES_TIMELINE_GET_LAYERS (timeline), layer_prio);
+
+ if (layer == NULL) {
+ do {
+ layer = ges_timeline_append_layer (timeline);
+ } while (ges_layer_get_priority (layer) < layer_prio);
+ }
+
+ GST_DEBUG ("%" GES_FORMAT "moving from layer: %i to %i",
+ GES_ARGS (child), GES_TIMELINE_ELEMENT_LAYER_PRIORITY (child),
+ layer_prio);
+ ges_clip_move_to_layer (GES_CLIP (child), g_list_nth_data (layers,
+ layer_prio));
+ } else if (GES_IS_GROUP (child)) {
+ GST_DEBUG_OBJECT (child, "moving from %i to %i",
+ _PRIORITY (child), diff + _PRIORITY (child));
+ ges_timeline_element_set_priority (child, diff + _PRIORITY (child));
+ }
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_start (GESTimelineElement * element, GstClockTime start)
+{
+ GList *tmp;
+ gint64 diff = start - _START (element);
+ GESTimeline *timeline;
+ GESContainer *container = GES_CONTAINER (element);
+ GESTimelineElement *toplevel =
+ ges_timeline_element_get_toplevel_parent (element);;
+
+ gst_object_unref (toplevel);
+ if (GES_GROUP (element)->priv->setting_value == TRUE)
+ /* Let GESContainer update itself */
+ return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_start (element,
+ start);
+
+ if (ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE) ||
+ ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next)
+ _set_start0 (tmp->data, _START (tmp->data) + diff);
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+ }
+
+ timeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
+ if (timeline)
+ return ges_timeline_move_object_simple (timeline, element, NULL,
+ GES_EDGE_NONE, start);
+
+ return TRUE;
+}
+
+static gboolean
+_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
+{
+ return FALSE;
+}
+
+static gboolean
+_set_duration (GESTimelineElement * element, GstClockTime duration)
+{
+ GList *tmp;
+ GstClockTime last_child_end = 0, new_end;
+ GESContainer *container = GES_CONTAINER (element);
+ GESGroupPrivate *priv = GES_GROUP (element)->priv;
+
+ if (priv->setting_value == TRUE)
+ /* Let GESContainer update itself */
+ return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_duration (element,
+ duration);
+
+ if (element->timeline
+ && !timeline_tree_can_move_element (timeline_get_tree (element->timeline),
+ element, _PRIORITY (element), element->start, duration, NULL)) {
+ return FALSE;
+ }
+
+ if (container->initiated_move == NULL) {
+ gboolean expending = (_DURATION (element) < duration);
+
+ new_end = _START (element) + duration;
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+ GstClockTime n_dur;
+
+ if ((!expending && _END (child) > new_end) ||
+ (expending && (_END (child) >= _END (element)))) {
+ n_dur = MAX (0, ((gint64) (new_end - _START (child))));
+ _set_duration0 (child, n_dur);
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+ }
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ if (_DURATION (tmp->data))
+ last_child_end =
+ MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
+ }
+
+ priv->setting_value = TRUE;
+ _set_duration0 (element, last_child_end - _START (element));
+ priv->setting_value = FALSE;
+
+ return FALSE;
+}
+
+/****************************************************
+ * *
+ * GESContainer virtual methods implementation *
+ * *
+ ****************************************************/
+
+static gboolean
+_add_child (GESContainer * group, GESTimelineElement * child)
+{
+ g_return_val_if_fail (GES_IS_CONTAINER (child), FALSE);
+
+ return TRUE;
+}
+
+static void
+_child_added (GESContainer * group, GESTimelineElement * child)
+{
+ GList *children, *tmp;
+ gchar *signals_ids_key;
+ ChildSignalIds *signals_ids;
+
+ GESGroupPrivate *priv = GES_GROUP (group)->priv;
+ GstClockTime last_child_end = 0, first_child_start = G_MAXUINT64;
+
+ if (!GES_TIMELINE_ELEMENT_TIMELINE (group)
+ && GES_TIMELINE_ELEMENT_TIMELINE (child)) {
+ timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (child),
+ GES_GROUP (group));
+ timeline_emit_group_added (GES_TIMELINE_ELEMENT_TIMELINE (child),
+ GES_GROUP (group));
+ }
+
+ children = GES_CONTAINER_CHILDREN (group);
+
+ for (tmp = children; tmp; tmp = tmp->next) {
+ last_child_end = MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
+ first_child_start =
+ MIN (GES_TIMELINE_ELEMENT_START (tmp->data), first_child_start);
+ }
+
+ priv->setting_value = TRUE;
+ ELEMENT_SET_FLAG (group, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ if (first_child_start != GES_TIMELINE_ELEMENT_START (group)) {
+ group->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ _set_start0 (GES_TIMELINE_ELEMENT (group), first_child_start);
+ }
+
+ if (last_child_end != GES_TIMELINE_ELEMENT_END (group)) {
+ _set_duration0 (GES_TIMELINE_ELEMENT (group),
+ last_child_end - first_child_start);
+ }
+ ELEMENT_UNSET_FLAG (group, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ priv->setting_value = FALSE;
+
+ group->children_control_mode = GES_CHILDREN_UPDATE;
+ _update_our_values (GES_GROUP (group));
+
+ signals_ids_key =
+ g_strdup_printf (GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT, child);
+ signals_ids = g_malloc0 (sizeof (ChildSignalIds));
+ g_object_set_data_full (G_OBJECT (group), signals_ids_key,
+ signals_ids, g_free);
+ g_free (signals_ids_key);
+ if (GES_IS_CLIP (child)) {
+ GESLayer *layer = ges_clip_get_layer (GES_CLIP (child));
+
+ signals_ids->child_clip_changed_layer_sid =
+ g_signal_connect (child, "notify::layer",
+ (GCallback) _child_clip_changed_layer_cb, group);
+
+ if (layer) {
+ signals_ids->child_priority_changed_sid = g_signal_connect (layer,
+ "notify::priority", (GCallback) _child_priority_changed_cb, child);
+ }
+ signals_ids->layer = layer;
+
+ } else if (GES_IS_GROUP (child), group) {
+ signals_ids->child_group_priority_changed_sid =
+ g_signal_connect (child, "notify::priority",
+ (GCallback) _child_group_priority_changed, group);
+ }
+}
+
+static void
+_disconnect_signals (GESGroup * group, GESTimelineElement * child,
+ ChildSignalIds * sigids)
+{
+ if (sigids->child_group_priority_changed_sid) {
+ g_signal_handler_disconnect (child,
+ sigids->child_group_priority_changed_sid);
+ sigids->child_group_priority_changed_sid = 0;
+ }
+
+ if (sigids->child_clip_changed_layer_sid) {
+ g_signal_handler_disconnect (child, sigids->child_clip_changed_layer_sid);
+ sigids->child_clip_changed_layer_sid = 0;
+ }
+
+ if (sigids->child_priority_changed_sid) {
+ g_signal_handler_disconnect (sigids->layer,
+ sigids->child_priority_changed_sid);
+ sigids->child_priority_changed_sid = 0;
+ }
+}
+
+
+static void
+_child_removed (GESContainer * group, GESTimelineElement * child)
+{
+ GList *children;
+ GstClockTime first_child_start;
+ gchar *signals_ids_key;
+ ChildSignalIds *sigids;
+ GESGroupPrivate *priv = GES_GROUP (group)->priv;
+
+ _ges_container_sort_children (group);
+
+ children = GES_CONTAINER_CHILDREN (group);
+
+ signals_ids_key =
+ g_strdup_printf (GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT, child);
+ sigids = g_object_get_data (G_OBJECT (group), signals_ids_key);
+ _disconnect_signals (GES_GROUP (group), child, sigids);
+ g_free (signals_ids_key);
+ if (children == NULL) {
+ GST_FIXME_OBJECT (group, "Auto destroy myself?");
+ if (GES_TIMELINE_ELEMENT_TIMELINE (group))
+ timeline_remove_group (GES_TIMELINE_ELEMENT_TIMELINE (group),
+ GES_GROUP (group));
+ return;
+ }
+
+ priv->setting_value = TRUE;
+ ELEMENT_SET_FLAG (group, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ first_child_start = GES_TIMELINE_ELEMENT_START (children->data);
+ if (first_child_start > GES_TIMELINE_ELEMENT_START (group)) {
+ group->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ _set_start0 (GES_TIMELINE_ELEMENT (group), first_child_start);
+ group->children_control_mode = GES_CHILDREN_UPDATE;
+ }
+ ELEMENT_UNSET_FLAG (group, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ priv->setting_value = FALSE;
+}
+
+static GList *
+_ungroup (GESContainer * group, gboolean recursive)
+{
+ GPtrArray *children_array;
+ GESTimeline *timeline;
+ GList *children, *tmp, *ret = NULL;
+
+ /* We choose 16 just as an arbitrary value */
+ children_array = g_ptr_array_sized_new (16);
+ timeline = GES_TIMELINE_ELEMENT_TIMELINE (group);
+
+ children = ges_container_get_children (group, FALSE);
+ for (tmp = children; tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+
+ gst_object_ref (child);
+ ges_container_remove (group, child);
+ g_ptr_array_add (children_array, child);
+ ret = g_list_append (ret, child);
+ }
+
+ if (timeline)
+ timeline_emit_group_removed (timeline, (GESGroup *) group, children_array);
+ g_ptr_array_free (children_array, TRUE);
+ g_list_free_full (children, gst_object_unref);
+
+ /* No need to remove from the timeline here, this will be done in _child_removed */
+
+ return ret;
+}
+
+static GESContainer *
+_group (GList * containers)
+{
+ GList *tmp;
+ GESTimeline *timeline = NULL;
+ GESContainer *ret = g_object_new (GES_TYPE_GROUP, NULL);
+
+ if (!containers)
+ return ret;
+
+ for (tmp = containers; tmp; tmp = tmp->next) {
+ if (!timeline) {
+ timeline = GES_TIMELINE_ELEMENT_TIMELINE (tmp->data);
+ } else if (timeline != GES_TIMELINE_ELEMENT_TIMELINE (tmp->data)) {
+ g_object_unref (ret);
+
+ return NULL;
+ }
+
+ ges_container_add (ret, tmp->data);
+ }
+
+ /* No need to add to the timeline here, this will be done in _child_added */
+
+ return ret;
+}
+
+static GESTimelineElement *
+_paste (GESTimelineElement * element, GESTimelineElement * ref,
+ GstClockTime paste_position)
+{
+ GESTimelineElement *ngroup =
+ GES_TIMELINE_ELEMENT_CLASS (parent_class)->paste (element, ref,
+ paste_position);
+
+ if (ngroup) {
+ if (GES_CONTAINER_CHILDREN (ngroup)) {
+ timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (GES_CONTAINER_CHILDREN
+ (ngroup)->data), GES_GROUP (element));
+ timeline_emit_group_added (GES_TIMELINE_ELEMENT_TIMELINE
+ (GES_CONTAINER_CHILDREN (ngroup)->data), GES_GROUP (element));
+ }
+ }
+
+ return ngroup;
+}
+
+
+/****************************************************
+ * *
+ * GObject virtual methods implementation *
+ * *
+ ****************************************************/
+static void
+ges_group_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
+
+ switch (property_id) {
+ case PROP_START:
+ g_value_set_uint64 (value, self->start);
+ break;
+ case PROP_INPOINT:
+ g_value_set_uint64 (value, self->inpoint);
+ break;
+ case PROP_DURATION:
+ g_value_set_uint64 (value, self->duration);
+ break;
+ case PROP_MAX_DURATION:
+ g_value_set_uint64 (value, self->maxduration);
+ break;
+ case PROP_PRIORITY:
+ g_value_set_uint (value, self->priority);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ }
+}
+
+static void
+ges_group_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
+
+ switch (property_id) {
+ case PROP_START:
+ ges_timeline_element_set_start (self, g_value_get_uint64 (value));
+ break;
+ case PROP_INPOINT:
+ ges_timeline_element_set_inpoint (self, g_value_get_uint64 (value));
+ break;
+ case PROP_DURATION:
+ ges_timeline_element_set_duration (self, g_value_get_uint64 (value));
+ break;
+ case PROP_PRIORITY:
+ ges_timeline_element_set_priority (self, g_value_get_uint (value));
+ break;
+ case PROP_MAX_DURATION:
+ ges_timeline_element_set_max_duration (self, g_value_get_uint64 (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ }
+}
+
+static void
+ges_group_class_init (GESGroupClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESContainerClass *container_class = GES_CONTAINER_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_group_get_property;
+ object_class->set_property = ges_group_set_property;
+
+ element_class->trim = _trim;
+ element_class->set_duration = _set_duration;
+ element_class->set_inpoint = _set_inpoint;
+ element_class->set_start = _set_start;
+ element_class->set_priority = _set_priority;
+ element_class->paste = _paste;
+
+ /* We override start, inpoint, duration and max-duration from GESTimelineElement
+ * in order to makes sure those fields are not serialized.
+ */
+ /**
+ * GESGroup:start:
+ *
+ * The position of the object in its container (in nanoseconds).
+ */
+ properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
+ "The position in the container", 0, G_MAXUINT64, 0,
+ G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
+
+ /**
+ * GESGroup:in-point:
+ *
+ * The in-point at which this #GESGroup will start outputting data
+ * from its contents (in nanoseconds).
+ *
+ * Ex : an in-point of 5 seconds means that the first outputted buffer will
+ * be the one located 5 seconds in the controlled resource.
+ */
+ properties[PROP_INPOINT] =
+ g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
+ G_MAXUINT64, 0, G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
+
+ /**
+ * GESGroup:duration:
+ *
+ * The duration (in nanoseconds) which will be used in the container
+ */
+ properties[PROP_DURATION] =
+ g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
+ G_MAXUINT64, GST_CLOCK_TIME_NONE,
+ G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
+
+ /**
+ * GESGroup:max-duration:
+ *
+ * The maximum duration (in nanoseconds) of the #GESGroup.
+ */
+ properties[PROP_MAX_DURATION] =
+ g_param_spec_uint64 ("max-duration", "Maximum duration",
+ "The maximum duration of the object", 0, G_MAXUINT64, GST_CLOCK_TIME_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | GES_PARAM_NO_SERIALIZATION);
+
+ /**
+ * GESTGroup:priority:
+ *
+ * The priority of the object.
+ */
+ properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
+ "The priority of the object", 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+
+ container_class->add_child = _add_child;
+ container_class->child_added = _child_added;
+ container_class->child_removed = _child_removed;
+ container_class->ungroup = _ungroup;
+ container_class->group = _group;
+ container_class->grouping_priority = 0;
+}
+
+static void
+ges_group_init (GESGroup * self)
+{
+ self->priv = ges_group_get_instance_private (self);
+
+ self->priv->setting_value = FALSE;
+}
+
+/****************************************************
+ * *
+ * API implementation *
+ * *
+ ****************************************************/
+
+/**
+ * ges_group_new:
+ *
+ * Created a new empty #GESGroup, if you want to group several container
+ * together, it is recommanded to use the #ges_container_group method so the
+ * proper subclass is selected.
+ *
+ * Returns: (transfer floating): The new empty group.
+ */
+GESGroup *
+ges_group_new (void)
+{
+ return g_object_new (GES_TYPE_GROUP, NULL);
+}
--- /dev/null
+/* * 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 GES_GROUP_H
+#define GES_GROUP_H
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include "ges-clip.h"
+#include "ges-container.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_GROUP (ges_group_get_type ())
+#define GES_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_GROUP, GESGroup))
+#define GES_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_GROUP, GESGroupClass))
+#define GES_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_GROUP))
+#define GES_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_GROUP))
+#define GES_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_GROUP, GESGroupClass))
+
+typedef struct _GESGroupPrivate GESGroupPrivate;
+
+struct _GESGroup {
+ GESContainer parent;
+
+ /*< private >*/
+ GESGroupPrivate *priv;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESGroupClass {
+ GESContainerClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_group_get_type (void);
+GES_API
+GESGroup *ges_group_new (void);
+
+G_END_DECLS
+#endif /* _GES_GROUP_H */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesimagesource
+ * @title: GESImageSource
+ * @short_description: outputs the video stream from a media file as a still
+ * image.
+ *
+ * Outputs the video stream from a given file as a still frame. The frame
+ * chosen will be determined by the in-point property on the track element. For
+ * image files, do not set the in-point property.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-image-source.h"
+
+
+struct _GESImageSourcePrivate
+{
+ /* Dummy variable */
+ void *nothing;
+};
+
+enum
+{
+ PROP_0,
+ PROP_URI
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESImageSource, ges_image_source,
+ GES_TYPE_VIDEO_SOURCE);
+static void
+ges_image_source_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESImageSource *uriclip = GES_IMAGE_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, uriclip->uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_image_source_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESImageSource *uriclip = GES_IMAGE_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ uriclip->uri = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_image_source_dispose (GObject * object)
+{
+ GESImageSource *uriclip = GES_IMAGE_SOURCE (object);
+
+ if (uriclip->uri)
+ g_free (uriclip->uri);
+
+ G_OBJECT_CLASS (ges_image_source_parent_class)->dispose (object);
+}
+
+static void
+pad_added_cb (GstElement * timeline, GstPad * pad, GstElement * scale)
+{
+ GstPad *sinkpad;
+ GstPadLinkReturn ret;
+
+ sinkpad = gst_element_get_static_pad (scale, "sink");
+ if (sinkpad) {
+ GST_DEBUG ("got sink pad, trying to link");
+
+ ret = gst_pad_link (pad, sinkpad);
+ gst_object_unref (sinkpad);
+ if (GST_PAD_LINK_SUCCESSFUL (ret)) {
+ GST_DEBUG ("linked ok, returning");
+ return;
+ }
+ }
+
+ GST_DEBUG ("pad failed to link properly");
+}
+
+static GstElement *
+ges_image_source_create_source (GESTrackElement * track_element)
+{
+ GstElement *bin, *source, *scale, *freeze, *iconv;
+ GstPad *src, *target;
+
+ bin = GST_ELEMENT (gst_bin_new ("still-image-bin"));
+ source = gst_element_factory_make ("uridecodebin", NULL);
+ scale = gst_element_factory_make ("videoscale", NULL);
+ freeze = gst_element_factory_make ("imagefreeze", NULL);
+ iconv = gst_element_factory_make ("videoconvert", NULL);
+
+ g_object_set (scale, "add-borders", TRUE, NULL);
+
+ gst_bin_add_many (GST_BIN (bin), source, scale, freeze, iconv, NULL);
+
+ gst_element_link_pads_full (scale, "src", iconv, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+ gst_element_link_pads_full (iconv, "src", freeze, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+
+ /* FIXME: add capsfilter here with sink caps (see 626518) */
+
+ target = gst_element_get_static_pad (freeze, "src");
+
+ src = gst_ghost_pad_new ("src", target);
+ gst_element_add_pad (bin, src);
+ gst_object_unref (target);
+
+ g_object_set (source, "uri", ((GESImageSource *) track_element)->uri, NULL);
+
+ g_signal_connect (G_OBJECT (source), "pad-added",
+ G_CALLBACK (pad_added_cb), scale);
+
+ return bin;
+}
+
+static void
+ges_image_source_class_init (GESImageSourceClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass);
+
+ object_class->get_property = ges_image_source_get_property;
+ object_class->set_property = ges_image_source_set_property;
+ object_class->dispose = ges_image_source_dispose;
+
+ /**
+ * GESImageSource:uri:
+ *
+ * The location of the file/resource to use.
+ */
+ g_object_class_install_property (object_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "uri of the resource",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ GES_TIMELINE_ELEMENT_CLASS (klass)->set_inpoint = NULL;
+ source_class->create_source = ges_image_source_create_source;
+}
+
+static void
+ges_image_source_init (GESImageSource * self)
+{
+ self->priv = ges_image_source_get_instance_private (self);
+}
+
+/**
+ * ges_image_source_new:
+ * @uri: the URI the source should control
+ *
+ * Creates a new #GESImageSource for the provided @uri.
+ *
+ * Returns: (transfer floating): A new #GESImageSource.
+ */
+GESImageSource *
+ges_image_source_new (gchar * uri)
+{
+ return g_object_new (GES_TYPE_IMAGE_SOURCE, "uri", uri, "track-type",
+ GES_TRACK_TYPE_VIDEO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_IMAGE_SOURCE
+#define _GES_IMAGE_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-video-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_IMAGE_SOURCE ges_image_source_get_type()
+
+#define GES_IMAGE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_IMAGE_SOURCE, GESImageSource))
+
+#define GES_IMAGE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_IMAGE_SOURCE, GESImageSourceClass))
+
+#define GES_IS_IMAGE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_IMAGE_SOURCE))
+
+#define GES_IS_IMAGE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_IMAGE_SOURCE))
+
+#define GES_IMAGE_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_IMAGE_SOURCE, GESImageSourceClass))
+
+typedef struct _GESImageSourcePrivate GESImageSourcePrivate;
+
+/**
+ * GESImageSource:
+ */
+struct _GESImageSource {
+ /*< private >*/
+ GESVideoSource parent;
+
+ gchar *uri;
+
+ GESImageSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESImageSourceClass {
+ GESVideoSourceClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_image_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_IMAGE_SOURCE */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 __GES_INTERNAL_H__
+#define __GES_INTERNAL_H__
+#include <gst/gst.h>
+#include <gst/pbutils/encoding-profile.h>
+#include <gio/gio.h>
+
+#include "ges-timeline.h"
+#include "ges-track-element.h"
+#include "ges-timeline-element.h"
+
+#include "ges-asset.h"
+#include "ges-base-xml-formatter.h"
+#include "ges-timeline-tree.h"
+
+G_BEGIN_DECLS
+
+#ifndef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT (_ges_debug ())
+#endif
+
+G_GNUC_INTERNAL
+GstDebugCategory * _ges_debug (void);
+
+/* The first 2 NLE priorities are used for:
+ * 0- The Mixing element
+ * 1- The Gaps
+ */
+#define MIN_NLE_PRIO 2
+#define LAYER_HEIGHT 1000
+
+#define _START(obj) GES_TIMELINE_ELEMENT_START (obj)
+#define _INPOINT(obj) GES_TIMELINE_ELEMENT_INPOINT (obj)
+#define _DURATION(obj) GES_TIMELINE_ELEMENT_DURATION (obj)
+#define _MAXDURATION(obj) GES_TIMELINE_ELEMENT_MAX_DURATION (obj)
+#define _PRIORITY(obj) GES_TIMELINE_ELEMENT_PRIORITY (obj)
+#ifndef _END
+#define _END(obj) (_START (obj) + _DURATION (obj))
+#endif
+#define _set_start0 ges_timeline_element_set_start
+#define _set_inpoint0 ges_timeline_element_set_inpoint
+#define _set_duration0 ges_timeline_element_set_duration
+#define _set_priority0 ges_timeline_element_set_priority
+
+#define GES_TIMELINE_ELEMENT_FORMAT \
+ "s<%p>" \
+ " [ %" GST_TIME_FORMAT \
+ " (%" GST_TIME_FORMAT \
+ ") - %" GST_TIME_FORMAT "(%" GST_TIME_FORMAT") layer: %" G_GINT32_FORMAT "] "
+
+#define GES_TIMELINE_ELEMENT_ARGS(element) \
+ GES_TIMELINE_ELEMENT_NAME(element), element, \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_START(element)), \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_INPOINT(element)), \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_DURATION(element)), \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_MAX_DURATION(element)), \
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY(element)
+
+#define GES_FORMAT GES_TIMELINE_ELEMENT_FORMAT
+#define GES_ARGS GES_TIMELINE_ELEMENT_ARGS
+
+G_GNUC_INTERNAL gboolean
+timeline_ripple_object (GESTimeline *timeline, GESTimelineElement *obj,
+ gint new_layer_priority,
+ GList * layers, GESEdge edge,
+ guint64 position);
+
+G_GNUC_INTERNAL gboolean
+timeline_slide_object (GESTimeline *timeline, GESTrackElement *obj,
+ GList * layers, GESEdge edge, guint64 position);
+
+G_GNUC_INTERNAL gboolean
+timeline_roll_object (GESTimeline *timeline, GESTimelineElement *obj,
+ GList * layers, GESEdge edge, guint64 position);
+
+G_GNUC_INTERNAL gboolean
+timeline_trim_object (GESTimeline *timeline, GESTimelineElement * object,
+ guint32 new_layer_priority, GList * layers, GESEdge edge,
+ guint64 position);
+G_GNUC_INTERNAL gboolean
+ges_timeline_trim_object_simple (GESTimeline * timeline, GESTimelineElement * obj,
+ guint32 new_layer_priority, GList * layers, GESEdge edge,
+ guint64 position, gboolean snapping);
+
+G_GNUC_INTERNAL gboolean
+ges_timeline_move_object_simple (GESTimeline * timeline, GESTimelineElement * object,
+ GList * layers, GESEdge edge, guint64 position);
+
+G_GNUC_INTERNAL gboolean
+timeline_move_object (GESTimeline *timeline, GESTimelineElement * object,
+ guint32 new_layer_priority, GList * layers, GESEdge edge,
+ guint64 position);
+
+G_GNUC_INTERNAL void
+timeline_add_group (GESTimeline *timeline,
+ GESGroup *group);
+G_GNUC_INTERNAL void
+timeline_remove_group (GESTimeline *timeline,
+ GESGroup *group);
+G_GNUC_INTERNAL void
+timeline_emit_group_added (GESTimeline *timeline,
+ GESGroup *group);
+G_GNUC_INTERNAL void
+timeline_emit_group_removed (GESTimeline * timeline,
+ GESGroup * group, GPtrArray * array);
+
+G_GNUC_INTERNAL
+gboolean
+timeline_add_element (GESTimeline *timeline,
+ GESTimelineElement *element);
+G_GNUC_INTERNAL
+gboolean
+timeline_remove_element (GESTimeline *timeline,
+ GESTimelineElement *element);
+
+G_GNUC_INTERNAL
+GNode *
+timeline_get_tree (GESTimeline *timeline);
+
+G_GNUC_INTERNAL
+void
+timeline_update_transition (GESTimeline *timeline);
+
+G_GNUC_INTERNAL
+void
+timeline_fill_gaps (GESTimeline *timeline);
+
+G_GNUC_INTERNAL void
+timeline_create_transitions (GESTimeline * timeline, GESTrackElement * track_element);
+
+G_GNUC_INTERNAL
+void
+track_resort_and_fill_gaps (GESTrack *track);
+
+G_GNUC_INTERNAL
+void
+track_disable_last_gap (GESTrack *track, gboolean disabled);
+
+G_GNUC_INTERNAL void
+ges_asset_cache_init (void);
+
+G_GNUC_INTERNAL void
+ges_asset_cache_deinit (void);
+
+G_GNUC_INTERNAL void
+ges_asset_set_id (GESAsset *asset, const gchar *id);
+
+G_GNUC_INTERNAL void
+ges_asset_cache_put (GESAsset * asset, GTask *task);
+
+G_GNUC_INTERNAL gboolean
+ges_asset_cache_set_loaded(GType extractable_type, const gchar * id, GError *error);
+
+GES_API GESAsset*
+ges_asset_cache_lookup(GType extractable_type, const gchar * id);
+
+GES_API gboolean
+ges_asset_try_proxy (GESAsset *asset, const gchar *new_id);
+
+G_GNUC_INTERNAL gboolean
+ges_asset_request_id_update (GESAsset *asset, gchar **proposed_id,
+ GError *error);
+G_GNUC_INTERNAL gchar *
+ges_effect_assect_id_get_type_and_bindesc (const char *id,
+ GESTrackType *track_type,
+ GError **error);
+
+G_GNUC_INTERNAL void _ges_uri_asset_cleanup (void);
+
+G_GNUC_INTERNAL gboolean _ges_uri_asset_ensure_setup (gpointer uriasset_class);
+
+/* GESExtractable internall methods
+ *
+ * FIXME Check if that should be public later
+ */
+G_GNUC_INTERNAL GType
+ges_extractable_type_get_asset_type (GType type);
+
+G_GNUC_INTERNAL gchar *
+ges_extractable_type_check_id (GType type, const gchar *id, GError **error);
+
+G_GNUC_INTERNAL GParameter *
+ges_extractable_type_get_parameters_from_id (GType type, const gchar *id,
+ guint *n_params);
+G_GNUC_INTERNAL GType
+ges_extractable_get_real_extractable_type_for_id (GType type, const gchar * id);
+
+G_GNUC_INTERNAL gboolean
+ges_extractable_register_metas (GType extractable_type, GESAsset *asset);
+
+/************************************************
+ * *
+ * GESFormatter internal methods *
+ * *
+ ************************************************/
+G_GNUC_INTERNAL void
+ges_formatter_set_project (GESFormatter *formatter,
+ GESProject *project);
+G_GNUC_INTERNAL GESProject *
+ges_formatter_get_project (GESFormatter *formatter);
+G_GNUC_INTERNAL GESAsset *
+_find_formatter_asset_for_id (const gchar *id);
+
+
+
+/************************************************
+ * *
+ * GESProject internal methods *
+ * *
+ ************************************************/
+
+/* FIXME This should probably become public, but we need to make sure it
+ * is the right API before doing so */
+G_GNUC_INTERNAL gboolean ges_project_set_loaded (GESProject * project,
+ GESFormatter *formatter);
+G_GNUC_INTERNAL gchar * ges_project_try_updating_id (GESProject *self,
+ GESAsset *asset,
+ GError *error);
+G_GNUC_INTERNAL void ges_project_add_loading_asset (GESProject *project,
+ GType extractable_type,
+ const gchar *id);
+G_GNUC_INTERNAL gchar* ges_uri_asset_try_update_id (GError *error, GESAsset *wrong_asset);
+/************************************************
+ * *
+ * GESBaseXmlFormatter internal methods *
+ * *
+ ************************************************/
+
+/* FIXME GESBaseXmlFormatter is all internal for now, the API is not stable
+ * fo now, so do not expose it */
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_clip (GESBaseXmlFormatter * self,
+ const gchar *id,
+ const char *asset_id,
+ GType type,
+ GstClockTime start,
+ GstClockTime inpoint,
+ GstClockTime duration,
+ guint layer_prio,
+ GESTrackType track_types,
+ GstStructure *properties,
+ GstStructure * children_properties,
+ const gchar *metadatas,
+ GError **error);
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
+ const gchar * id,
+ GType extractable_type,
+ GstStructure *properties,
+ const gchar *metadatas,
+ const gchar *proxy_id,
+ GError **error);
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_layer (GESBaseXmlFormatter *self,
+ GType extractable_type,
+ guint priority,
+ GstStructure *properties,
+ const gchar *metadatas,
+ GError **error);
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_track (GESBaseXmlFormatter *self,
+ GESTrackType track_type,
+ GstCaps *caps,
+ const gchar *id,
+ GstStructure *properties,
+ const gchar *metadatas,
+ GError **error);
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_encoding_profile(GESBaseXmlFormatter * self,
+ const gchar *type,
+ const gchar *parent,
+ const gchar * name,
+ const gchar * description,
+ GstCaps * format,
+ const gchar * preset,
+ GstStructure * preset_properties,
+ const gchar * preset_name,
+ guint id,
+ guint presence,
+ GstCaps * restriction,
+ guint pass,
+ gboolean variableframerate,
+ GstStructure * properties,
+ gboolean enabled,
+ GError ** error);
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter *self,
+ GType effect_type,
+ const gchar *asset_id,
+ const gchar * track_id,
+ const gchar *timeline_obj_id,
+ GstStructure *children_properties,
+ GstStructure *properties,
+ const gchar *metadatas,
+ GError **error);
+
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_source (GESBaseXmlFormatter *self,
+ const gchar * track_id,
+ GstStructure *children_properties);
+
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_group (GESBaseXmlFormatter *self,
+ const gchar *name,
+ const gchar *properties,
+ const gchar *metadatas);
+
+G_GNUC_INTERNAL void ges_base_xml_formatter_last_group_add_child(GESBaseXmlFormatter *self,
+ const gchar * id,
+ const gchar * name);
+
+G_GNUC_INTERNAL void ges_base_xml_formatter_add_control_binding (GESBaseXmlFormatter * self,
+ const gchar * binding_type,
+ const gchar * source_type,
+ const gchar * property_name,
+ gint mode,
+ const gchar *track_id,
+ GSList * timed_values);
+
+G_GNUC_INTERNAL gboolean set_property_foreach (GQuark field_id,
+ const GValue * value,
+ GObject * object);
+
+G_GNUC_INTERNAL GstElement * get_element_for_encoding_profile (GstEncodingProfile *prof,
+ GstElementFactoryListType type);
+
+/* Function to initialise GES */
+G_GNUC_INTERNAL void _init_standard_transition_assets (void);
+G_GNUC_INTERNAL void _init_formatter_assets (void);
+
+/* Utilities */
+G_GNUC_INTERNAL gint element_start_compare (GESTimelineElement * a,
+ GESTimelineElement * b);
+G_GNUC_INTERNAL gint element_end_compare (GESTimelineElement * a,
+ GESTimelineElement * b);
+G_GNUC_INTERNAL GstElementFactory *
+ges_get_compositor_factory (void);
+
+G_GNUC_INTERNAL void
+ges_base_xml_formatter_set_timeline_properties(GESBaseXmlFormatter * self,
+ GESTimeline *timeline,
+ const gchar *properties,
+ const gchar *metadatas);
+
+/****************************************************
+ * GESContainer *
+ ****************************************************/
+G_GNUC_INTERNAL void _ges_container_sort_children (GESContainer *container);
+G_GNUC_INTERNAL void _ges_container_sort_children_by_end (GESContainer *container);
+G_GNUC_INTERNAL void _ges_container_set_height (GESContainer * container,
+ guint32 height);
+G_GNUC_INTERNAL gint _ges_container_get_priority_offset (GESContainer * container,
+ GESTimelineElement *elem);
+G_GNUC_INTERNAL void _ges_container_set_priority_offset (GESContainer * container,
+ GESTimelineElement *elem,
+ gint32 priority_offset);
+
+
+/****************************************************
+ * GESClip *
+ ****************************************************/
+G_GNUC_INTERNAL void ges_clip_set_layer (GESClip *clip, GESLayer *layer);
+G_GNUC_INTERNAL gboolean ges_clip_is_moving_from_layer (GESClip *clip);
+G_GNUC_INTERNAL void ges_clip_set_moving_from_layer (GESClip *clip, gboolean is_moving);
+G_GNUC_INTERNAL GESTrackElement* ges_clip_create_track_element (GESClip *clip, GESTrackType type);
+G_GNUC_INTERNAL GList* ges_clip_create_track_elements (GESClip *clip, GESTrackType type);
+
+/****************************************************
+ * GESLayer *
+ ****************************************************/
+G_GNUC_INTERNAL gboolean ges_layer_resync_priorities (GESLayer * layer);
+G_GNUC_INTERNAL void layer_set_priority (GESLayer * layer, guint priority, gboolean emit);
+
+/****************************************************
+ * GESTrackElement *
+ ****************************************************/
+#define NLE_OBJECT_TRACK_ELEMENT_QUARK (g_quark_from_string ("nle_object_track_element_quark"))
+G_GNUC_INTERNAL gboolean ges_track_element_set_track (GESTrackElement * object, GESTrack * track);
+G_GNUC_INTERNAL void ges_track_element_copy_properties (GESTimelineElement * element,
+ GESTimelineElement * elementcopy);
+
+G_GNUC_INTERNAL void ges_track_element_copy_bindings (GESTrackElement *element,
+ GESTrackElement *new_element,
+ guint64 position);
+
+G_GNUC_INTERNAL GstElement *ges_source_create_topbin (const gchar * bin_name, GstElement * sub_element, ...);
+G_GNUC_INTERNAL void ges_track_set_caps (GESTrack *track,
+ const GstCaps *caps);
+G_GNUC_INTERNAL GstElement * ges_track_get_composition (GESTrack *track);
+
+
+/*********************************************
+ * GESTrackElement subclasses contructores *
+ ********************************************/
+G_GNUC_INTERNAL GESAudioTestSource * ges_audio_test_source_new (void);
+G_GNUC_INTERNAL GESAudioUriSource * ges_audio_uri_source_new (gchar *uri);
+G_GNUC_INTERNAL GESVideoUriSource * ges_video_uri_source_new (gchar *uri);
+G_GNUC_INTERNAL GESImageSource * ges_image_source_new (gchar *uri);
+G_GNUC_INTERNAL GESTitleSource * ges_title_source_new (void);
+G_GNUC_INTERNAL GESVideoTestSource * ges_video_test_source_new (void);
+
+/****************************************************
+ * GESTimelineElement *
+ ****************************************************/
+typedef enum
+{
+ GES_CLIP_IS_MOVING = (1 << 0),
+ GES_TIMELINE_ELEMENT_SET_SIMPLE = (1 << 1),
+} GESTimelineElementFlags;
+
+G_GNUC_INTERNAL gdouble ges_timeline_element_get_media_duration_factor(GESTimelineElement *self);
+G_GNUC_INTERNAL GESTimelineElement * ges_timeline_element_get_copied_from (GESTimelineElement *self);
+G_GNUC_INTERNAL GESTimelineElementFlags ges_timeline_element_flags (GESTimelineElement *self);
+G_GNUC_INTERNAL void ges_timeline_element_set_flags (GESTimelineElement *self, GESTimelineElementFlags flags);
+
+#define ELEMENT_FLAGS(obj) (ges_timeline_element_flags (GES_TIMELINE_ELEMENT(obj)))
+#define ELEMENT_SET_FLAG(obj,flag) (ges_timeline_element_set_flags(GES_TIMELINE_ELEMENT(obj), (ELEMENT_FLAGS(obj) | (flag))))
+#define ELEMENT_UNSET_FLAG(obj,flag) (ges_timeline_element_set_flags(GES_TIMELINE_ELEMENT(obj), (ELEMENT_FLAGS(obj) & ~(flag))))
+#define ELEMENT_FLAG_IS_SET(obj,flag) ((ELEMENT_FLAGS (obj) & (flag)) == (flag))
+
+/******************************
+ * GESMultiFile internal API *
+ ******************************/
+typedef struct GESMultiFileURI
+{
+ gchar *location;
+ gint start;
+ gint end;
+} GESMultiFileURI;
+
+G_GNUC_INTERNAL GESMultiFileURI * ges_multi_file_uri_new (const gchar * uri);
+
+/************************
+ * Our property masks *
+ ************************/
+#define GES_PARAM_NO_SERIALIZATION (1 << (G_PARAM_USER_SHIFT + 1))
+
+/********************
+ * Gnonlin helpers *
+ ********************/
+
+G_GNUC_INTERNAL gboolean ges_nle_composition_add_object (GstElement *comp, GstElement *object);
+G_GNUC_INTERNAL gboolean ges_nle_composition_remove_object (GstElement *comp, GstElement *object);
+G_GNUC_INTERNAL gboolean ges_nle_object_commit (GstElement * nlesource, gboolean recurse);
+
+G_END_DECLS
+
+#endif /* __GES_INTERNAL_H__ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ * 2011 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:geslayer
+ * @title: GESLayer
+ * @short_description: Non-overlapping sequence of GESClip
+ *
+ * Responsible for the ordering of the various contained Clip(s). A
+ * timeline layer has a "priority" property, which is used to manage the
+ * priorities of individual Clips. Two layers should not have the
+ * same priority within a given timeline.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-layer.h"
+#include "ges.h"
+#include "ges-source-clip.h"
+
+static void ges_meta_container_interface_init
+ (GESMetaContainerInterface * iface);
+
+struct _GESLayerPrivate
+{
+ /*< private > */
+ GList *clips_start; /* The Clips sorted by start and
+ * priority */
+
+ guint32 priority; /* The priority of the layer within the
+ * containing timeline */
+ gboolean auto_transition;
+};
+
+typedef struct
+{
+ GESClip *clip;
+ GESLayer *layer;
+} NewAssetUData;
+
+enum
+{
+ PROP_0,
+ PROP_PRIORITY,
+ PROP_AUTO_TRANSITION,
+ PROP_LAST
+};
+
+enum
+{
+ OBJECT_ADDED,
+ OBJECT_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint ges_layer_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_CODE (GESLayer, ges_layer,
+ G_TYPE_INITIALLY_UNOWNED, G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, NULL)
+ G_ADD_PRIVATE (GESLayer)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
+ ges_meta_container_interface_init));
+
+/* GObject standard vmethods */
+static void
+ges_layer_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESLayer *layer = GES_LAYER (object);
+
+ switch (property_id) {
+ case PROP_PRIORITY:
+ g_value_set_uint (value, layer->priv->priority);
+ break;
+ case PROP_AUTO_TRANSITION:
+ g_value_set_boolean (value, layer->priv->auto_transition);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_layer_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESLayer *layer = GES_LAYER (object);
+
+ switch (property_id) {
+ case PROP_PRIORITY:
+ GST_FIXME ("Deprecated, use ges_timeline_move_layer instead");
+ layer_set_priority (layer, g_value_get_uint (value), FALSE);
+ break;
+ case PROP_AUTO_TRANSITION:
+ ges_layer_set_auto_transition (layer, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_layer_dispose (GObject * object)
+{
+ GESLayer *layer = GES_LAYER (object);
+ GESLayerPrivate *priv = layer->priv;
+
+ GST_DEBUG ("Disposing layer");
+
+ while (priv->clips_start)
+ ges_layer_remove_clip (layer, (GESClip *) priv->clips_start->data);
+
+ G_OBJECT_CLASS (ges_layer_parent_class)->dispose (object);
+}
+
+static gboolean
+_register_metas (GESLayer * layer)
+{
+ ges_meta_container_register_meta_float (GES_META_CONTAINER (layer),
+ GES_META_READ_WRITE, GES_META_VOLUME, 1.0);
+
+ return TRUE;
+}
+
+static void
+ges_meta_container_interface_init (GESMetaContainerInterface * iface)
+{
+
+}
+
+static void
+ges_layer_class_init (GESLayerClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ges_layer_get_property;
+ object_class->set_property = ges_layer_set_property;
+ object_class->dispose = ges_layer_dispose;
+
+ /**
+ * GESLayer:priority:
+ *
+ * The priority of the layer in the #GESTimeline. 0 is the highest
+ * priority. Conceptually, a #GESTimeline is a stack of GESLayers,
+ * and the priority of the layer represents its position in the stack. Two
+ * layers should not have the same priority within a given GESTimeline.
+ *
+ * Note that the timeline needs to be commited (with #ges_timeline_commit)
+ * for the change to be taken into account.
+ *
+ * Deprecated:1.16.0: use #ges_timeline_move_layer instead. This deprecation means
+ * that you will not need to handle layer priorities at all yourself, GES
+ * will make sure there is never 'gaps' between layer priorities.
+ */
+ g_object_class_install_property (object_class, PROP_PRIORITY,
+ g_param_spec_uint ("priority", "Priority",
+ "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
+
+ /**
+ * GESLayer:auto-transition:
+ *
+ * Sets whether transitions are added automagically when clips overlap.
+ */
+ g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
+ g_param_spec_boolean ("auto-transition", "Auto-Transition",
+ "whether the transitions are added", FALSE, G_PARAM_READWRITE));
+
+ /**
+ * GESLayer::clip-added:
+ * @layer: the #GESLayer
+ * @clip: the #GESClip that was added.
+ *
+ * Will be emitted after the clip was added to the layer.
+ */
+ ges_layer_signals[OBJECT_ADDED] =
+ g_signal_new ("clip-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass, object_added),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_CLIP);
+
+ /**
+ * GESLayer::clip-removed:
+ * @layer: the #GESLayer
+ * @clip: the #GESClip that was removed
+ *
+ * Will be emitted after the clip was removed from the layer.
+ */
+ ges_layer_signals[OBJECT_REMOVED] =
+ g_signal_new ("clip-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass,
+ object_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+ 1, GES_TYPE_CLIP);
+}
+
+static void
+ges_layer_init (GESLayer * self)
+{
+ self->priv = ges_layer_get_instance_private (self);
+
+ self->priv->priority = 0;
+ self->priv->auto_transition = FALSE;
+ self->min_nle_priority = MIN_NLE_PRIO;
+ self->max_nle_priority = LAYER_HEIGHT + MIN_NLE_PRIO;
+
+ _register_metas (self);
+}
+
+static gint
+ges_layer_resync_priorities_by_type (GESLayer * layer,
+ gint starting_priority, GType type)
+{
+ GstClockTime next_reset = 0;
+ gint priority = starting_priority, max_priority = priority;
+ GList *tmp;
+ GESTimelineElement *element;
+
+ layer->priv->clips_start =
+ g_list_sort (layer->priv->clips_start,
+ (GCompareFunc) element_start_compare);
+ for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
+
+ element = GES_TIMELINE_ELEMENT (tmp->data);
+
+ if (GES_IS_TRANSITION_CLIP (element)) {
+ /* Blindly set transitions priorities to 0 */
+ _set_priority0 (element, 0);
+ continue;
+ } else if (!g_type_is_a (G_OBJECT_TYPE (element), type))
+ continue;
+
+ if (element->start > next_reset) {
+ priority = starting_priority;
+ next_reset = 0;
+ }
+
+ if (element->start + element->duration > next_reset)
+ next_reset = element->start + element->duration;
+
+ _set_priority0 (element, priority);
+ priority = priority + GES_CONTAINER_HEIGHT (element);
+
+ if (priority > max_priority)
+ max_priority = priority;
+ }
+
+ return max_priority;
+}
+
+/**
+ * ges_layer_resync_priorities:
+ * @layer: a #GESLayer
+ *
+ * Resyncs the priorities of the clips controlled by @layer.
+ */
+gboolean
+ges_layer_resync_priorities (GESLayer * layer)
+{
+ gint min_source_prios;
+
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+
+ GST_INFO_OBJECT (layer, "Resync priorities (prio: %d)",
+ layer->priv->priority);
+
+ min_source_prios = ges_layer_resync_priorities_by_type (layer, 1,
+ GES_TYPE_OPERATION_CLIP);
+
+ ges_layer_resync_priorities_by_type (layer, min_source_prios,
+ GES_TYPE_SOURCE_CLIP);
+
+ return TRUE;
+}
+
+void
+layer_set_priority (GESLayer * layer, guint priority, gboolean emit)
+{
+ GST_DEBUG ("layer:%p, priority:%d", layer, priority);
+
+ if (priority != layer->priv->priority) {
+ layer->priv->priority = priority;
+ layer->min_nle_priority = (priority * LAYER_HEIGHT) + MIN_NLE_PRIO;
+ layer->max_nle_priority = ((priority + 1) * LAYER_HEIGHT) + MIN_NLE_PRIO;
+
+ ges_layer_resync_priorities (layer);
+ }
+
+ if (emit)
+ g_object_notify (G_OBJECT (layer), "priority");
+}
+
+static void
+new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
+{
+ GError *error = NULL;
+
+ GESAsset *asset = ges_asset_request_finish (res, &error);
+
+ GST_DEBUG_OBJECT (udata->layer, "%" GST_PTR_FORMAT " Asset loaded, "
+ "setting its asset", udata->clip);
+
+ if (error) {
+ GESProject *project = udata->layer->timeline ?
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
+ (udata->layer->timeline))) : NULL;
+ if (project) {
+ gchar *possible_id;
+
+ possible_id = ges_project_try_updating_id (project, source, error);
+ if (possible_id) {
+ ges_asset_request_async (ges_asset_get_extractable_type (source),
+ possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, udata);
+ g_free (possible_id);
+ return;
+ }
+ }
+
+ GST_ERROR ("Asset could not be created for uri %s, error: %s",
+ ges_asset_get_id (asset), error->message);
+ } else {
+ GESProject *project = udata->layer->timeline ?
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
+ (udata->layer->timeline))) : NULL;
+ ges_extractable_set_asset (GES_EXTRACTABLE (udata->clip), asset);
+
+ ges_project_add_asset (project, asset);
+
+ /* clip was already ref-sinked when creating udata,
+ * gst_layer_add_clip() creates a new ref as such and
+ * below we unref the ref from udata */
+ ges_layer_add_clip (udata->layer, udata->clip);
+ }
+
+ gst_object_unref (asset);
+ gst_object_unref (udata->clip);
+ g_slice_free (NewAssetUData, udata);
+}
+
+/**
+ * ges_layer_get_duration:
+ * @layer: The #GESLayer to get the duration from
+ *
+ * Lets you retrieve the duration of the layer, which means
+ * the end time of the last clip inside it
+ *
+ * Returns: The duration of a layer
+ */
+GstClockTime
+ges_layer_get_duration (GESLayer * layer)
+{
+ GList *tmp;
+ GstClockTime duration = 0;
+
+ g_return_val_if_fail (GES_IS_LAYER (layer), 0);
+
+ for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
+ duration = MAX (duration, _END (tmp->data));
+ }
+
+ return duration;
+}
+
+static gboolean
+ges_layer_remove_clip_internal (GESLayer * layer, GESClip * clip,
+ gboolean emit_removed)
+{
+ GESLayer *current_layer;
+
+ GST_DEBUG ("layer:%p, clip:%p", layer, clip);
+
+ current_layer = ges_clip_get_layer (clip);
+ if (G_UNLIKELY (current_layer != layer)) {
+ GST_WARNING ("Clip doesn't belong to this layer");
+
+ if (current_layer != NULL)
+ gst_object_unref (current_layer);
+
+ return FALSE;
+ }
+ gst_object_unref (current_layer);
+
+ /* Remove it from our list of controlled objects */
+ layer->priv->clips_start = g_list_remove (layer->priv->clips_start, clip);
+
+ if (emit_removed) {
+ /* emit 'clip-removed' */
+ g_signal_emit (layer, ges_layer_signals[OBJECT_REMOVED], 0, clip);
+ }
+
+ /* inform the clip it's no longer in a layer */
+ ges_clip_set_layer (clip, NULL);
+ /* so neither in a timeline */
+ if (layer->timeline)
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), NULL);
+
+ /* Remove our reference to the clip */
+ gst_object_unref (clip);
+
+ return TRUE;
+}
+
+/* Public methods */
+/**
+ * ges_layer_remove_clip:
+ * @layer: a #GESLayer
+ * @clip: the #GESClip to remove
+ *
+ * Removes the given @clip from the @layer and unparents it.
+ * Unparenting it means the reference owned by @layer on the @clip will be
+ * removed. If you wish to use the @clip after this function, make sure you
+ * call gst_object_ref() before removing it from the @layer.
+ *
+ * Returns: %TRUE if the clip could be removed, %FALSE if the layer does
+ * not want to remove the clip.
+ */
+gboolean
+ges_layer_remove_clip (GESLayer * layer, GESClip * clip)
+{
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+ g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+
+ return ges_layer_remove_clip_internal (layer, clip, TRUE);
+}
+
+/**
+ * ges_layer_set_priority:
+ * @layer: a #GESLayer
+ * @priority: the priority to set
+ *
+ * Sets the layer to the given @priority. See the documentation of the
+ * priority property for more information.
+ *
+ * Deprecated:1.16.0: use #ges_timeline_move_layer instead. This deprecation means
+ * that you will not need to handle layer priorities at all yourself, GES
+ * will make sure there is never 'gaps' between layer priorities.
+ */
+void
+ges_layer_set_priority (GESLayer * layer, guint priority)
+{
+ g_return_if_fail (GES_IS_LAYER (layer));
+
+ GST_FIXME ("Deprecated, use ges_timeline_move_layer instead");
+
+ layer_set_priority (layer, priority, TRUE);
+}
+
+/**
+ * ges_layer_get_auto_transition:
+ * @layer: a #GESLayer
+ *
+ * Gets whether transitions are automatically added when objects
+ * overlap or not.
+ *
+ * Returns: %TRUE if transitions are automatically added, else %FALSE.
+ */
+gboolean
+ges_layer_get_auto_transition (GESLayer * layer)
+{
+ g_return_val_if_fail (GES_IS_LAYER (layer), 0);
+
+ return layer->priv->auto_transition;
+}
+
+/**
+ * ges_layer_set_auto_transition:
+ * @layer: a #GESLayer
+ * @auto_transition: whether the auto_transition is active
+ *
+ * Sets the layer to the given @auto_transition. See the documentation of the
+ * property auto_transition for more information.
+ */
+void
+ges_layer_set_auto_transition (GESLayer * layer, gboolean auto_transition)
+{
+
+ g_return_if_fail (GES_IS_LAYER (layer));
+
+ layer->priv->auto_transition = auto_transition;
+ g_object_notify (G_OBJECT (layer), "auto-transition");
+}
+
+/**
+ * ges_layer_get_priority:
+ * @layer: a #GESLayer
+ *
+ * Get the priority of @layer within the timeline.
+ *
+ * Returns: The priority of the @layer within the timeline.
+ */
+guint
+ges_layer_get_priority (GESLayer * layer)
+{
+ g_return_val_if_fail (GES_IS_LAYER (layer), 0);
+
+ return layer->priv->priority;
+}
+
+/**
+ * ges_layer_get_clips:
+ * @layer: a #GESLayer
+ *
+ * Get the clips this layer contains.
+ *
+ * Returns: (transfer full) (element-type GESClip): a #GList of
+ * clips. The user is responsible for
+ * unreffing the contained objects and freeing the list.
+ */
+
+GList *
+ges_layer_get_clips (GESLayer * layer)
+{
+ GESLayerClass *klass;
+
+ g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
+
+ klass = GES_LAYER_GET_CLASS (layer);
+
+ if (klass->get_objects) {
+ return klass->get_objects (layer);
+ }
+
+ return g_list_sort (g_list_copy_deep (layer->priv->clips_start,
+ (GCopyFunc) gst_object_ref, NULL),
+ (GCompareFunc) element_start_compare);
+}
+
+/**
+ * ges_layer_is_empty:
+ * @layer: The #GESLayer to check
+ *
+ * Convenience method to check if @layer is empty (doesn't contain any clip),
+ * or not.
+ *
+ * Returns: %TRUE if @layer is empty, %FALSE if it already contains at least
+ * one #GESClip
+ */
+gboolean
+ges_layer_is_empty (GESLayer * layer)
+{
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+
+ return (layer->priv->clips_start == NULL);
+}
+
+/**
+ * ges_layer_add_clip:
+ * @layer: a #GESLayer
+ * @clip: (transfer floating): the #GESClip to add.
+ *
+ * Adds the given clip to the layer. Sets the clip's parent, and thus
+ * takes ownership of the clip.
+ *
+ * An clip can only be added to one layer.
+ *
+ * Calling this method will construct and properly set all the media related
+ * elements on @clip. If you need to know when those objects (actually #GESTrackElement)
+ * are constructed, you should connect to the container::child-added signal which
+ * is emited right after those elements are ready to be used.
+ *
+ * Returns: %TRUE if the clip was properly added to the layer, or %FALSE
+ * if the @layer refuses to add the clip.
+ */
+gboolean
+ges_layer_add_clip (GESLayer * layer, GESClip * clip)
+{
+ GESAsset *asset;
+ GESLayerPrivate *priv;
+ GESLayer *current_layer;
+
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+ g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+
+ GST_DEBUG_OBJECT (layer, "adding clip:%p", clip);
+
+ priv = layer->priv;
+ current_layer = ges_clip_get_layer (clip);
+ if (G_UNLIKELY (current_layer)) {
+ GST_WARNING ("Clip %p already belongs to another layer", clip);
+ gst_object_ref_sink (clip);
+ gst_object_unref (current_layer);
+
+ return FALSE;
+ }
+
+ asset = ges_extractable_get_asset (GES_EXTRACTABLE (clip));
+ if (asset == NULL) {
+ gchar *id;
+ NewAssetUData *mudata = g_slice_new (NewAssetUData);
+
+ mudata->clip = gst_object_ref_sink (clip);
+ mudata->layer = layer;
+
+ GST_DEBUG_OBJECT (layer, "%" GST_PTR_FORMAT " as no reference to any "
+ "assets creating a asset... trying sync", clip);
+
+ id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
+ asset = ges_asset_request (G_OBJECT_TYPE (clip), id, NULL);
+ if (asset == NULL) {
+ GESProject *project = layer->timeline ?
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
+ (layer->timeline))) : NULL;
+
+ ges_asset_request_async (G_OBJECT_TYPE (clip),
+ id, NULL, (GAsyncReadyCallback) new_asset_cb, mudata);
+
+ if (project)
+ ges_project_add_loading_asset (project, G_OBJECT_TYPE (clip), id);
+ g_free (id);
+
+ GST_LOG_OBJECT (layer, "Object added async");
+ return TRUE;
+ }
+ g_free (id);
+
+ ges_extractable_set_asset (GES_EXTRACTABLE (clip), asset);
+
+ g_slice_free (NewAssetUData, mudata);
+ gst_clear_object (&asset);
+ } else {
+ gst_object_ref_sink (clip);
+ }
+
+ /* Take a reference to the clip and store it stored by start/priority */
+ priv->clips_start = g_list_insert_sorted (priv->clips_start, clip,
+ (GCompareFunc) element_start_compare);
+
+ /* Inform the clip it's now in this layer */
+ ges_clip_set_layer (clip, layer);
+
+ GST_DEBUG ("current clip priority : %d, Height: %d", _PRIORITY (clip),
+ LAYER_HEIGHT);
+
+ /* Set the priority. */
+ if (_PRIORITY (clip) > LAYER_HEIGHT) {
+ GST_WARNING_OBJECT (layer,
+ "%p is out of the layer space, setting its priority to "
+ "%d, setting it to the maximum priority of the layer: %d", clip,
+ _PRIORITY (clip), LAYER_HEIGHT - 1);
+ _set_priority0 (GES_TIMELINE_ELEMENT (clip), LAYER_HEIGHT - 1);
+ }
+
+ ges_layer_resync_priorities (layer);
+
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip),
+ layer->timeline);
+
+ /* emit 'clip-added' */
+ g_signal_emit (layer, ges_layer_signals[OBJECT_ADDED], 0, clip);
+
+ if (!ELEMENT_FLAG_IS_SET (clip, GES_CLIP_IS_MOVING) && layer->timeline
+ && !timeline_tree_can_move_element (timeline_get_tree (layer->timeline),
+ GES_TIMELINE_ELEMENT (clip),
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip),
+ GES_TIMELINE_ELEMENT_START (clip),
+ GES_TIMELINE_ELEMENT_DURATION (clip), NULL)) {
+ GST_INFO_OBJECT (layer, "Clip %" GES_FORMAT, GES_ARGS (clip));
+ ges_layer_remove_clip_internal (layer, clip, TRUE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * ges_layer_add_asset:
+ * @layer: a #GESLayer
+ * @asset: The asset to add to
+ * @start: The start value to set on the new #GESClip,
+ * if @start == GST_CLOCK_TIME_NONE, it will be set to
+ * the current duration of @layer
+ * @inpoint: The inpoint value to set on the new #GESClip
+ * @duration: The duration value to set on the new #GESClip
+ * @track_types: The #GESTrackType to set on the the new #GESClip
+ *
+ * Creates Clip from asset, adds it to layer and
+ * returns a reference to it.
+ *
+ * Returns: (transfer none): Created #GESClip
+ */
+GESClip *
+ges_layer_add_asset (GESLayer * layer,
+ GESAsset * asset, GstClockTime start, GstClockTime inpoint,
+ GstClockTime duration, GESTrackType track_types)
+{
+ GESClip *clip;
+
+ g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
+ g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+ g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
+ (asset), GES_TYPE_CLIP), NULL);
+
+ GST_DEBUG_OBJECT (layer, "Adding asset %s with: start: %" GST_TIME_FORMAT
+ " inpoint: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
+ " track types: %d (%s)", ges_asset_get_id (asset), GST_TIME_ARGS (start),
+ GST_TIME_ARGS (inpoint), GST_TIME_ARGS (duration), track_types,
+ ges_track_type_name (track_types));
+
+ clip = GES_CLIP (ges_asset_extract (asset, NULL));
+
+ if (!GST_CLOCK_TIME_IS_VALID (start)) {
+ start = ges_layer_get_duration (layer);
+
+ GST_DEBUG_OBJECT (layer,
+ "No start specified, setting it to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (start));
+ }
+
+ _set_start0 (GES_TIMELINE_ELEMENT (clip), start);
+ _set_inpoint0 (GES_TIMELINE_ELEMENT (clip), inpoint);
+ if (track_types != GES_TRACK_TYPE_UNKNOWN)
+ ges_clip_set_supported_formats (clip, track_types);
+
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ _set_duration0 (GES_TIMELINE_ELEMENT (clip), duration);
+ }
+
+ if (!ges_layer_add_clip (layer, clip)) {
+ return NULL;
+ }
+
+ return clip;
+}
+
+/**
+ * ges_layer_new:
+ *
+ * Creates a new #GESLayer.
+ *
+ * Returns: (transfer floating): A new #GESLayer
+ */
+GESLayer *
+ges_layer_new (void)
+{
+ return g_object_new (GES_TYPE_LAYER, NULL);
+}
+
+/**
+ * ges_layer_get_timeline:
+ * @layer: The #GESLayer to get the parent #GESTimeline from
+ *
+ * Get the #GESTimeline in which #GESLayer currently is.
+ *
+ * Returns: (transfer none) (nullable): the #GESTimeline in which #GESLayer
+ * currently is or %NULL if not in any timeline yet.
+ */
+GESTimeline *
+ges_layer_get_timeline (GESLayer * layer)
+{
+ g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
+
+ return layer->timeline;
+}
+
+void
+ges_layer_set_timeline (GESLayer * layer, GESTimeline * timeline)
+{
+ GList *tmp;
+
+ g_return_if_fail (GES_IS_LAYER (layer));
+
+ GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
+
+ for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
+ ges_timeline_element_set_timeline (tmp->data, timeline);
+ }
+
+ layer->timeline = timeline;
+}
+
+/**
+ * ges_layer_get_clips_in_interval:
+ * @layer: a #GESLayer
+ * @start: start of the interval
+ * @end: end of the interval
+ *
+ * Gets the clips which appear between @start and @end on @layer.
+ *
+ * Returns: (transfer full) (element-type GESClip): a #GList of clips intersecting [@start, @end) interval on @layer.
+ */
+GList *
+ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start,
+ GstClockTime end)
+{
+ GList *tmp;
+ GList *intersecting_clips = NULL;
+ GstClockTime clip_start, clip_end;
+ gboolean clip_intersects;
+
+ g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
+
+ layer->priv->clips_start =
+ g_list_sort (layer->priv->clips_start,
+ (GCompareFunc) element_start_compare);
+ for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
+ clip_intersects = FALSE;
+ clip_start = ges_timeline_element_get_start (tmp->data);
+ clip_end = clip_start + ges_timeline_element_get_duration (tmp->data);
+ if (start <= clip_start && clip_start < end)
+ clip_intersects = TRUE;
+ else if (start < clip_end && clip_end <= end)
+ clip_intersects = TRUE;
+ else if (clip_start < start && clip_end > end)
+ clip_intersects = TRUE;
+
+ if (clip_intersects)
+ intersecting_clips =
+ g_list_insert_sorted (intersecting_clips,
+ gst_object_ref (tmp->data), (GCompareFunc) element_start_compare);
+ }
+ return intersecting_clips;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_LAYER
+#define _GES_LAYER
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_LAYER ges_layer_get_type()
+
+#define GES_LAYER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_LAYER, GESLayer))
+
+#define GES_LAYER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_LAYER, GESLayerClass))
+
+#define GES_IS_LAYER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_LAYER))
+
+#define GES_IS_LAYER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_LAYER))
+
+#define GES_LAYER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_LAYER, GESLayerClass))
+
+typedef struct _GESLayerPrivate GESLayerPrivate;
+
+/**
+ * GESLayer:
+ * @timeline: the #GESTimeline where this layer is being used.
+ */
+struct _GESLayer {
+ GInitiallyUnowned parent;
+
+ /*< public >*/
+
+ GESTimeline *timeline;
+
+ /*< protected >*/
+ guint32 min_nle_priority, max_nle_priority;
+
+ GESLayerPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESLayerClass:
+ * @get_objects: method to get the objects contained in the layer
+ *
+ * Subclasses can override the @get_objects if they can provide a more
+ * efficient way of providing the list of contained #GESClip(s).
+ */
+struct _GESLayerClass {
+ /*< private >*/
+ GInitiallyUnownedClass parent_class;
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+ GList *(*get_objects) (GESLayer * layer);
+
+ /*< private >*/
+ /* Signals */
+ void (*object_added) (GESLayer * layer, GESClip * object);
+ void (*object_removed) (GESLayer * layer, GESClip * object);
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_layer_get_type (void);
+
+GES_API
+GESLayer* ges_layer_new (void);
+
+GES_API
+void ges_layer_set_timeline (GESLayer * layer,
+ GESTimeline * timeline);
+
+GES_API GESTimeline *
+ges_layer_get_timeline (GESLayer * layer);
+
+GES_API
+gboolean ges_layer_add_clip (GESLayer * layer,
+ GESClip * clip);
+GES_API
+GESClip * ges_layer_add_asset (GESLayer *layer,
+ GESAsset *asset,
+ GstClockTime start,
+ GstClockTime inpoint,
+ GstClockTime duration,
+ GESTrackType track_types);
+
+GES_API
+gboolean ges_layer_remove_clip (GESLayer * layer,
+ GESClip * clip);
+
+GES_API
+void ges_layer_set_priority (GESLayer * layer,
+ guint priority);
+
+GES_API
+gboolean ges_layer_is_empty (GESLayer * layer);
+
+GES_API
+GList* ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start, GstClockTime end);
+
+GES_API
+guint ges_layer_get_priority (GESLayer * layer);
+
+GES_API
+gboolean ges_layer_get_auto_transition (GESLayer * layer);
+
+GES_API
+void ges_layer_set_auto_transition (GESLayer * layer,
+ gboolean auto_transition);
+
+GES_API
+GList* ges_layer_get_clips (GESLayer * layer);
+GES_API
+GstClockTime ges_layer_get_duration (GESLayer *layer);
+
+G_END_DECLS
+
+#endif /* _GES_LAYER */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2012 Paul Lange <palango@gmx.de>
+ * Copyright (C) <2014> 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "ges-meta-container.h"
+
+/**
+* SECTION: gesmetacontainer
+* @short_description: An interface for storing meta
+*
+* Interface that allows reading and writing meta
+*/
+
+static GQuark ges_meta_key;
+
+G_DEFINE_INTERFACE_WITH_CODE (GESMetaContainer, ges_meta_container,
+ G_TYPE_OBJECT, ges_meta_key =
+ g_quark_from_static_string ("ges-meta-container-data");
+ );
+
+enum
+{
+ NOTIFY_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint _signals[LAST_SIGNAL] = { 0 };
+
+typedef struct RegisteredMeta
+{
+ GType item_type;
+ GESMetaFlag flags;
+} RegisteredMeta;
+
+typedef struct ContainerData
+{
+ GstStructure *structure;
+ GHashTable *static_items;
+} ContainerData;
+
+static void
+ges_meta_container_default_init (GESMetaContainerInterface * iface)
+{
+
+ /**
+ * GESMetaContainer::notify:
+ * @container: a #GESMetaContainer
+ * @prop: the key of the value that changed
+ * @value: the #GValue containing the new value
+ *
+ * The notify signal is used to be notify of changes of values
+ * of some metadatas
+ */
+ _signals[NOTIFY_SIGNAL] =
+ g_signal_new ("notify-meta", G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
+ G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VALUE);
+}
+
+static void
+_free_meta_container_data (ContainerData * data)
+{
+ gst_structure_free (data->structure);
+ g_hash_table_unref (data->static_items);
+
+ g_slice_free (ContainerData, data);
+}
+
+static void
+_free_static_item (RegisteredMeta * item)
+{
+ g_slice_free (RegisteredMeta, item);
+}
+
+static ContainerData *
+_create_container_data (GESMetaContainer * container)
+{
+ ContainerData *data = g_slice_new (ContainerData);
+ data->structure = gst_structure_new_empty ("metadatas");
+ data->static_items = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) (GDestroyNotify) _free_static_item);
+ g_object_set_qdata_full (G_OBJECT (container), ges_meta_key, data,
+ (GDestroyNotify) _free_meta_container_data);
+
+ return data;
+}
+
+static GstStructure *
+_meta_container_get_structure (GESMetaContainer * container)
+{
+ ContainerData *data;
+
+ data = g_object_get_qdata (G_OBJECT (container), ges_meta_key);
+ if (!data)
+ data = _create_container_data (container);
+
+ return data->structure;
+}
+
+typedef struct
+{
+ GESMetaForeachFunc func;
+ const GESMetaContainer *container;
+ gpointer data;
+} MetadataForeachData;
+
+static gboolean
+structure_foreach_wrapper (GQuark field_id, const GValue * value,
+ gpointer user_data)
+{
+ MetadataForeachData *data = (MetadataForeachData *) user_data;
+
+ data->func (data->container, g_quark_to_string (field_id), value, data->data);
+ return TRUE;
+}
+
+static gboolean
+_append_foreach (GQuark field_id, const GValue * value, GESMetaContainer * self)
+{
+ ges_meta_container_set_meta (self, g_quark_to_string (field_id), value);
+
+ return TRUE;
+}
+
+/**
+ * ges_meta_container_foreach:
+ * @container: container to iterate over
+ * @func: (scope call): function to be called for each metadata
+ * @user_data: (closure): user specified data
+ *
+ * Calls the given function for each metadata inside the meta container. Note
+ * that if there is no metadata, the function won't be called at all.
+ */
+void
+ges_meta_container_foreach (GESMetaContainer * container,
+ GESMetaForeachFunc func, gpointer user_data)
+{
+ GstStructure *structure;
+ MetadataForeachData foreach_data;
+
+ g_return_if_fail (GES_IS_META_CONTAINER (container));
+ g_return_if_fail (func != NULL);
+
+ structure = _meta_container_get_structure (container);
+
+ foreach_data.func = func;
+ foreach_data.container = container;
+ foreach_data.data = user_data;
+
+ gst_structure_foreach (structure,
+ (GstStructureForeachFunc) structure_foreach_wrapper, &foreach_data);
+}
+
+/* _can_write_value should have been checked before calling */
+static gboolean
+_register_meta (GESMetaContainer * container, GESMetaFlag flags,
+ const gchar * meta_item, GType type)
+{
+ ContainerData *data;
+ RegisteredMeta *static_item;
+
+ data = g_object_get_qdata (G_OBJECT (container), ges_meta_key);
+ if (!data)
+ data = _create_container_data (container);
+ else if (g_hash_table_lookup (data->static_items, meta_item)) {
+ GST_WARNING_OBJECT (container, "Static meta %s already registered",
+ meta_item);
+
+ return FALSE;
+ }
+
+ static_item = g_slice_new0 (RegisteredMeta);
+ static_item->item_type = type;
+ static_item->flags = flags;
+ g_hash_table_insert (data->static_items, g_strdup (meta_item), static_item);
+
+ return TRUE;
+}
+
+static gboolean
+_set_value (GESMetaContainer * container, const gchar * meta_item,
+ const GValue * value)
+{
+ GstStructure *structure;
+ gchar *val = gst_value_serialize (value);
+
+ if (val == NULL) {
+ GST_WARNING_OBJECT (container, "Could not set value on item: %s",
+ meta_item);
+
+ g_free (val);
+ return FALSE;
+ }
+
+ structure = _meta_container_get_structure (container);
+
+ GST_DEBUG_OBJECT (container, "Setting meta_item %s value: %s::%s",
+ meta_item, G_VALUE_TYPE_NAME (value), val);
+
+ gst_structure_set_value (structure, meta_item, value);
+ g_signal_emit (container, _signals[NOTIFY_SIGNAL], 0, meta_item, value);
+
+ g_free (val);
+ return TRUE;
+}
+
+static gboolean
+_can_write_value (GESMetaContainer * container, const gchar * item_name,
+ GType type)
+{
+ ContainerData *data;
+ RegisteredMeta *static_item = NULL;
+
+ data = g_object_get_qdata (G_OBJECT (container), ges_meta_key);
+ if (!data) {
+ _create_container_data (container);
+ return TRUE;
+ }
+
+ static_item = g_hash_table_lookup (data->static_items, item_name);
+
+ if (static_item == NULL)
+ return TRUE;
+
+ if ((static_item->flags & GES_META_WRITABLE) == FALSE) {
+ GST_WARNING_OBJECT (container, "Can not write %s", item_name);
+ return FALSE;
+ }
+
+ if (static_item->item_type != type) {
+ GST_WARNING_OBJECT (container, "Can not set value of type %s on %s "
+ "its type is: %s", g_type_name (static_item->item_type), item_name,
+ g_type_name (type));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#define CREATE_SETTER(name, value_ctype, value_gtype, setter_name) \
+gboolean \
+ges_meta_container_set_ ## name (GESMetaContainer *container, \
+ const gchar *meta_item, value_ctype value) \
+{ \
+ GValue gval = { 0 }; \
+ gboolean ret; \
+ \
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE); \
+ g_return_val_if_fail (meta_item != NULL, FALSE); \
+ \
+ if (_can_write_value (container, meta_item, value_gtype) == FALSE) \
+ return FALSE; \
+ \
+ g_value_init (&gval, value_gtype); \
+ g_value_set_ ##setter_name (&gval, value); \
+ \
+ ret = _set_value (container, meta_item, &gval); \
+ g_value_unset (&gval); \
+ return ret; \
+}
+
+/**
+ * ges_meta_container_set_boolean:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (boolean, gboolean, G_TYPE_BOOLEAN, boolean);
+
+/**
+ * ges_meta_container_set_int:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (int, gint, G_TYPE_INT, int);
+
+/**
+ * ges_meta_container_set_uint:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (uint, guint, G_TYPE_UINT, uint);
+
+/**
+ * ges_meta_container_set_int64:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (int64, gint64, G_TYPE_INT64, int64);
+
+/**
+ * ges_meta_container_set_uint64:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (uint64, guint64, G_TYPE_UINT64, uint64);
+
+/**
+ * ges_meta_container_set_float:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (float, float, G_TYPE_FLOAT, float);
+
+/**
+ * ges_meta_container_set_double:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (double, double, G_TYPE_DOUBLE, double);
+
+/**
+ * ges_meta_container_set_date:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (date, const GDate *, G_TYPE_DATE, boxed);
+
+/**
+ * ges_meta_container_set_date_time:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+CREATE_SETTER (date_time, const GstDateTime *, GST_TYPE_DATE_TIME, boxed);
+
+/**
+* ges_meta_container_set_string:
+* @container: Target container
+* @meta_item: Name of the meta item to set
+* @value: Value to set
+*
+* Sets the value of a given meta item
+*
+* Return: %TRUE if the meta could be added, %FALSE otherwise
+*/
+CREATE_SETTER (string, const gchar *, G_TYPE_STRING, string);
+
+/**
+ * ges_meta_container_set_meta:
+ * @container: Target container
+ * @meta_item: Name of the meta item to set
+ * @value: (allow-none): Value to set
+ *
+ * Sets the value of a given meta item
+ *
+ * Return: %TRUE if the meta could be added, %FALSE otherwise
+ */
+gboolean
+ges_meta_container_set_meta (GESMetaContainer * container,
+ const gchar * meta_item, const GValue * value)
+{
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (meta_item != NULL, FALSE);
+
+ if (value == NULL) {
+ GstStructure *structure = _meta_container_get_structure (container);
+ gst_structure_remove_field (structure, meta_item);
+
+ g_signal_emit (container, _signals[NOTIFY_SIGNAL], 0, meta_item, value);
+
+ return TRUE;
+ }
+
+ if (_can_write_value (container, meta_item, G_VALUE_TYPE (value)) == FALSE)
+ return FALSE;
+
+ return _set_value (container, meta_item, value);
+}
+
+/**
+ * ges_meta_container_metas_to_string:
+ * @container: a #GESMetaContainer
+ *
+ * Serializes a meta container to a string.
+ *
+ * Returns: (nullable): a newly-allocated string, or NULL in case of an error.
+ * The string must be freed with g_free() when no longer needed.
+ */
+gchar *
+ges_meta_container_metas_to_string (GESMetaContainer * container)
+{
+ GstStructure *structure;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), NULL);
+
+ structure = _meta_container_get_structure (container);
+
+ return gst_structure_to_string (structure);
+}
+
+/**
+ * ges_meta_container_add_metas_from_string:
+ * @container: Target container
+ * @str: a string created with ges_meta_container_metas_to_string()
+ *
+ * Deserializes a meta container.
+ *
+ * Returns: TRUE on success, FALSE if there was an error.
+ */
+gboolean
+ges_meta_container_add_metas_from_string (GESMetaContainer * container,
+ const gchar * str)
+{
+ GstStructure *n_structure;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+
+ n_structure = gst_structure_from_string (str, NULL);
+ if (n_structure == NULL) {
+ GST_WARNING_OBJECT (container, "Could not add metas: %s", str);
+ return FALSE;
+ }
+
+ gst_structure_foreach (n_structure, (GstStructureForeachFunc) _append_foreach,
+ container);
+
+ gst_structure_free (n_structure);
+ return TRUE;
+}
+
+#define CREATE_REGISTER_STATIC(name, value_ctype, value_gtype, setter_name) \
+gboolean \
+ges_meta_container_register_meta_ ## name (GESMetaContainer *container,\
+ GESMetaFlag flags, const gchar *meta_item, value_ctype value) \
+{ \
+ gboolean ret; \
+ GValue gval = { 0 }; \
+ \
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE); \
+ g_return_val_if_fail (meta_item != NULL, FALSE); \
+ \
+ if (!_register_meta (container, flags, meta_item, value_gtype)) \
+ return FALSE; \
+ \
+ g_value_init (&gval, value_gtype); \
+ g_value_set_ ##setter_name (&gval, value); \
+ \
+ ret = _set_value (container, meta_item, &gval); \
+ \
+ g_value_unset (&gval); \
+ return ret; \
+}
+
+/**
+ * ges_meta_container_register_meta_boolean:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (boolean, gboolean, G_TYPE_BOOLEAN, boolean);
+
+/**
+ * ges_meta_container_register_meta_int:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (int, gint, G_TYPE_INT, int);
+
+/**
+ * ges_meta_container_register_meta_uint:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (uint, guint, G_TYPE_UINT, uint);
+
+/**
+ * ges_meta_container_register_meta_int64:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (int64, gint64, G_TYPE_INT64, int64);
+
+/**
+ * ges_meta_container_register_meta_uint64:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (uint64, guint64, G_TYPE_UINT64, uint64);
+
+/**
+ * ges_meta_container_register_meta_float:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+*/
+CREATE_REGISTER_STATIC (float, float, G_TYPE_FLOAT, float);
+
+/**
+ * ges_meta_container_register_meta_double:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (double, double, G_TYPE_DOUBLE, double);
+
+/**
+ * ges_meta_container_register_meta_date:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: (allow-none): Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (date, const GDate *, G_TYPE_DATE, boxed);
+
+/**
+ * ges_meta_container_register_meta_date_time:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: (allow-none): Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (date_time, const GstDateTime *, GST_TYPE_DATE_TIME,
+ boxed);
+
+/**
+ * ges_meta_container_register_meta_string:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: (allow-none): Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the meta could be registered, %FALSE otherwise
+ */
+CREATE_REGISTER_STATIC (string, const gchar *, G_TYPE_STRING, string);
+
+/**
+ * ges_meta_container_register_meta:
+ * @container: Target container
+ * @flags: The #GESMetaFlag to be used
+ * @meta_item: Name of the meta item to set
+ * @value: Value to set
+ *
+ * Sets a static meta on @container. This method lets you define static
+ * metadatas, which means that the type of the registered will be the only
+ * type accepted for this meta on that particular @container.
+ *
+ * Return: %TRUE if the static meta could be added, %FALSE otherwise
+ */
+gboolean
+ges_meta_container_register_meta (GESMetaContainer * container,
+ GESMetaFlag flags, const gchar * meta_item, const GValue * value)
+{
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (meta_item != NULL, FALSE);
+
+ if (!_register_meta (container, flags, meta_item, G_VALUE_TYPE (value)))
+ return FALSE;
+
+ return _set_value (container, meta_item, value);
+}
+
+gboolean
+ges_meta_container_check_meta_registered (GESMetaContainer * container,
+ const gchar * meta_item, GESMetaFlag * flags, GType * type)
+{
+ ContainerData *data;
+ RegisteredMeta *static_item;
+
+ data = g_object_get_qdata (G_OBJECT (container), ges_meta_key);
+ if (!data)
+ return FALSE;
+
+ static_item = g_hash_table_lookup (data->static_items, meta_item);
+ if (static_item == NULL) {
+ GST_WARNING_OBJECT (container, "Static meta %s has not been registered yet",
+ meta_item);
+
+ return FALSE;
+ }
+
+ if (type)
+ *type = static_item->item_type;
+
+ if (flags)
+ *flags = static_item->flags;
+
+ return TRUE;
+}
+
+/* Copied from gsttaglist.c */
+/***** evil macros to get all the *_get_* functions right *****/
+
+#define CREATE_GETTER(name,type) \
+gboolean \
+ges_meta_container_get_ ## name (GESMetaContainer *container, \
+ const gchar *meta_item, type value) \
+{ \
+ GstStructure *structure; \
+ \
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE); \
+ g_return_val_if_fail (meta_item != NULL, FALSE); \
+ g_return_val_if_fail (value != NULL, FALSE); \
+ \
+ structure = _meta_container_get_structure (container); \
+ \
+ return gst_structure_get_ ## name (structure, meta_item, value); \
+}
+
+/**
+ * ges_meta_container_get_boolean:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+CREATE_GETTER (boolean, gboolean *);
+
+/**
+ * ges_meta_container_get_int:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+CREATE_GETTER (int, gint *);
+
+/**
+ * ges_meta_container_get_uint:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+CREATE_GETTER (uint, guint *);
+
+/**
+ * ges_meta_container_get_double:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+CREATE_GETTER (double, gdouble *);
+
+/**
+ * ges_meta_container_get_int64:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns %FALSE if @meta_item
+ * can not be found.
+ */
+gboolean
+ges_meta_container_get_int64 (GESMetaContainer * container,
+ const gchar * meta_item, gint64 * dest)
+{
+ GstStructure *structure;
+ const GValue *value;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (meta_item != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ structure = _meta_container_get_structure (container);
+
+ value = gst_structure_get_value (structure, meta_item);
+ if (!value || G_VALUE_TYPE (value) != G_TYPE_INT64)
+ return FALSE;
+
+ *dest = g_value_get_int64 (value);
+
+ return TRUE;
+}
+
+/**
+ * ges_meta_container_get_uint64:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns %FALSE if @meta_item
+ * can not be found.
+ */
+gboolean
+ges_meta_container_get_uint64 (GESMetaContainer * container,
+ const gchar * meta_item, guint64 * dest)
+{
+ GstStructure *structure;
+ const GValue *value;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (meta_item != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ structure = _meta_container_get_structure (container);
+
+ value = gst_structure_get_value (structure, meta_item);
+ if (!value || G_VALUE_TYPE (value) != G_TYPE_UINT64)
+ return FALSE;
+
+ *dest = g_value_get_uint64 (value);
+
+ return TRUE;
+}
+
+/**
+ * ges_meta_container_get_float:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns %FALSE if @meta_item
+ * can not be found.
+ */
+gboolean
+ges_meta_container_get_float (GESMetaContainer * container,
+ const gchar * meta_item, gfloat * dest)
+{
+ GstStructure *structure;
+ const GValue *value;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (meta_item != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ structure = _meta_container_get_structure (container);
+
+ value = gst_structure_get_value (structure, meta_item);
+ if (!value || G_VALUE_TYPE (value) != G_TYPE_FLOAT)
+ return FALSE;
+
+ *dest = g_value_get_float (value);
+
+ return TRUE;
+}
+
+/**
+ * ges_meta_container_get_string:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+const gchar *
+ges_meta_container_get_string (GESMetaContainer * container,
+ const gchar * meta_item)
+{
+ GstStructure *structure;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (meta_item != NULL, FALSE);
+
+ structure = _meta_container_get_structure (container);
+
+ return gst_structure_get_string (structure, meta_item);
+}
+
+/**
+ * ges_meta_container_get_meta:
+ * @container: Target container
+ * @key: The key name of the meta to retrieve
+ *
+ * Gets the value of a given meta item, returns NULL if @key
+ * can not be found.
+ *
+ * Returns: the #GValue corresponding to the meta with the given @key.
+ */
+const GValue *
+ges_meta_container_get_meta (GESMetaContainer * container, const gchar * key)
+{
+ GstStructure *structure;
+
+ g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ structure = _meta_container_get_structure (container);
+
+ return gst_structure_get_value (structure, key);
+}
+
+/**
+ * ges_meta_container_get_date:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+CREATE_GETTER (date, GDate **);
+
+/**
+ * ges_meta_container_get_date_time:
+ * @container: Target container
+ * @meta_item: Name of the meta item to get
+ * @dest: (out): Destination to which value of meta item will be copied
+ *
+ * Gets the value of a given meta item, returns NULL if @meta_item
+ * can not be found.
+ */
+CREATE_GETTER (date_time, GstDateTime **);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2012 Paul Lange <palango@gmx.de>
+ *
+ * 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 _GES_META_CONTAINER
+#define _GES_META_CONTAINER
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include "ges-enums.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_META_CONTAINER (ges_meta_container_get_type ())
+#define GES_META_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_META_CONTAINER, GESMetaContainer))
+#define GES_IS_META_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_META_CONTAINER))
+#define GES_META_CONTAINER_GET_INTERFACE (inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GES_TYPE_META_CONTAINER, GESMetaContainerInterface))
+
+/**
+ * GES_META_FORMATTER_NAME:
+ *
+ * Name of a formatter it is used as ID of Formater assets (string)
+ *
+ * The name of the formatter
+ */
+#define GES_META_FORMATTER_NAME "name"
+
+/**
+ * GES_META_DESCRIPTION:
+ *
+ * The description of an object, can be used in various context (string)
+ *
+ * The description
+ */
+#define GES_META_DESCRIPTION "description"
+
+/**
+ * GES_META_FORMATTER_MIMETYPE:
+ *
+ * Mimetype used for the file produced by a formatter (string)
+ *
+ * The mime type
+ */
+#define GES_META_FORMATTER_MIMETYPE "mimetype"
+
+/**
+ * GES_META_FORMATTER_EXTENSION:
+ *
+ * The extension of the files produced by a formatter (string)
+ */
+#define GES_META_FORMATTER_EXTENSION "extension"
+
+/**
+ * GES_META_FORMATTER_VERSION:
+ *
+ * The version of a formatter (double)
+ *
+ * The formatter version
+ */
+#define GES_META_FORMATTER_VERSION "version"
+
+/**
+ * GES_META_FORMATTER_RANK:
+ *
+ * The rank of a formatter (GstRank)
+ *
+ * The rank of a formatter
+ */
+#define GES_META_FORMATTER_RANK "rank"
+
+/**
+ * GES_META_VOLUME:
+ *
+ * The volume, can be used for audio track or layers
+ *
+ * The volume for a track or a layer, it is register as a float
+ */
+#define GES_META_VOLUME "volume"
+
+/**
+ * GES_META_VOLUME_DEFAULT:
+ *
+ * The default volume
+ *
+ * The default volume for a track or a layer as a float
+ */
+#define GES_META_VOLUME_DEFAULT 1.0
+
+/**
+ * GES_META_FORMAT_VERSION:
+ *
+ * The version of the format in which a project is serialized
+ */
+#define GES_META_FORMAT_VERSION "format-version"
+
+typedef struct _GESMetaContainer GESMetaContainer;
+typedef struct _GESMetaContainerInterface GESMetaContainerInterface;
+
+struct _GESMetaContainerInterface {
+ GTypeInterface parent_iface;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_meta_container_get_type (void);
+
+GES_API gboolean
+ges_meta_container_set_boolean (GESMetaContainer *container,
+ const gchar* meta_item,
+ gboolean value);
+
+GES_API gboolean
+ges_meta_container_set_int (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint value);
+
+GES_API gboolean
+ges_meta_container_set_uint (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint value);
+
+GES_API gboolean
+ges_meta_container_set_int64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint64 value);
+
+GES_API gboolean
+ges_meta_container_set_uint64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint64 value);
+
+GES_API gboolean
+ges_meta_container_set_float (GESMetaContainer *container,
+ const gchar* meta_item,
+ gfloat value);
+
+GES_API gboolean
+ges_meta_container_set_double (GESMetaContainer *container,
+ const gchar* meta_item,
+ gdouble value);
+
+GES_API gboolean
+ges_meta_container_set_date (GESMetaContainer *container,
+ const gchar* meta_item,
+ const GDate* value);
+
+GES_API gboolean
+ges_meta_container_set_date_time (GESMetaContainer *container,
+ const gchar* meta_item,
+ const GstDateTime* value);
+
+GES_API gboolean
+ges_meta_container_set_string (GESMetaContainer *container,
+ const gchar* meta_item,
+ const gchar* value);
+
+GES_API gboolean
+ges_meta_container_set_meta (GESMetaContainer * container,
+ const gchar* meta_item,
+ const GValue *value);
+
+GES_API gboolean
+ges_meta_container_register_meta_boolean (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gboolean value);
+
+GES_API gboolean
+ges_meta_container_register_meta_int (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gint value);
+
+GES_API gboolean
+ges_meta_container_register_meta_uint (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ guint value);
+
+GES_API gboolean
+ges_meta_container_register_meta_int64 (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gint64 value);
+
+GES_API gboolean
+ges_meta_container_register_meta_uint64 (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ guint64 value);
+
+GES_API gboolean
+ges_meta_container_register_meta_float (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gfloat value);
+
+GES_API gboolean
+ges_meta_container_register_meta_double (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ gdouble value);
+
+GES_API gboolean
+ges_meta_container_register_meta_date (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const GDate* value);
+
+GES_API gboolean
+ges_meta_container_register_meta_date_time (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const GstDateTime* value);
+
+GES_API gboolean
+ges_meta_container_register_meta_string (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const gchar* value);
+
+GES_API gboolean
+ges_meta_container_register_meta (GESMetaContainer *container,
+ GESMetaFlag flags,
+ const gchar* meta_item,
+ const GValue * value);
+
+GES_API gboolean
+ges_meta_container_check_meta_registered (GESMetaContainer *container,
+ const gchar * meta_item,
+ GESMetaFlag * flags,
+ GType * type);
+
+GES_API gboolean
+ges_meta_container_get_boolean (GESMetaContainer *container,
+ const gchar* meta_item,
+ gboolean* dest);
+
+GES_API gboolean
+ges_meta_container_get_int (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint* dest);
+
+GES_API gboolean
+ges_meta_container_get_uint (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint* dest);
+
+GES_API gboolean
+ges_meta_container_get_int64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ gint64* dest);
+
+GES_API gboolean
+ges_meta_container_get_uint64 (GESMetaContainer *container,
+ const gchar* meta_item,
+ guint64* dest);
+
+GES_API gboolean
+ges_meta_container_get_float (GESMetaContainer *container,
+ const gchar* meta_item,
+ gfloat* dest);
+
+GES_API gboolean
+ges_meta_container_get_double (GESMetaContainer *container,
+ const gchar* meta_item,
+ gdouble* dest);
+
+GES_API gboolean
+ges_meta_container_get_date (GESMetaContainer *container,
+ const gchar* meta_item,
+ GDate** dest);
+
+GES_API gboolean
+ges_meta_container_get_date_time (GESMetaContainer *container,
+ const gchar* meta_item,
+ GstDateTime** dest);
+
+GES_API const gchar *
+ges_meta_container_get_string (GESMetaContainer * container,
+ const gchar * meta_item);
+
+GES_API const GValue *
+ges_meta_container_get_meta (GESMetaContainer * container,
+ const gchar * key);
+
+typedef void
+(*GESMetaForeachFunc) (const GESMetaContainer *container,
+ const gchar *key,
+ const GValue *value,
+ gpointer user_data);
+
+GES_API void
+ges_meta_container_foreach (GESMetaContainer *container,
+ GESMetaForeachFunc func,
+ gpointer user_data);
+
+GES_API gchar *
+ges_meta_container_metas_to_string (GESMetaContainer *container);
+
+GES_API gboolean
+ges_meta_container_add_metas_from_string (GESMetaContainer *container,
+ const gchar *str);
+
+G_END_DECLS
+#endif /* _GES_META_CONTAINER */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2013 Lubosz Sarnecki <lubosz@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesmultifilesource
+ * @title: GESMultiFileSource
+ * @short_description: outputs the video stream from a sequence of images.
+ *
+ * Outputs the video stream from a given image sequence. The start frame
+ * chosen will be determined by the in-point property on the track element.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-multi-file-source.h"
+#include "ges-extractable.h"
+#include "ges-uri-asset.h"
+#include "ges-internal.h"
+
+/* Extractable interface implementation */
+
+static gchar *
+ges_extractable_check_id (GType type, const gchar * id, GError ** error)
+{
+ return g_strdup (id);
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->check_id = ges_extractable_check_id;
+}
+
+struct _GESMultiFileSourcePrivate
+{
+ /* Dummy variable */
+ void *nothing;
+};
+
+enum
+{
+ PROP_0,
+ PROP_URI
+};
+
+G_DEFINE_TYPE_WITH_CODE (GESMultiFileSource, ges_multi_file_source,
+ GES_TYPE_VIDEO_SOURCE, G_ADD_PRIVATE (GESMultiFileSource)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+static void
+ges_multi_file_source_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESMultiFileSource *uriclip = GES_MULTI_FILE_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, uriclip->uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_multi_file_source_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESMultiFileSource *uriclip = GES_MULTI_FILE_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ uriclip->uri = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_multi_file_source_dispose (GObject * object)
+{
+ GESMultiFileSource *uriclip = GES_MULTI_FILE_SOURCE (object);
+
+ if (uriclip->uri)
+ g_free (uriclip->uri);
+
+ G_OBJECT_CLASS (ges_multi_file_source_parent_class)->dispose (object);
+}
+
+static void
+pad_added_cb (GstElement * decodebin, GstPad * pad, GstElement * bin)
+{
+ GstPad *srcpad;
+
+ srcpad = gst_ghost_pad_new ("src", pad);
+
+ gst_pad_set_active (srcpad, TRUE);
+ gst_element_add_pad (bin, srcpad);
+}
+
+/**
+ * ges_multi_file_uri_new: (skip)
+ *
+ * Reads start/stop index and location from a multifile uri.
+ *
+ */
+GESMultiFileURI *
+ges_multi_file_uri_new (const gchar * uri)
+{
+ gchar *colon = NULL;
+ gchar *at = NULL;
+ gchar *indices;
+ int charpos;
+ GESMultiFileURI *uri_data;
+ const int prefix_size = strlen (GES_MULTI_FILE_URI_PREFIX);
+
+ uri_data = malloc (sizeof (GESMultiFileURI));
+
+ uri_data->start = 0;
+ uri_data->end = -1;
+
+ at = strchr (uri, '@');
+ if (at != NULL) {
+ charpos = (int) (at - uri);
+ indices = g_strdup_printf ("%.*s", charpos, uri);
+ indices = &indices[prefix_size];
+ colon = strchr (indices, ':');
+ if (colon != NULL) {
+ charpos = (int) (colon - indices);
+ uri_data->end = atoi (colon + 1);
+ uri_data->start = atoi (g_strdup_printf ("%.*s", charpos, indices));
+ GST_DEBUG ("indices start: %d end %d\n", uri_data->start, uri_data->end);
+ } else {
+ GST_ERROR
+ ("Malformated multifile uri. You are using '@' and are missing ':'");
+ }
+ uri_data->location = at + 1;
+ } else {
+ uri_data->location = g_strdup (&uri[prefix_size]);
+ }
+ GST_DEBUG ("location: %s\n", uri_data->location);
+
+ return uri_data;
+}
+
+static GstElement *
+ges_multi_file_source_create_source (GESTrackElement * track_element)
+{
+ GESMultiFileSource *self;
+ GstElement *bin, *src, *decodebin;
+ GstCaps *disc_caps;
+ GstDiscovererStreamInfo *stream_info;
+ GValue fps = G_VALUE_INIT;
+ GstCaps *caps;
+ GESUriSourceAsset *asset;
+ GESMultiFileURI *uri_data;
+
+ self = (GESMultiFileSource *) track_element;
+
+ asset =
+ GES_URI_SOURCE_ASSET (ges_extractable_get_asset (GES_EXTRACTABLE (self)));
+
+ if (asset != NULL) {
+ stream_info = ges_uri_source_asset_get_stream_info (asset);
+ g_assert (stream_info);
+ disc_caps = gst_discoverer_stream_info_get_caps (stream_info);
+ caps = gst_caps_copy (disc_caps);
+ GST_DEBUG_OBJECT (disc_caps, "Got some nice caps");
+ gst_object_unref (stream_info);
+ gst_caps_unref (disc_caps);
+ } else {
+ caps = gst_caps_new_empty ();
+ GST_WARNING ("Could not extract asset.");
+ }
+
+ g_value_init (&fps, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&fps, 25, 1);
+ gst_caps_set_value (caps, "framerate", &fps);
+
+ bin = GST_ELEMENT (gst_bin_new ("multi-image-bin"));
+ src = gst_element_factory_make ("multifilesrc", NULL);
+
+ uri_data = ges_multi_file_uri_new (self->uri);
+ g_object_set (src, "start-index", uri_data->start, "stop-index",
+ uri_data->end, "caps", caps, "location", uri_data->location, NULL);
+ g_free (uri_data);
+
+ decodebin = gst_element_factory_make ("decodebin", NULL);
+
+ gst_bin_add_many (GST_BIN (bin), src, decodebin, NULL);
+ gst_element_link_pads_full (src, "src", decodebin, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+
+ g_signal_connect (G_OBJECT (decodebin), "pad-added",
+ G_CALLBACK (pad_added_cb), bin);
+
+ return bin;
+}
+
+static void
+ges_multi_file_source_class_init (GESMultiFileSourceClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass);
+
+ object_class->get_property = ges_multi_file_source_get_property;
+ object_class->set_property = ges_multi_file_source_set_property;
+ object_class->dispose = ges_multi_file_source_dispose;
+
+ /**
+ * GESMultiFileSource:uri:
+ *
+ * The uri of the file/resource to use. You can set a start index,
+ * a stop index and a sequence pattern.
+ * The format is <multifile://start:stop\@location-pattern>.
+ * The pattern uses printf string formating.
+ *
+ * Example uris:
+ *
+ * multifile:///home/you/image\%03d.jpg
+ *
+ * multifile://20:50@/home/you/sequence/\%04d.png
+ *
+ */
+ g_object_class_install_property (object_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "multifile uri",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ source_class->create_source = ges_multi_file_source_create_source;
+}
+
+static void
+ges_multi_file_source_init (GESMultiFileSource * self)
+{
+ self->priv = ges_multi_file_source_get_instance_private (self);
+}
+
+/**
+ * ges_multi_file_source_new:
+ * @uri: the URI the source should control
+ *
+ * Creates a new #GESMultiFileSource for the provided @uri.
+ *
+ * Returns: (transfer floating): A new #GESMultiFileSource.
+ */
+GESMultiFileSource *
+ges_multi_file_source_new (gchar * uri)
+{
+ return g_object_new (GES_TYPE_MULTI_FILE_SOURCE, "uri", uri,
+ "track-type", GES_TRACK_TYPE_VIDEO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2013 Lubosz Sarnecki <lubosz@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_MULTI_FILE_SOURCE
+#define _GES_MULTI_FILE_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-video-source.h>
+
+G_BEGIN_DECLS
+#define GES_TYPE_MULTI_FILE_SOURCE ges_multi_file_source_get_type()
+#define GES_MULTI_FILE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_MULTI_FILE_SOURCE, GESMultiFileSource))
+#define GES_MULTI_FILE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_MULTI_FILE_SOURCE, GESMultiFileSourceClass))
+#define GES_IS_MULTI_FILE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_MULTI_FILE_SOURCE))
+#define GES_IS_MULTI_FILE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_MULTI_FILE_SOURCE))
+#define GES_MULTI_FILE_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_MULTI_FILE_SOURCE, GESMultiFileSourceClass))
+typedef struct _GESMultiFileSourcePrivate GESMultiFileSourcePrivate;
+
+/**
+ * GESMultiFileSource:
+ */
+struct _GESMultiFileSource
+{
+ /*< private > */
+ GESVideoSource parent;
+
+ gchar *uri;
+
+ GESMultiFileSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESMultiFileSourceClass
+{
+ GESVideoSourceClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_multi_file_source_get_type (void);
+
+GES_API
+GESMultiFileSource *ges_multi_file_source_new (gchar * uri);
+
+#define GES_MULTI_FILE_URI_PREFIX "multifile://"
+
+G_END_DECLS
+#endif /* _GES_MULTI_FILE_SOURCE */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesoperationclip
+ * @title: GESOperationClip
+ * @short_description: Base Class for operations in a GESLayer
+ *
+ * Operations are any kind of object that both outputs AND consumes data.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges.h"
+#include "ges-internal.h"
+#include "ges-operation-clip.h"
+
+struct _GESOperationClipPrivate
+{
+ void *nada;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESOperationClip, ges_operation_clip,
+ GES_TYPE_CLIP);
+
+static void
+ges_operation_clip_class_init (GESOperationClipClass * klass)
+{
+}
+
+static void
+ges_operation_clip_init (GESOperationClip * self)
+{
+ self->priv = ges_operation_clip_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_OPERATION_CLIP
+#define _GES_OPERATION_CLIP
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-clip.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_OPERATION_CLIP ges_operation_clip_get_type()
+
+#define GES_OPERATION_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_OPERATION_CLIP, GESOperationClip))
+
+#define GES_OPERATION_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_OPERATION_CLIP, GESOperationClipClass))
+
+#define GES_IS_OPERATION_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_OPERATION_CLIP))
+
+#define GES_IS_OPERATION_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_OPERATION_CLIP))
+
+#define GES_OPERATION_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_OPERATION_CLIP, GESOperationClipClass))
+
+typedef struct _GESOperationClipPrivate GESOperationClipPrivate;
+
+/**
+ * GESOperationClip:
+ *
+ * The GESOperationClip subclass. Subclasses can access these fields.
+ */
+struct _GESOperationClip {
+ /*< private >*/
+ GESClip parent;
+
+ GESOperationClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESOperationClipClass:
+ */
+struct _GESOperationClipClass {
+ /*< private >*/
+ GESClipClass parent_class;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_operation_clip_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_OPERATION_CLIP */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesoperation
+ * @title: GESOperation
+ * @short_description: Base Class for effects and overlays
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-operation.h"
+
+struct _GESOperationPrivate
+{
+ /* Dummy variable */
+ void *nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESOperation, ges_operation,
+ GES_TYPE_TRACK_ELEMENT);
+
+static void
+ges_operation_class_init (GESOperationClass * klass)
+{
+ GESTrackElementClass *track_class = GES_TRACK_ELEMENT_CLASS (klass);
+
+ track_class->nleobject_factorytype = "nleoperation";
+}
+
+static void
+ges_operation_init (GESOperation * self)
+{
+ self->priv = ges_operation_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_OPERATION
+#define _GES_OPERATION
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-track-element.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_OPERATION ges_operation_get_type()
+
+#define GES_OPERATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_OPERATION, GESOperation))
+
+#define GES_OPERATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_OPERATION, GESOperationClass))
+
+#define GES_IS_OPERATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_OPERATION))
+
+#define GES_IS_OPERATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_OPERATION))
+
+#define GES_OPERATION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_OPERATION, GESOperationClass))
+
+typedef struct _GESOperationPrivate GESOperationPrivate;
+
+/**
+ * GESOperation:
+ *
+ * Base class for overlays, transitions, and effects
+ */
+
+struct _GESOperation {
+ /*< private >*/
+ GESTrackElement parent;
+
+ GESOperationPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESOperationClass:
+ */
+
+struct _GESOperationClass {
+ /*< private >*/
+ GESTrackElementClass parent_class;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_operation_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_OPERATION */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesoverlayclip
+ * @title: GESOverlayClip
+ * @short_description: Base Class for overlays in a GESLayer
+ *
+ * Overlays are objects which modify the underlying layer(s).
+ *
+ * Examples of overlays include text, image watermarks, or audio dubbing.
+ *
+ * Transitions, which change from one source to another over time, are
+ * not considered overlays.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-operation-clip.h"
+#include "ges-overlay-clip.h"
+
+struct _GESOverlayClipPrivate
+{
+ /* Dummy variable */
+ void *nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESOverlayClip, ges_overlay_clip,
+ GES_TYPE_OPERATION_CLIP);
+
+static void
+ges_overlay_clip_class_init (GESOverlayClipClass * klass)
+{
+}
+
+static void
+ges_overlay_clip_init (GESOverlayClip * self)
+{
+ self->priv = ges_overlay_clip_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_OVERLAY_CLIP
+#define _GES_OVERLAY_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-operation-clip.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_OVERLAY_CLIP ges_overlay_clip_get_type()
+
+#define GES_OVERLAY_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_OVERLAY_CLIP, GESOverlayClip))
+
+#define GES_OVERLAY_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_OVERLAY_CLIP, GESOverlayClipClass))
+
+#define GES_IS_OVERLAY_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_OVERLAY_CLIP))
+
+#define GES_IS_OVERLAY_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_OVERLAY_CLIP))
+
+#define GES_OVERLAY_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_OVERLAY_CLIP, GESOverlayClipClass))
+
+typedef struct _GESOverlayClipPrivate GESOverlayClipPrivate;
+
+/**
+ * GESOverlayClip:
+ */
+
+struct _GESOverlayClip {
+ /*< private >*/
+ GESOperationClip parent;
+
+ GESOverlayClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESOverlayClipClass:
+ * @parent_class: parent class
+ */
+
+struct _GESOverlayClipClass {
+ GESOperationClipClass parent_class;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_overlay_clip_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_OVERLAY_CLIP */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gespipeline
+ * @title: GESPipeline
+ * @short_description: Convenience GstPipeline for editing.
+ *
+ * #GESPipeline allows developers to view and render #GESTimeline
+ * in a simple fashion.
+ * Its usage is inspired by the 'playbin' element from gst-plugins-base.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/videooverlay.h>
+#include <stdio.h>
+
+#include "ges-internal.h"
+#include "ges-pipeline.h"
+#include "ges-screenshot.h"
+#include "ges-audio-track.h"
+#include "ges-video-track.h"
+
+GST_DEBUG_CATEGORY_STATIC (ges_pipeline_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT ges_pipeline_debug
+
+#define DEFAULT_TIMELINE_MODE GES_PIPELINE_MODE_PREVIEW
+#define IN_RENDERING_MODE(timeline) ((timeline->priv->mode) & (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER))
+#define CHECK_THREAD(pipeline) g_assert(pipeline->priv->valid_thread == g_thread_self())
+
+/* Structure corresponding to a timeline - sink link */
+
+typedef struct
+{
+ GESTrack *track;
+ GstElement *tee;
+ GstPad *srcpad; /* Timeline source pad */
+ GstPad *playsinkpad;
+ GstPad *encodebinpad;
+} OutputChain;
+
+
+struct _GESPipelinePrivate
+{
+ GESTimeline *timeline;
+ GstElement *playsink;
+ GstElement *encodebin;
+ /* Note : urisink is only created when a URI has been provided */
+ GstElement *urisink;
+
+ GESPipelineFlags mode;
+
+ GMutex dyn_mutex;
+ GList *chains;
+ GList *not_rendered_tracks;
+
+ GstEncodingProfile *profile;
+
+ GThread *valid_thread;
+};
+
+enum
+{
+ PROP_0,
+ PROP_AUDIO_SINK,
+ PROP_VIDEO_SINK,
+ PROP_TIMELINE,
+ PROP_MODE,
+ PROP_AUDIO_FILTER,
+ PROP_VIDEO_FILTER,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+static GstStateChangeReturn ges_pipeline_change_state (GstElement *
+ element, GstStateChange transition);
+
+static OutputChain *get_output_chain_for_track (GESPipeline * self,
+ GESTrack * track);
+static OutputChain *new_output_chain_for_track (GESPipeline * self,
+ GESTrack * track);
+static void _link_track (GESPipeline * self, GESTrack * track);
+static void _unlink_track (GESPipeline * self, GESTrack * track);
+
+/****************************************************
+ * Video Overlay vmethods implementation *
+ ****************************************************/
+static void
+_overlay_expose (GstVideoOverlay * overlay)
+{
+ GESPipeline *pipeline = GES_PIPELINE (overlay);
+
+ gst_video_overlay_expose (GST_VIDEO_OVERLAY (pipeline->priv->playsink));
+}
+
+static void
+_overlay_handle_events (GstVideoOverlay * overlay, gboolean handle_events)
+{
+ GESPipeline *pipeline = GES_PIPELINE (overlay);
+
+ gst_video_overlay_handle_events (GST_VIDEO_OVERLAY (pipeline->priv->playsink),
+ handle_events);
+}
+
+static void
+_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
+ gint y, gint width, gint height)
+{
+ GESPipeline *pipeline = GES_PIPELINE (overlay);
+
+ gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (pipeline->priv->
+ playsink), x, y, width, height);
+}
+
+static void
+_overlay_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
+{
+ GESPipeline *pipeline = GES_PIPELINE (overlay);
+
+ gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (pipeline->
+ priv->playsink), handle);
+}
+
+static void
+video_overlay_init (gpointer g_iface, gpointer g_iface_data)
+{
+ GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
+
+ iface->expose = _overlay_expose;
+ iface->handle_events = _overlay_handle_events;
+ iface->set_render_rectangle = _overlay_set_render_rectangle;
+ iface->set_window_handle = _overlay_set_window_handle;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESPipeline, ges_pipeline,
+ GST_TYPE_PIPELINE, G_ADD_PRIVATE (GESPipeline)
+ G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, video_overlay_init));
+
+static void
+ges_pipeline_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESPipeline *self = GES_PIPELINE (object);
+
+ switch (property_id) {
+ case PROP_AUDIO_SINK:
+ g_object_get_property (G_OBJECT (self->priv->playsink), "audio-sink",
+ value);
+ break;
+ case PROP_VIDEO_SINK:
+ g_object_get_property (G_OBJECT (self->priv->playsink), "video-sink",
+ value);
+ break;
+ case PROP_TIMELINE:
+ g_value_set_object (value, self->priv->timeline);
+ break;
+ case PROP_MODE:
+ g_value_set_flags (value, self->priv->mode);
+ break;
+ case PROP_AUDIO_FILTER:
+ g_object_get_property (G_OBJECT (self->priv->playsink), "audio-filter",
+ value);
+ break;
+ case PROP_VIDEO_FILTER:
+ g_object_get_property (G_OBJECT (self->priv->playsink), "video-filter",
+ value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_pipeline_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESPipeline *self = GES_PIPELINE (object);
+
+ switch (property_id) {
+ case PROP_AUDIO_SINK:
+ g_object_set_property (G_OBJECT (self->priv->playsink), "audio-sink",
+ value);
+ break;
+ case PROP_VIDEO_SINK:
+ g_object_set_property (G_OBJECT (self->priv->playsink), "video-sink",
+ value);
+ break;
+ case PROP_TIMELINE:
+ ges_pipeline_set_timeline (GES_PIPELINE (object),
+ g_value_get_object (value));
+ break;
+ case PROP_MODE:
+ ges_pipeline_set_mode (GES_PIPELINE (object), g_value_get_flags (value));
+ break;
+ case PROP_AUDIO_FILTER:
+ g_object_set (self->priv->playsink, "audio-filter",
+ GST_ELEMENT (g_value_get_object (value)), NULL);
+ break;
+ case PROP_VIDEO_FILTER:
+ g_object_set (self->priv->playsink, "video-filter",
+ GST_ELEMENT (g_value_get_object (value)), NULL);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+_timeline_track_added_cb (GESTimeline * timeline, GESTrack * track,
+ GESPipeline * pipeline)
+{
+ track_disable_last_gap (track,
+ ! !(pipeline->priv->mode & (GES_PIPELINE_MODE_RENDER |
+ GES_PIPELINE_MODE_SMART_RENDER)));
+ _link_track (pipeline, track);
+}
+
+static void
+_timeline_track_removed_cb (GESTimeline * timeline, GESTrack * track,
+ GESPipeline * pipeline)
+{
+ _unlink_track (pipeline, track);
+}
+
+static void
+ges_pipeline_dispose (GObject * object)
+{
+ GESPipeline *self = GES_PIPELINE (object);
+
+ if (self->priv->playsink) {
+ if (self->priv->mode & (GES_PIPELINE_MODE_PREVIEW))
+ gst_bin_remove (GST_BIN (object), self->priv->playsink);
+ else
+ gst_object_unref (self->priv->playsink);
+ self->priv->playsink = NULL;
+ }
+
+ if (self->priv->encodebin) {
+ if (self->priv->mode & (GES_PIPELINE_MODE_RENDER |
+ GES_PIPELINE_MODE_SMART_RENDER))
+ gst_bin_remove (GST_BIN (object), self->priv->encodebin);
+ else
+ gst_object_unref (self->priv->encodebin);
+ self->priv->encodebin = NULL;
+ }
+
+ if (self->priv->profile) {
+ gst_encoding_profile_unref (self->priv->profile);
+ self->priv->profile = NULL;
+ }
+
+ if (self->priv->timeline) {
+ g_signal_handlers_disconnect_by_func (self->priv->timeline,
+ _timeline_track_added_cb, self);
+ g_signal_handlers_disconnect_by_func (self->priv->timeline,
+ _timeline_track_removed_cb, self);
+ gst_element_set_state (GST_ELEMENT (self->priv->timeline), GST_STATE_NULL);
+ }
+
+ G_OBJECT_CLASS (ges_pipeline_parent_class)->dispose (object);
+}
+
+static void
+ges_pipeline_class_init (GESPipelineClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ GST_DEBUG_CATEGORY_INIT (ges_pipeline_debug, "gespipeline",
+ GST_DEBUG_FG_YELLOW, "ges pipeline");
+
+ object_class->dispose = ges_pipeline_dispose;
+ object_class->get_property = ges_pipeline_get_property;
+ object_class->set_property = ges_pipeline_set_property;
+
+ /**
+ * GESPipeline:audio-sink:
+ *
+ * Audio sink for the preview.
+ */
+ properties[PROP_AUDIO_SINK] = g_param_spec_object ("audio-sink", "Audio Sink",
+ "Audio sink for the preview.",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GESPipeline:video-sink:
+ *
+ * Video sink for the preview.
+ */
+ properties[PROP_VIDEO_SINK] = g_param_spec_object ("video-sink", "Video Sink",
+ "Video sink for the preview.",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GESPipeline:timeline:
+ *
+ * Timeline to use in this pipeline. See also
+ * ges_pipeline_set_timeline() for more info.
+ */
+ properties[PROP_TIMELINE] = g_param_spec_object ("timeline", "Timeline",
+ "Timeline to use in this pipeline. See also "
+ "ges_pipeline_set_timeline() for more info.",
+ GES_TYPE_TIMELINE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GESPipeline:mode:
+ *
+ * Pipeline mode. See ges_pipeline_set_mode() for more
+ * info.
+ */
+ properties[PROP_MODE] = g_param_spec_flags ("mode", "Mode",
+ "Pipeline mode. See ges_pipeline_set_mode() for more info.",
+ GES_TYPE_PIPELINE_FLAGS, DEFAULT_TIMELINE_MODE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GESPipeline::audio-filter
+ *
+ * The audio filter(s) to apply during playback right before the audio sink
+ *
+ * Since: 1.6.0
+ */
+ properties[PROP_AUDIO_FILTER] =
+ g_param_spec_object ("audio-filter", "Audio filter",
+ "the audio filter(s) to apply, if possible", GST_TYPE_ELEMENT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GESPipeline::video-filter
+ *
+ * The video filter(s) to apply during playback right before the video sink
+ *
+ * Since: 1.6.0
+ */
+ properties[PROP_VIDEO_FILTER] =
+ g_param_spec_object ("video-filter", "Video filter",
+ "the Video filter(s) to apply, if possible", GST_TYPE_ELEMENT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+
+ element_class->change_state = GST_DEBUG_FUNCPTR (ges_pipeline_change_state);
+
+ /* TODO : Add state_change handlers
+ * Don't change state if we don't have a timeline */
+}
+
+static void
+ges_pipeline_init (GESPipeline * self)
+{
+ GST_INFO_OBJECT (self, "Creating new 'playsink'");
+ self->priv = ges_pipeline_get_instance_private (self);
+ self->priv->valid_thread = g_thread_self ();
+
+ self->priv->playsink =
+ gst_element_factory_make ("playsink", "internal-sinks");
+ self->priv->encodebin =
+ gst_element_factory_make ("encodebin", "internal-encodebin");
+ g_object_set (self->priv->encodebin, "avoid-reencoding", TRUE, NULL);
+
+ if (G_UNLIKELY (self->priv->playsink == NULL))
+ goto no_playsink;
+ if (G_UNLIKELY (self->priv->encodebin == NULL))
+ goto no_encodebin;
+
+ ges_pipeline_set_mode (self, DEFAULT_TIMELINE_MODE);
+
+ return;
+
+no_playsink:
+ {
+ GST_ERROR_OBJECT (self, "Can't create playsink instance !");
+ return;
+ }
+no_encodebin:
+ {
+ GST_ERROR_OBJECT (self, "Can't create encodebin instance !");
+ return;
+ }
+}
+
+/**
+ * ges_pipeline_new:
+ *
+ * Creates a new conveninence #GESPipeline.
+ *
+ * Returns: (transfer floating): the new #GESPipeline.
+ */
+GESPipeline *
+ges_pipeline_new (void)
+{
+ return GES_PIPELINE (gst_element_factory_make ("gespipeline", NULL));
+}
+
+#define TRACK_COMPATIBLE_PROFILE(tracktype, profile) \
+ ( (GST_IS_ENCODING_AUDIO_PROFILE (profile) && (tracktype) == GES_TRACK_TYPE_AUDIO) || \
+ (GST_IS_ENCODING_VIDEO_PROFILE (profile) && (tracktype) == GES_TRACK_TYPE_VIDEO))
+
+static gboolean
+_track_is_compatible_with_profile (GESPipeline * self, GESTrack * track,
+ GstEncodingProfile * prof)
+{
+ if (TRACK_COMPATIBLE_PROFILE (track->type, prof)) {
+ if (self->priv->mode == GES_PIPELINE_MODE_SMART_RENDER) {
+ GstCaps *ocaps, *rcaps;
+
+ GST_DEBUG ("Smart Render mode, setting input caps");
+ ocaps = gst_encoding_profile_get_input_caps (prof);
+ ocaps = gst_caps_make_writable (ocaps);
+ if (track->type == GES_TRACK_TYPE_AUDIO)
+ rcaps = gst_caps_new_empty_simple ("audio/x-raw");
+ else
+ rcaps = gst_caps_new_empty_simple ("video/x-raw");
+ gst_caps_append (ocaps, rcaps);
+ ges_track_set_caps (track, ocaps);
+ gst_caps_unref (ocaps);
+ } else {
+ GstCaps *caps = NULL;
+
+ /* Raw preview or rendering mode */
+ if (track->type == GES_TRACK_TYPE_VIDEO)
+ caps = gst_caps_new_empty_simple ("video/x-raw");
+ else if (track->type == GES_TRACK_TYPE_AUDIO)
+ caps = gst_caps_new_empty_simple ("audio/x-raw");
+
+ if (caps) {
+ ges_track_set_caps (track, caps);
+ gst_caps_unref (caps);
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ges_pipeline_update_caps (GESPipeline * self)
+{
+ GList *ltrack, *tracks, *lstream;
+
+ if (!self->priv->profile)
+ return TRUE;
+
+ GST_DEBUG ("Updating track caps");
+
+ tracks = ges_timeline_get_tracks (self->priv->timeline);
+
+ /* Take each stream of the encoding profile and find a matching
+ * track to set the caps on */
+ for (ltrack = tracks; ltrack; ltrack = ltrack->next) {
+ GESTrack *track = (GESTrack *) ltrack->data;
+ GList *allstreams;
+
+ if (!GST_IS_ENCODING_CONTAINER_PROFILE (self->priv->profile)) {
+ if (_track_is_compatible_with_profile (self, track, self->priv->profile)) {
+ gst_object_unref (track);
+
+ goto done;
+ } else {
+ gst_object_unref (track);
+ continue;
+ }
+ }
+
+ allstreams = (GList *)
+ gst_encoding_container_profile_get_profiles (
+ (GstEncodingContainerProfile *) self->priv->profile);
+
+ /* Find a matching stream setting */
+ for (lstream = allstreams; lstream; lstream = lstream->next) {
+ GstEncodingProfile *prof = (GstEncodingProfile *) lstream->data;
+ if (_track_is_compatible_with_profile (self, track, prof))
+ break;
+ }
+
+ gst_object_unref (track);
+ }
+
+done:
+ if (tracks)
+ g_list_free (tracks);
+
+ GST_DEBUG ("Done updating caps");
+
+ return TRUE;
+}
+
+static void
+_link_tracks (GESPipeline * pipeline)
+{
+ GList *tmp;
+
+ GST_DEBUG_OBJECT (pipeline, "Linking tracks");
+
+ if (!pipeline->priv->timeline) {
+ GST_INFO_OBJECT (pipeline, "Not timeline set yet, doing nothing");
+
+ return;
+ }
+
+ for (tmp = pipeline->priv->timeline->tracks; tmp; tmp = tmp->next)
+ _link_track (pipeline, tmp->data);
+
+ if (IN_RENDERING_MODE (pipeline)) {
+ GString *unlinked_issues = NULL;
+ GstIterator *pads;
+ gboolean done = FALSE;
+ GValue paditem = { 0, };
+
+ pads = gst_element_iterate_sink_pads (pipeline->priv->encodebin);
+ while (!done) {
+ switch (gst_iterator_next (pads, &paditem)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *testpad = g_value_get_object (&paditem);
+ if (!gst_pad_is_linked (testpad)) {
+ GstCaps *sinkcaps = gst_pad_query_caps (testpad, NULL);
+ gchar *caps_string = gst_caps_to_string (sinkcaps);
+ gchar *path_string =
+ gst_object_get_path_string (GST_OBJECT (testpad));
+ gst_caps_unref (sinkcaps);
+
+ if (!unlinked_issues)
+ unlinked_issues =
+ g_string_new ("Following encodebin pads are not linked:\n");
+
+ g_string_append_printf (unlinked_issues, " - %s: %s", path_string,
+ caps_string);
+ g_free (caps_string);
+ g_free (path_string);
+ }
+ g_value_reset (&paditem);
+ }
+ break;
+ case GST_ITERATOR_DONE:
+ case GST_ITERATOR_ERROR:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ }
+ }
+ g_value_reset (&paditem);
+ gst_iterator_free (pads);
+
+ if (unlinked_issues) {
+ GST_ELEMENT_ERROR (pipeline, STREAM, FAILED, (NULL), ("%s",
+ unlinked_issues->str));
+ g_string_free (unlinked_issues, TRUE);
+ }
+ }
+}
+
+static void
+_unlink_tracks (GESPipeline * pipeline)
+{
+ GList *tmp;
+
+ GST_DEBUG_OBJECT (pipeline, "Disconnecting all tracks");
+ if (!pipeline->priv->timeline) {
+ GST_INFO_OBJECT (pipeline, "Not timeline set yet, doing nothing");
+
+ return;
+ }
+
+ for (tmp = pipeline->priv->timeline->tracks; tmp; tmp = tmp->next)
+ _unlink_track (pipeline, tmp->data);
+}
+
+static GstStateChangeReturn
+ges_pipeline_change_state (GstElement * element, GstStateChange transition)
+{
+ GESPipeline *self;
+ GstStateChangeReturn ret;
+
+ self = GES_PIPELINE (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ if (G_UNLIKELY (self->priv->timeline == NULL)) {
+ GST_ERROR_OBJECT (element,
+ "No GESTimeline set on the pipeline, cannot play !");
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto done;
+ }
+ if (IN_RENDERING_MODE (self)) {
+ GST_DEBUG ("rendering => Updating pipeline caps");
+ /* Set caps on all tracks according to profile if present */
+ if (!ges_pipeline_update_caps (self)) {
+ GST_ERROR_OBJECT (element, "Error setting the caps for rendering");
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto done;
+ }
+ }
+ _link_tracks (self);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ {
+ GList *tmp;
+
+ for (tmp = self->priv->not_rendered_tracks; tmp; tmp = tmp->next)
+ gst_element_set_locked_state (tmp->data, FALSE);
+ }
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ {
+ GstElement *queue = gst_bin_get_by_name (GST_BIN (self->priv->playsink),
+ "vqueue");
+
+ if (queue) {
+ GST_INFO_OBJECT (self, "Setting playsink video queue max-size-time to"
+ " 2 seconds.");
+ g_object_set (G_OBJECT (queue), "max-size-buffers", 0,
+ "max-size-bytes", 0, "max-size-time", (gint64) 2 * GST_SECOND,
+ NULL);
+ gst_object_unref (queue);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ ret =
+ GST_ELEMENT_CLASS (ges_pipeline_parent_class)->change_state
+ (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ case GST_STATE_CHANGE_NULL_TO_NULL:
+ _unlink_tracks (self);
+ break;
+ default:
+ break;
+ }
+
+done:
+ return ret;
+}
+
+static OutputChain *
+new_output_chain_for_track (GESPipeline * self, GESTrack * track)
+{
+ OutputChain *chain;
+
+ chain = g_new0 (OutputChain, 1);
+ chain->track = track;
+
+ return chain;
+}
+
+static OutputChain *
+get_output_chain_for_track (GESPipeline * self, GESTrack * track)
+{
+ GList *tmp;
+
+ for (tmp = self->priv->chains; tmp; tmp = tmp->next) {
+ OutputChain *chain = (OutputChain *) tmp->data;
+ if (chain->track == track)
+ return chain;
+ }
+
+ return NULL;
+}
+
+/* Fetches a compatible pad on the target element which isn't already
+ * linked */
+static GstPad *
+get_compatible_unlinked_pad (GstElement * element, GESTrack * track)
+{
+ GstPad *res = NULL;
+ GstIterator *pads;
+ gboolean done = FALSE;
+ const GstCaps *srccaps;
+ GValue paditem = { 0, };
+
+ if (G_UNLIKELY (track == NULL))
+ goto no_track;
+
+ GST_DEBUG_OBJECT (element, " track %" GST_PTR_FORMAT, track);
+
+ pads = gst_element_iterate_sink_pads (element);
+ srccaps = ges_track_get_caps (track);
+
+ GST_DEBUG_OBJECT (track, "srccaps %" GST_PTR_FORMAT, srccaps);
+
+ while (!done) {
+ switch (gst_iterator_next (pads, &paditem)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *testpad = g_value_get_object (&paditem);
+
+ if (!gst_pad_is_linked (testpad)) {
+ GstCaps *sinkcaps = gst_pad_query_caps (testpad, NULL);
+
+ GST_DEBUG_OBJECT (track, "sinkccaps %" GST_PTR_FORMAT, sinkcaps);
+
+ if (gst_caps_can_intersect (srccaps, sinkcaps)) {
+ res = gst_object_ref (testpad);
+ done = TRUE;
+ }
+ gst_caps_unref (sinkcaps);
+ }
+ g_value_reset (&paditem);
+ }
+ break;
+ case GST_ITERATOR_DONE:
+ case GST_ITERATOR_ERROR:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ }
+ }
+ g_value_reset (&paditem);
+ gst_iterator_free (pads);
+
+ return res;
+
+no_track:
+ {
+ GST_ERROR ("No track to check against");
+ return NULL;
+ }
+}
+
+static void
+_link_track (GESPipeline * self, GESTrack * track)
+{
+ GstPad *pad;
+ OutputChain *chain;
+ GstPad *sinkpad;
+ GstCaps *caps;
+ GstPadLinkReturn lret;
+ gboolean reconfigured = FALSE;
+
+ pad = ges_timeline_get_pad_for_track (self->priv->timeline, track);
+ if (G_UNLIKELY (!pad)) {
+ GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL),
+ ("Trying to link %" GST_PTR_FORMAT
+ " but no pad is exposed for it.", track));
+ return;
+ }
+
+ caps = gst_pad_query_caps (pad, NULL);
+ GST_DEBUG_OBJECT (self, "new pad %s:%s , caps:%" GST_PTR_FORMAT,
+ GST_DEBUG_PAD_NAME (pad), caps);
+ gst_caps_unref (caps);
+
+ /* Don't connect track if it's not going to be used */
+ if (track->type == GES_TRACK_TYPE_VIDEO &&
+ !(self->priv->mode & GES_PIPELINE_MODE_PREVIEW_VIDEO) &&
+ !(self->priv->mode & GES_PIPELINE_MODE_RENDER) &&
+ !(self->priv->mode & GES_PIPELINE_MODE_SMART_RENDER)) {
+ GST_DEBUG_OBJECT (self, "Video track... but we don't need it. Not linking");
+ }
+ if (track->type == GES_TRACK_TYPE_AUDIO &&
+ !(self->priv->mode & GES_PIPELINE_MODE_PREVIEW_AUDIO) &&
+ !(self->priv->mode & GES_PIPELINE_MODE_RENDER) &&
+ !(self->priv->mode & GES_PIPELINE_MODE_SMART_RENDER)) {
+ GST_DEBUG_OBJECT (self, "Audio track... but we don't need it. Not linking");
+ }
+
+ /* Get an existing chain or create it */
+ if (!(chain = get_output_chain_for_track (self, track)))
+ chain = new_output_chain_for_track (self, track);
+
+ if (chain->tee) {
+ gst_object_unref (pad);
+ GST_INFO_OBJECT (self, "Chain is already built (%" GST_PTR_FORMAT ")",
+ chain->encodebinpad ? chain->encodebinpad : chain->playsinkpad);
+
+ return;
+ }
+
+ chain->srcpad = pad;
+
+ /* Adding tee */
+ chain->tee = gst_element_factory_make ("tee", NULL);
+ gst_bin_add (GST_BIN_CAST (self), chain->tee);
+ gst_element_sync_state_with_parent (chain->tee);
+
+ /* Linking pad to tee */
+ sinkpad = gst_element_get_static_pad (chain->tee, "sink");
+ lret = gst_pad_link (pad, sinkpad);
+ if (lret != GST_PAD_LINK_OK) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+ (NULL), ("Could not link the tee (%s)", gst_pad_link_get_name (lret)));
+ goto error;
+ }
+
+ gst_object_unref (sinkpad);
+
+ /* Connect playsink */
+ if (self->priv->mode & GES_PIPELINE_MODE_PREVIEW) {
+ const gchar *sinkpad_name;
+ GstPad *tmppad;
+
+ GST_DEBUG_OBJECT (self, "Connecting to playsink");
+
+ switch (track->type) {
+ case GES_TRACK_TYPE_VIDEO:
+ sinkpad_name = "video_sink";
+ break;
+ case GES_TRACK_TYPE_AUDIO:
+ sinkpad_name = "audio_sink";
+ break;
+ case GES_TRACK_TYPE_TEXT:
+ sinkpad_name = "text_sink";
+ break;
+ default:
+ GST_WARNING_OBJECT (self, "Can't handle tracks of type %d yet",
+ track->type);
+ goto error;
+ }
+
+ /* Request a sinkpad from playsink */
+ if (G_UNLIKELY (!(sinkpad =
+ gst_element_get_request_pad (self->priv->playsink,
+ sinkpad_name)))) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+ (NULL), ("Could not get a pad from playsink for %s", sinkpad_name));
+ goto error;
+ }
+
+ tmppad = gst_element_get_request_pad (chain->tee, "src_%u");
+ lret = gst_pad_link_full (tmppad, sinkpad, GST_PAD_LINK_CHECK_NOTHING);
+ if (G_UNLIKELY (lret != GST_PAD_LINK_OK)) {
+ gst_object_unref (tmppad);
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+ (NULL),
+ ("Could not link %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT " (%s)",
+ tmppad, sinkpad, gst_pad_link_get_name (lret)));
+ goto error;
+ }
+ gst_object_unref (tmppad);
+
+ GST_DEBUG ("Reconfiguring playsink");
+
+ /* reconfigure playsink */
+ g_signal_emit_by_name (self->priv->playsink, "reconfigure", &reconfigured);
+ GST_DEBUG ("'reconfigure' returned %d", reconfigured);
+
+ /* We still hold a reference on the sinkpad */
+ chain->playsinkpad = sinkpad;
+ }
+
+ /* Connect to encodebin */
+ if (IN_RENDERING_MODE (self)) {
+ GstPad *tmppad;
+ GST_DEBUG_OBJECT (self, "Connecting to encodebin");
+
+ if (!chain->encodebinpad) {
+ /* Check for unused static pads */
+ sinkpad = get_compatible_unlinked_pad (self->priv->encodebin, track);
+
+ if (sinkpad == NULL) {
+ GstCaps *caps = gst_pad_query_caps (pad, NULL);
+
+ /* If no compatible static pad is available, request a pad */
+ g_signal_emit_by_name (self->priv->encodebin, "request-pad", caps,
+ &sinkpad);
+
+ if (G_UNLIKELY (sinkpad == NULL)) {
+ gst_element_set_locked_state (GST_ELEMENT (track), TRUE);
+
+ self->priv->not_rendered_tracks =
+ g_list_append (self->priv->not_rendered_tracks, track);
+
+ GST_INFO_OBJECT (self,
+ "Couldn't get a pad from encodebin for: %" GST_PTR_FORMAT, caps);
+ gst_caps_unref (caps);
+ goto error;
+ }
+
+ gst_caps_unref (caps);
+ }
+ chain->encodebinpad = sinkpad;
+ GST_INFO_OBJECT (track, "Linked to %" GST_PTR_FORMAT, sinkpad);
+ }
+
+ tmppad = gst_element_get_request_pad (chain->tee, "src_%u");
+ if (G_UNLIKELY (gst_pad_link_full (tmppad, chain->encodebinpad,
+ GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) {
+ GST_ERROR_OBJECT (self, "Couldn't link track pad to encodebin");
+ goto error;
+ }
+ gst_object_unref (tmppad);
+
+ }
+
+ /* If chain wasn't already present, insert it in list */
+ if (!get_output_chain_for_track (self, track))
+ self->priv->chains = g_list_append (self->priv->chains, chain);
+
+ GST_DEBUG ("done");
+ gst_object_unref (pad);
+ return;
+
+error:
+ {
+ gst_object_unref (pad);
+ if (chain->tee) {
+ gst_element_set_state (chain->tee, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (self), chain->tee);
+ }
+ if (sinkpad)
+ gst_object_unref (sinkpad);
+
+ g_free (chain);
+ }
+}
+
+static void
+_unlink_track (GESPipeline * self, GESTrack * track)
+{
+ OutputChain *chain;
+
+ GST_DEBUG_OBJECT (self, "Unlinking removed %" GST_PTR_FORMAT, track);
+
+ if (G_UNLIKELY (!(chain = get_output_chain_for_track (self, track)))) {
+ GST_DEBUG_OBJECT (self, "Track wasn't used");
+ return;
+ }
+
+ /* Unlink encodebin */
+ if (chain->encodebinpad) {
+ GstPad *peer = gst_pad_get_peer (chain->encodebinpad);
+ gst_pad_unlink (peer, chain->encodebinpad);
+ gst_object_unref (peer);
+ gst_element_release_request_pad (self->priv->encodebin,
+ chain->encodebinpad);
+ gst_object_unref (chain->encodebinpad);
+ }
+
+ /* Unlink playsink */
+ if (chain->playsinkpad) {
+ GstPad *peer = gst_pad_get_peer (chain->playsinkpad);
+ gst_pad_unlink (peer, chain->playsinkpad);
+ gst_object_unref (peer);
+ gst_element_release_request_pad (self->priv->playsink, chain->playsinkpad);
+ gst_object_unref (chain->playsinkpad);
+ }
+
+ gst_element_set_state (chain->tee, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), chain->tee);
+
+ self->priv->chains = g_list_remove (self->priv->chains, chain);
+ g_free (chain);
+
+ GST_DEBUG ("done");
+}
+
+/**
+ * ges_pipeline_set_timeline:
+ * @pipeline: a #GESPipeline
+ * @timeline: (transfer full): the #GESTimeline to set on the @pipeline.
+ *
+ * Sets the timeline to use in this pipeline.
+ *
+ * The reference to the @timeline will be stolen by the @pipeline.
+ *
+ * Returns: %TRUE if the @timeline could be successfully set on the @pipeline,
+ * else %FALSE.
+ */
+gboolean
+ges_pipeline_set_timeline (GESPipeline * pipeline, GESTimeline * timeline)
+{
+
+ g_return_val_if_fail (GES_IS_PIPELINE (pipeline), FALSE);
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (pipeline->priv->timeline == NULL, FALSE);
+ CHECK_THREAD (pipeline);
+
+ GST_DEBUG ("pipeline:%p, timeline:%p", timeline, pipeline);
+
+ if (G_UNLIKELY (!gst_bin_add (GST_BIN_CAST (pipeline),
+ GST_ELEMENT (timeline)))) {
+ return FALSE;
+ }
+ pipeline->priv->timeline = timeline;
+
+ g_signal_connect (timeline, "track-added",
+ G_CALLBACK (_timeline_track_added_cb), pipeline);
+ g_signal_connect (timeline, "track-removed",
+ G_CALLBACK (_timeline_track_removed_cb), pipeline);
+ /* FIXME Check if we should rollback if we can't sync state */
+ gst_element_sync_state_with_parent (GST_ELEMENT (timeline));
+
+ return TRUE;
+}
+
+/**
+ * ges_pipeline_set_render_settings:
+ * @pipeline: a #GESPipeline
+ * @output_uri: the URI to which the timeline will be rendered
+ * @profile: the #GstEncodingProfile to use to render the timeline.
+ *
+ * Specify where the pipeline shall be rendered and with what settings.
+ *
+ * A copy of @profile and @output_uri will be done internally, the caller can
+ * safely free those values afterwards.
+ *
+ * This method must be called before setting the pipeline mode to
+ * #GES_PIPELINE_MODE_RENDER
+ *
+ * Returns: %TRUE if the settings were aknowledged properly, else %FALSE
+ */
+gboolean
+ges_pipeline_set_render_settings (GESPipeline * pipeline,
+ const gchar * output_uri, GstEncodingProfile * profile)
+{
+ GError *err = NULL;
+ GstEncodingProfile *set_profile;
+
+ g_return_val_if_fail (GES_IS_PIPELINE (pipeline), FALSE);
+ CHECK_THREAD (pipeline);
+
+ /* FIXME Properly handle multi track, for now GESPipeline
+ * only hanles single track per type, so we should just set the
+ * presence to 1.
+ */
+ if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) {
+ const GList *tmpprofiles =
+ gst_encoding_container_profile_get_profiles
+ (GST_ENCODING_CONTAINER_PROFILE (profile));
+ GList *tmptrack, *tracks =
+ ges_timeline_get_tracks (pipeline->priv->timeline);
+
+ for (; tmpprofiles; tmpprofiles = tmpprofiles->next) {
+ for (tmptrack = tracks; tmptrack; tmptrack = tmptrack->next) {
+ if ((GST_IS_ENCODING_AUDIO_PROFILE (tmpprofiles->data) &&
+ GES_IS_AUDIO_TRACK (tmptrack->data)) ||
+ (GST_IS_ENCODING_VIDEO_PROFILE (tmpprofiles->data) &&
+ GES_IS_VIDEO_TRACK (tmptrack->data))) {
+ GST_DEBUG_OBJECT (pipeline, "Setting presence to 1!");
+ gst_encoding_profile_set_presence (tmpprofiles->data, 1);
+ gst_encoding_profile_set_allow_dynamic_output (tmpprofiles->data,
+ FALSE);
+ }
+ }
+ }
+
+ g_list_free_full (tracks, gst_object_unref);
+ }
+
+ /* Clear previous URI sink if it existed */
+ /* FIXME : We should figure out if it was added to the pipeline,
+ * and if so, remove it. */
+ if (pipeline->priv->urisink) {
+ gst_object_unref (pipeline->priv->urisink);
+ pipeline->priv->urisink = NULL;
+ }
+
+ pipeline->priv->urisink =
+ gst_element_make_from_uri (GST_URI_SINK, output_uri, "urisink", &err);
+ if (G_UNLIKELY (pipeline->priv->urisink == NULL)) {
+ GST_ERROR_OBJECT (pipeline, "Couldn't not create sink for URI %s: '%s'",
+ output_uri, ((err
+ && err->message) ? err->message : "failed to create element"));
+ g_clear_error (&err);
+ return FALSE;
+ }
+
+ if (pipeline->priv->profile)
+ gst_encoding_profile_unref (pipeline->priv->profile);
+ g_object_set (pipeline->priv->encodebin, "avoid-reencoding",
+ !(!(pipeline->priv->mode & GES_PIPELINE_MODE_SMART_RENDER)), NULL);
+ g_object_set (pipeline->priv->encodebin, "profile", profile, NULL);
+ g_object_get (pipeline->priv->encodebin, "profile", &set_profile, NULL);
+
+ if (set_profile == NULL) {
+ GST_ERROR_OBJECT (pipeline, "Profile %" GST_PTR_FORMAT " could no be set",
+ profile);
+
+ return FALSE;
+ }
+
+ /* We got a referencer when getting back the profile */
+ pipeline->priv->profile = profile;
+
+ return TRUE;
+}
+
+/**
+ * ges_pipeline_get_mode:
+ * @pipeline: a #GESPipeline
+ *
+ * Returns: the #GESPipelineFlags currently in use.
+ **/
+GESPipelineFlags
+ges_pipeline_get_mode (GESPipeline * pipeline)
+{
+ return pipeline->priv->mode;
+}
+
+/**
+ * ges_pipeline_set_mode:
+ * @pipeline: a #GESPipeline
+ * @mode: the #GESPipelineFlags to use
+ *
+ * switches the @pipeline to the specified @mode. The default mode when
+ * creating a #GESPipeline is #GES_PIPELINE_MODE_PREVIEW.
+ *
+ * Note: The @pipeline will be set to #GST_STATE_NULL during this call due to
+ * the internal changes that happen. The caller will therefore have to
+ * set the @pipeline to the requested state after calling this method.
+ *
+ * Returns: %TRUE if the mode was properly set, else %FALSE.
+ **/
+gboolean
+ges_pipeline_set_mode (GESPipeline * pipeline, GESPipelineFlags mode)
+{
+
+ GList *tmp;
+ g_return_val_if_fail (GES_IS_PIPELINE (pipeline), FALSE);
+ CHECK_THREAD (pipeline);
+
+ GST_DEBUG_OBJECT (pipeline, "current mode : %d, mode : %d",
+ pipeline->priv->mode, mode);
+
+ /* fast-path, nothing to change */
+ if (mode == pipeline->priv->mode)
+ return TRUE;
+
+ /* FIXME: It would be nice if we are only (de)activating preview
+ * modes to not set the whole pipeline to NULL, but instead just
+ * do the proper (un)linking to playsink. */
+
+ /* Switch pipeline to NULL since we're changing the configuration */
+ gst_element_set_state (GST_ELEMENT_CAST (pipeline), GST_STATE_NULL);
+
+
+ if (pipeline->priv->timeline) {
+ gboolean disabled =
+ ! !(mode & (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER));
+
+ for (tmp = pipeline->priv->timeline->tracks; tmp; tmp = tmp->next)
+ track_disable_last_gap (GES_TRACK (tmp->data), disabled);
+ }
+
+ /* remove no-longer needed components */
+ if (pipeline->priv->mode & GES_PIPELINE_MODE_PREVIEW &&
+ !(mode & GES_PIPELINE_MODE_PREVIEW)) {
+ /* Disable playsink */
+ GST_DEBUG ("Disabling playsink");
+ gst_object_ref (pipeline->priv->playsink);
+ gst_bin_remove (GST_BIN_CAST (pipeline), pipeline->priv->playsink);
+ }
+ if ((pipeline->priv->mode &
+ (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER)) &&
+ !(mode & (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER))) {
+ GList *tmp;
+ GstCaps *caps;
+
+ for (tmp = pipeline->priv->timeline->tracks; tmp; tmp = tmp->next) {
+ GESTrackType type = GES_TRACK (tmp->data)->type;
+
+ if (type == GES_TRACK_TYPE_AUDIO)
+ caps = gst_caps_new_empty_simple ("audio/x-raw");
+ else if (type == GES_TRACK_TYPE_VIDEO)
+ caps = gst_caps_new_empty_simple ("video/x-raw");
+ else
+ continue;
+
+ ges_track_set_caps (GES_TRACK (tmp->data), caps);
+ gst_caps_unref (caps);
+ }
+
+ /* Disable render bin */
+ GST_DEBUG ("Disabling rendering bin");
+ gst_object_ref (pipeline->priv->encodebin);
+ gst_object_ref (pipeline->priv->urisink);
+ gst_bin_remove_many (GST_BIN_CAST (pipeline),
+ pipeline->priv->encodebin, pipeline->priv->urisink, NULL);
+ }
+
+ /* Add new elements */
+ if (!(pipeline->priv->mode & GES_PIPELINE_MODE_PREVIEW) &&
+ (mode & GES_PIPELINE_MODE_PREVIEW)) {
+ /* Add playsink */
+ GST_DEBUG ("Adding playsink");
+ if (!gst_bin_add (GST_BIN_CAST (pipeline), pipeline->priv->playsink)) {
+ GST_ERROR_OBJECT (pipeline, "Couldn't add playsink");
+ return FALSE;
+ }
+ }
+ if (!(pipeline->priv->mode &
+ (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER)) &&
+ (mode & (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER))) {
+ /* Adding render bin */
+ GST_DEBUG ("Adding render bin");
+
+ if (G_UNLIKELY (pipeline->priv->urisink == NULL)) {
+ GST_ERROR_OBJECT (pipeline, "Output URI not set !");
+ return FALSE;
+ }
+ if (!gst_bin_add (GST_BIN_CAST (pipeline), pipeline->priv->encodebin)) {
+ GST_ERROR_OBJECT (pipeline, "Couldn't add encodebin");
+ return FALSE;
+ }
+ if (!gst_bin_add (GST_BIN_CAST (pipeline), pipeline->priv->urisink)) {
+ GST_ERROR_OBJECT (pipeline, "Couldn't add URI sink");
+ return FALSE;
+ }
+ g_object_set (pipeline->priv->encodebin, "avoid-reencoding",
+ !(!(mode & GES_PIPELINE_MODE_SMART_RENDER)), NULL);
+
+ gst_element_link_pads_full (pipeline->priv->encodebin, "src",
+ pipeline->priv->urisink, "sink", GST_PAD_LINK_CHECK_NOTHING);
+ }
+
+ /* FIXUPS */
+ /* FIXME
+ * If we are rendering, set playsink to sync=False,
+ * If we are NOT rendering, set playsink to sync=TRUE */
+
+ pipeline->priv->mode = mode;
+
+ return TRUE;
+}
+
+/**
+ * ges_pipeline_get_thumbnail:
+ * @self: a #GESPipeline in %GST_STATE_PLAYING or %GST_STATE_PAUSED
+ * @caps: (transfer none): caps specifying current format. Use %GST_CAPS_ANY
+ * for native size.
+ *
+ * Returns a #GstSample with the currently playing image in the format specified by
+ * caps. The caller should free the sample with #gst_sample_unref when finished. If ANY
+ * caps are specified, the information will be returned in the whatever format
+ * is currently used by the sink. This information can be retrieve from caps
+ * associated with the buffer.
+ *
+ * Returns: (transfer full) (nullable): a #GstSample or %NULL
+ */
+
+GstSample *
+ges_pipeline_get_thumbnail (GESPipeline * self, GstCaps * caps)
+{
+ GstElement *sink;
+
+ g_return_val_if_fail (GES_IS_PIPELINE (self), FALSE);
+ CHECK_THREAD (self);
+
+ sink = self->priv->playsink;
+
+ if (!sink) {
+ GST_WARNING ("thumbnailing can only be done if we have a playsink");
+ return NULL;
+ }
+
+ return ges_play_sink_convert_frame (sink, caps);
+}
+
+/**
+ * ges_pipeline_save_thumbnail:
+ * @self: a #GESPipeline in %GST_STATE_PLAYING or %GST_STATE_PAUSED
+ * @width: the requested width or -1 for native size
+ * @height: the requested height or -1 for native size
+ * @format: a string specifying the desired mime type (for example,
+ * image/jpeg)
+ * @location: the path to save the thumbnail
+ * @error: (out) (allow-none) (transfer full): An error to be set in case
+ * something wrong happens or %NULL
+ *
+ * Saves the current frame to the specified @location.
+ *
+ * Returns: %TRUE if the thumbnail was properly save, else %FALSE.
+ */
+gboolean
+ges_pipeline_save_thumbnail (GESPipeline * self, int width, int
+ height, const gchar * format, const gchar * location, GError ** error)
+{
+ GstMapInfo map_info;
+ GstBuffer *b;
+ GstSample *sample;
+ GstCaps *caps;
+ gboolean res = TRUE;
+
+ g_return_val_if_fail (GES_IS_PIPELINE (self), FALSE);
+ CHECK_THREAD (self);
+
+ caps = gst_caps_from_string (format);
+
+ if (width > 1)
+ gst_caps_set_simple (caps, "width", G_TYPE_INT, width, NULL);
+
+ if (height > 1)
+ gst_caps_set_simple (caps, "height", G_TYPE_INT, height, NULL);
+
+ if (!(sample = ges_pipeline_get_thumbnail (self, caps))) {
+ gst_caps_unref (caps);
+ return FALSE;
+ }
+
+ b = gst_sample_get_buffer (sample);
+ if (gst_buffer_map (b, &map_info, GST_MAP_READ)) {
+ if (!g_file_set_contents (location, (const char *) map_info.data,
+ map_info.size, error)) {
+ GST_WARNING ("Could not save thumbnail: %s",
+ error ? (*error)->message : "");
+ res = FALSE;
+ }
+ }
+
+ gst_caps_unref (caps);
+ gst_buffer_unmap (b, &map_info);
+ gst_sample_unref (sample);
+
+ return res;
+}
+
+/**
+ * ges_pipeline_get_thumbnail_rgb24:
+ * @self: a #GESPipeline in %GST_STATE_PLAYING or %GST_STATE_PAUSED
+ * @width: the requested width or -1 for native size
+ * @height: the requested height or -1 for native size
+ *
+ * A convenience method for @ges_pipeline_get_thumbnail which
+ * returns a buffer in 24-bit RGB, optionally scaled to the specified width
+ * and height. If -1 is specified for either dimension, it will be left at
+ * native size. You can retreive this information from the caps associated
+ * with the buffer.
+ *
+ * The caller is responsible for unreffing the returned sample with
+ * #gst_sample_unref.
+ *
+ * Returns: (transfer full) (nullable): a #GstSample or %NULL
+ */
+
+GstSample *
+ges_pipeline_get_thumbnail_rgb24 (GESPipeline * self, gint width, gint height)
+{
+ GstSample *ret;
+ GstCaps *caps;
+
+ g_return_val_if_fail (GES_IS_PIPELINE (self), FALSE);
+ CHECK_THREAD (self);
+
+ caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
+ "RGB", NULL);
+
+ if (width != -1)
+ gst_caps_set_simple (caps, "width", G_TYPE_INT, (gint) width, NULL);
+
+ if (height != -1)
+ gst_caps_set_simple (caps, "height", G_TYPE_INT, (gint) height, NULL);
+
+ ret = ges_pipeline_get_thumbnail (self, caps);
+ gst_caps_unref (caps);
+ return ret;
+}
+
+/**
+ * ges_pipeline_preview_get_video_sink:
+ * @self: a #GESPipeline
+ *
+ * Obtains a pointer to playsink's video sink element that is used for
+ * displaying video when the #GESPipeline is in %GES_PIPELINE_MODE_PREVIEW
+ *
+ * The caller is responsible for unreffing the returned element with
+ * #gst_object_unref.
+ *
+ * Returns: (transfer full): a pointer to the playsink video sink #GstElement
+ */
+GstElement *
+ges_pipeline_preview_get_video_sink (GESPipeline * self)
+{
+ GstElement *sink = NULL;
+
+ g_return_val_if_fail (GES_IS_PIPELINE (self), FALSE);
+ CHECK_THREAD (self);
+
+ g_object_get (self->priv->playsink, "video-sink", &sink, NULL);
+
+ return sink;
+};
+
+/**
+ * ges_pipeline_preview_set_video_sink:
+ * @self: a #GESPipeline in %GST_STATE_NULL
+ * @sink: (transfer none): a video sink #GstElement
+ *
+ * Sets playsink's video sink element that is used for displaying video when
+ * the #GESPipeline is in %GES_PIPELINE_MODE_PREVIEW
+ */
+void
+ges_pipeline_preview_set_video_sink (GESPipeline * self, GstElement * sink)
+{
+ g_return_if_fail (GES_IS_PIPELINE (self));
+ CHECK_THREAD (self);
+
+ g_object_set (self->priv->playsink, "video-sink", sink, NULL);
+};
+
+/**
+ * ges_pipeline_preview_get_audio_sink:
+ * @self: a #GESPipeline
+ *
+ * Obtains a pointer to playsink's audio sink element that is used for
+ * displaying audio when the #GESPipeline is in %GES_PIPELINE_MODE_PREVIEW
+ *
+ * The caller is responsible for unreffing the returned element with
+ * #gst_object_unref.
+ *
+ * Returns: (transfer full): a pointer to the playsink audio sink #GstElement
+ */
+GstElement *
+ges_pipeline_preview_get_audio_sink (GESPipeline * self)
+{
+ GstElement *sink = NULL;
+
+ g_return_val_if_fail (GES_IS_PIPELINE (self), FALSE);
+ CHECK_THREAD (self);
+
+ g_object_get (self->priv->playsink, "audio-sink", &sink, NULL);
+
+ return sink;
+};
+
+/**
+ * ges_pipeline_preview_set_audio_sink:
+ * @self: a #GESPipeline in %GST_STATE_NULL
+ * @sink: (transfer none): a audio sink #GstElement
+ *
+ * Sets playsink's audio sink element that is used for displaying audio when
+ * the #GESPipeline is in %GES_PIPELINE_MODE_PREVIEW
+ */
+void
+ges_pipeline_preview_set_audio_sink (GESPipeline * self, GstElement * sink)
+{
+ g_return_if_fail (GES_IS_PIPELINE (self));
+ CHECK_THREAD (self);
+
+ g_object_set (self->priv->playsink, "audio-sink", sink, NULL);
+};
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_PIPELINE
+#define _GES_PIPELINE
+
+#include <glib-object.h>
+#include <ges/ges.h>
+#include <gst/pbutils/encoding-profile.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_PIPELINE ges_pipeline_get_type()
+
+#define GES_PIPELINE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_PIPELINE, GESPipeline))
+
+#define GES_PIPELINE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_PIPELINE, GESPipelineClass))
+
+#define GES_IS_PIPELINE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_PIPELINE))
+
+#define GES_IS_PIPELINE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_PIPELINE))
+
+#define GES_PIPELINE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_PIPELINE, GESPipelineClass))
+
+typedef struct _GESPipelinePrivate GESPipelinePrivate;
+
+/**
+ * GESPipeline:
+ *
+ */
+
+struct _GESPipeline {
+ /*< private >*/
+ GstPipeline parent;
+
+ GESPipelinePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESPipelineClass:
+ * @parent_class: parent class
+ *
+ */
+
+struct _GESPipelineClass {
+ /*< private >*/
+ GstPipelineClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_pipeline_get_type (void);
+
+GES_API
+GESPipeline* ges_pipeline_new (void);
+
+GES_API
+gboolean ges_pipeline_set_timeline (GESPipeline * pipeline,
+ GESTimeline * timeline);
+
+GES_API
+gboolean ges_pipeline_set_render_settings (GESPipeline *pipeline,
+ const gchar * output_uri,
+ GstEncodingProfile *profile);
+GES_API
+gboolean ges_pipeline_set_mode (GESPipeline *pipeline,
+ GESPipelineFlags mode);
+
+GES_API
+GESPipelineFlags ges_pipeline_get_mode (GESPipeline *pipeline);
+
+GES_API GstSample *
+ges_pipeline_get_thumbnail(GESPipeline *self, GstCaps *caps);
+
+GES_API GstSample *
+ges_pipeline_get_thumbnail_rgb24(GESPipeline *self,
+ gint width, gint height);
+
+GES_API gboolean
+ges_pipeline_save_thumbnail(GESPipeline *self,
+ int width, int height, const gchar *format, const gchar *location,
+ GError **error);
+
+GES_API GstElement *
+ges_pipeline_preview_get_video_sink (GESPipeline * self);
+
+GES_API void
+ges_pipeline_preview_set_video_sink (GESPipeline * self,
+ GstElement * sink);
+
+GES_API GstElement *
+ges_pipeline_preview_get_audio_sink (GESPipeline * self);
+
+GES_API void
+ges_pipeline_preview_set_audio_sink (GESPipeline * self,
+ GstElement * sink);
+
+G_END_DECLS
+
+#endif /* _GES_PIPELINE */
+
--- /dev/null
+/* GStreamer Editing Services Pitivi Formatter
+ * Copyright (C) 2011-2012 Mathieu Duponchelle <seeed@laposte.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gespitiviformatter
+ * @title: GESPitiviFormatter
+ * @short_description: A formatter for the obsolete Pitivi xptv project file format
+ *
+ * This is a legacy format and you should avoid to use it. The formatter
+ * is really not in good shape and is deprecated.
+ *
+ * Deprecated: 1.0
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#undef VERSION
+#endif
+
+#include <libxml/xmlreader.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlwriter.h>
+
+#include "ges-internal.h"
+#include <ges/ges-pitivi-formatter.h>
+#include <ges/ges.h>
+
+/* The Pitivi etree formatter is 0.1 we set GES one to 0.2 */
+//#define VERSION "0.2"
+#define DOUBLE_VERSION 0.2
+
+#undef GST_CAT_DEFAULT
+GST_DEBUG_CATEGORY_STATIC (ges_pitivi_formatter_debug);
+#define GST_CAT_DEFAULT ges_pitivi_formatter_debug
+
+typedef struct SrcMapping
+{
+ gchar *id;
+ GESClip *clip;
+ guint priority;
+ GList *track_element_ids;
+} SrcMapping;
+
+struct _GESPitiviFormatterPrivate
+{
+ xmlXPathContextPtr xpathCtx;
+
+ /* {"sourceId" : {"prop": "value"}} */
+ GHashTable *sources_table;
+
+ /* Used as a set of the uris */
+ GHashTable *source_uris;
+
+ /* {trackId: {"factory_ref": factoryId, ""}
+ * if effect:
+ * {"factory_ref": "effect",
+ * "effect_name": name
+ * "effect_props": {"propname": value}}}
+ */
+ GHashTable *track_elements_table;
+
+ /* {factory-ref: [track-object-ref-id,...]} */
+ GHashTable *clips_table;
+
+ /* {layerPriority: layer} */
+ GHashTable *layers_table;
+
+ GESTimeline *timeline;
+
+ GESTrack *tracka, *trackv;
+
+ /* List the Clip that haven't been loaded yet */
+ GList *sources_to_load;
+
+ /* Saving context */
+ /* {factory_id: uri} */
+ GHashTable *saving_source_table;
+ guint nb_sources;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESPitiviFormatter, ges_pitivi_formatter,
+ GES_TYPE_FORMATTER);
+
+
+static void
+list_table_destroyer (gpointer key, gpointer value, void *unused)
+{
+ g_list_foreach (value, (GFunc) g_free, NULL);
+ g_list_free (value);
+}
+
+static gboolean
+pitivi_can_load_uri (GESFormatter * dummy_instance, const gchar * uri,
+ GError ** error)
+{
+ xmlDocPtr doc;
+ gboolean ret = TRUE;
+ xmlXPathObjectPtr xpathObj;
+ xmlXPathContextPtr xpathCtx;
+ gchar *filename = g_filename_from_uri (uri, NULL, NULL);
+
+ if (!filename || !g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ g_free (filename);
+ return FALSE;
+ }
+
+ g_free (filename);
+
+ if (!(doc = xmlParseFile (uri))) {
+ GST_ERROR ("The xptv file for uri %s was badly formed", uri);
+ return FALSE;
+ }
+
+ xpathCtx = xmlXPathNewContext (doc);
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *) "/pitivi", xpathCtx);
+ if (!xpathObj || !xpathObj->nodesetval || xpathObj->nodesetval->nodeNr == 0)
+ ret = FALSE;
+
+ xmlFreeDoc (doc);
+ xmlXPathFreeObject (xpathObj);
+ xmlXPathFreeContext (xpathCtx);
+
+ return ret;
+}
+
+/* Project loading functions */
+
+/* Return: a GHashTable containing:
+ * {attr: value}
+ */
+static GHashTable *
+get_nodes_infos (xmlNodePtr node)
+{
+ xmlAttr *cur_attr;
+ GHashTable *props_table;
+ gchar *name, *value;
+
+ props_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+
+ for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
+ name = (gchar *) cur_attr->name;
+ value = (gchar *) xmlGetProp (node, cur_attr->name);
+ g_hash_table_insert (props_table, g_strdup (name), g_strdup (value));
+ xmlFree (value);
+ }
+
+ return props_table;
+}
+
+static gboolean
+create_tracks (GESFormatter * self)
+{
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+ GList *tracks = NULL;
+
+ tracks = ges_timeline_get_tracks (self->timeline);
+
+ GST_DEBUG ("Creating tracks, current number of tracks %d",
+ g_list_length (tracks));
+
+ if (tracks) {
+ GList *tmp = NULL;
+ GESTrack *track;
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ track = tmp->data;
+ if (track->type == GES_TRACK_TYPE_AUDIO) {
+ priv->tracka = track;
+ } else {
+ priv->trackv = track;
+ }
+ }
+ g_list_foreach (tracks, (GFunc) gst_object_unref, NULL);
+ g_list_free (tracks);
+ return TRUE;
+ }
+
+ priv->tracka = GES_TRACK (ges_audio_track_new ());
+ priv->trackv = GES_TRACK (ges_video_track_new ());
+
+ if (!ges_timeline_add_track (self->timeline, priv->trackv)) {
+ return FALSE;
+ }
+
+ if (!ges_timeline_add_track (self->timeline, priv->tracka)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+parse_metadatas (GESFormatter * self)
+{
+ guint i, size;
+ xmlNodePtr node;
+ xmlAttr *cur_attr;
+ xmlNodeSetPtr nodes;
+ xmlXPathObjectPtr xpathObj;
+ GESMetaContainer *metacontainer = GES_META_CONTAINER (self->project);
+
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *)
+ "/pitivi/metadata", GES_PITIVI_FORMATTER (self)->priv->xpathCtx);
+ nodes = xpathObj->nodesetval;
+
+ size = (nodes) ? nodes->nodeNr : 0;
+ for (i = 0; i < size; i++) {
+ node = nodes->nodeTab[i];
+ for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
+ ges_meta_container_set_string (metacontainer, (gchar *) cur_attr->name,
+ (gchar *) xmlGetProp (node, cur_attr->name));
+ }
+ }
+}
+
+static void
+list_sources (GESFormatter * self)
+{
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+ xmlXPathObjectPtr xpathObj;
+ GHashTable *table;
+ int size, j;
+ gchar *id, *filename;
+ xmlNodeSetPtr nodes;
+
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *)
+ "/pitivi/factories/sources/source", priv->xpathCtx);
+ nodes = xpathObj->nodesetval;
+
+ size = (nodes) ? nodes->nodeNr : 0;
+ for (j = 0; j < size; ++j) {
+ table = get_nodes_infos (nodes->nodeTab[j]);
+ id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
+ filename = (gchar *) g_hash_table_lookup (table, (gchar *) "filename");
+ g_hash_table_insert (priv->sources_table, g_strdup (id), table);
+ g_hash_table_insert (priv->source_uris, g_strdup (filename),
+ g_strdup (filename));
+ if (self->project)
+ ges_project_create_asset (self->project, filename, GES_TYPE_URI_CLIP);
+ }
+
+ xmlXPathFreeObject (xpathObj);
+}
+
+static gboolean
+parse_track_elements (GESFormatter * self)
+{
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+ xmlXPathObjectPtr xpathObj;
+ xmlNodeSetPtr nodes;
+ int size, j;
+ gchar *id, *fac_ref;
+ GHashTable *table = NULL, *effect_table = NULL;
+ xmlNode *first_child;
+ gchar *media_type;
+
+ /* FIXME Make this whole function cleaner starting from
+ * "/pitivi/timeline/tracks/track/stream" and descending
+ * into the children. */
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *)
+ "/pitivi/timeline/tracks/track/track-objects/track-object",
+ priv->xpathCtx);
+
+ if (xpathObj == NULL) {
+ GST_DEBUG ("No track object found");
+
+ return FALSE;
+ }
+
+ nodes = xpathObj->nodesetval;
+ size = (nodes) ? nodes->nodeNr : 0;
+
+ for (j = 0; j < size; ++j) {
+ xmlNodePtr node = nodes->nodeTab[j];
+
+ table = get_nodes_infos (nodes->nodeTab[j]);
+ id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
+ first_child = nodes->nodeTab[j]->children->next;
+ fac_ref = (gchar *) xmlGetProp (first_child, (xmlChar *) "id");
+
+ /* We check if the first child is "effect" */
+ if (!g_strcmp0 ((gchar *) first_child->name, (gchar *) "effect")) {
+ xmlChar *effect_name;
+ xmlNodePtr fact_node = first_child->children->next;
+
+ /* We have a node called "text" in between thus ->next->next */
+ xmlNodePtr elem_props_node = fact_node->next->next;
+
+ effect_name = xmlGetProp (fact_node, (xmlChar *) "name");
+ g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"),
+ g_strdup ((gchar *) effect_name));
+ xmlFree (effect_name);
+
+ /* We put the effects properties in an hacktable (Lapsus is on :) */
+ effect_table = get_nodes_infos (elem_props_node);
+
+ g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
+ g_strdup ("effect"));
+
+ xmlFree (fac_ref);
+ } else {
+
+ g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
+ g_strdup (fac_ref));
+ xmlFree (fac_ref);
+ }
+
+ /* Same as before, we got a text node in between, thus the 2 prev
+ * node->parent is <track-objects>, the one before is <stream>
+ */
+ media_type = (gchar *) xmlGetProp (node->parent->prev->prev,
+ (const xmlChar *) "type");
+ g_hash_table_insert (table, g_strdup ((gchar *) "media_type"),
+ g_strdup (media_type));
+ xmlFree (media_type);
+
+
+ if (effect_table)
+ g_hash_table_insert (table, g_strdup ("effect_props"), effect_table);
+
+ g_hash_table_insert (priv->track_elements_table, g_strdup (id), table);
+ }
+
+ xmlXPathFreeObject (xpathObj);
+ return TRUE;
+}
+
+static gboolean
+parse_clips (GESFormatter * self)
+{
+ int size, j;
+ xmlNodeSetPtr nodes;
+ xmlXPathObjectPtr xpathObj;
+ xmlNodePtr clip_nd, tmp_nd, tmp_nd2;
+ xmlChar *trackelementrefId, *facrefId = NULL;
+
+ GList *reflist = NULL;
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+ GHashTable *clips_table = priv->clips_table;
+
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *)
+ "/pitivi/timeline/timeline-objects/timeline-object", priv->xpathCtx);
+
+ if (xpathObj == NULL) {
+ xmlXPathFreeObject (xpathObj);
+ return FALSE;
+ }
+
+ nodes = xpathObj->nodesetval;
+ size = (nodes) ? nodes->nodeNr : 0;
+
+ for (j = 0; j < size; j++) {
+ clip_nd = nodes->nodeTab[j];
+
+ for (tmp_nd = clip_nd->children; tmp_nd; tmp_nd = tmp_nd->next) {
+ /* We assume that factory-ref is always before the tckobjs-ref */
+ if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "factory-ref")) {
+ facrefId = xmlGetProp (tmp_nd, (xmlChar *) "id");
+
+ } else if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "track-object-refs")) {
+
+ for (tmp_nd2 = tmp_nd->children; tmp_nd2; tmp_nd2 = tmp_nd2->next) {
+ if (!xmlStrcmp (tmp_nd2->name, (xmlChar *) "track-object-ref")) {
+ /* We add the track object ref ID to the list of the current
+ * Clip tracks, this way we can merge 2
+ * Clip-s into 1 when we have unlinked TrackElement-s */
+ reflist = g_hash_table_lookup (clips_table, facrefId);
+ trackelementrefId = xmlGetProp (tmp_nd2, (xmlChar *) "id");
+ reflist =
+ g_list_append (reflist, g_strdup ((gchar *) trackelementrefId));
+ g_hash_table_insert (clips_table, g_strdup ((gchar *) facrefId),
+ reflist);
+
+ xmlFree (trackelementrefId);
+ }
+ }
+ }
+ }
+ }
+
+ xmlXPathFreeObject (xpathObj);
+ return TRUE;
+}
+
+static void
+set_properties (GObject * obj, GHashTable * props_table)
+{
+ gint i;
+ gchar **prop_array, *valuestr;
+ gint64 value;
+
+ gchar props[3][10] = { "duration", "in_point", "start" };
+
+ for (i = 0; i < 3; i++) {
+ valuestr = g_hash_table_lookup (props_table, props[i]);
+ prop_array = g_strsplit (valuestr, ")", 0);
+ value = g_ascii_strtoll (prop_array[1], NULL, 0);
+ g_object_set (obj, props[i], value, NULL);
+
+ g_strfreev (prop_array);
+ }
+}
+
+static void
+track_element_added_cb (GESClip * clip,
+ GESTrackElement * track_element, GHashTable * props_table)
+{
+ GESPitiviFormatter *formatter;
+
+ formatter = GES_PITIVI_FORMATTER (g_hash_table_lookup (props_table,
+ "current-formatter"));
+ if (formatter) {
+ GESPitiviFormatterPrivate *priv = formatter->priv;
+
+ /* Make sure the hack to get a ref to the formatter
+ * doesn't break everything */
+ g_hash_table_steal (props_table, "current-formatter");
+
+ priv->sources_to_load = g_list_remove (priv->sources_to_load, clip);
+ if (!priv->sources_to_load && GES_FORMATTER (formatter)->project)
+ ges_project_set_loaded (GES_FORMATTER (formatter)->project,
+ GES_FORMATTER (formatter));
+ }
+
+ /* Disconnect the signal */
+ g_signal_handlers_disconnect_by_func (clip, track_element_added_cb,
+ props_table);
+}
+
+static void
+make_source (GESFormatter * self, GList * reflist, GHashTable * source_table)
+{
+ GHashTable *props_table, *effect_table;
+ gchar **prio_array;
+ GESLayer *layer;
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+
+ gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL, *prio_str;
+ GList *tmp = NULL, *keys, *tmp_key;
+ GESUriClip *src = NULL;
+ gint prio;
+ gboolean a_avail = FALSE, v_avail = FALSE, video;
+ GHashTable *trackelement_table = priv->track_elements_table;
+
+ for (tmp = reflist; tmp; tmp = tmp->next) {
+
+ /* Get the layer */
+ props_table = g_hash_table_lookup (trackelement_table, (gchar *) tmp->data);
+ prio_str = (gchar *) g_hash_table_lookup (props_table, "priority");
+ prio_array = g_strsplit (prio_str, ")", 0);
+ prio = (gint) g_ascii_strtod (prio_array[1], NULL);
+ g_strfreev (prio_array);
+
+ /* If we do not have any layer with this priority, create it */
+ if (!(layer = g_hash_table_lookup (priv->layers_table, &prio))) {
+ layer = ges_layer_new ();
+ g_object_set (layer, "auto-transition", TRUE, "priority", prio, NULL);
+ ges_timeline_add_layer (self->timeline, layer);
+ g_hash_table_insert (priv->layers_table, g_memdup (&prio,
+ sizeof (guint64)), layer);
+ }
+
+ fac_ref = (gchar *) g_hash_table_lookup (props_table, "fac_ref");
+ media_type = (gchar *) g_hash_table_lookup (props_table, "media_type");
+
+ if (!g_strcmp0 (media_type, "pitivi.stream.VideoStream"))
+ video = TRUE;
+ else
+ video = FALSE;
+
+ /* FIXME I am sure we could reimplement this whole part
+ * in a simpler way */
+
+ if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
+ /* FIXME this is a hack to get a ref to the formatter when receiving
+ * child-added */
+ g_hash_table_insert (props_table, (gchar *) "current-formatter", self);
+ if (a_avail && (!video)) {
+ a_avail = FALSE;
+ } else if (v_avail && (video)) {
+ v_avail = FALSE;
+ } else {
+
+ /* If we only have audio or only video in the previous source,
+ * set it has such */
+ if (a_avail) {
+ ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
+ } else if (v_avail) {
+ ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
+ }
+
+ filename = (gchar *) g_hash_table_lookup (source_table, "filename");
+
+ src = ges_uri_clip_new (filename);
+
+ if (!video) {
+ v_avail = TRUE;
+ a_avail = FALSE;
+ } else {
+ a_avail = TRUE;
+ v_avail = FALSE;
+ }
+
+ set_properties (G_OBJECT (src), props_table);
+ ges_layer_add_clip (layer, GES_CLIP (src));
+
+ g_signal_connect (src, "child-added",
+ G_CALLBACK (track_element_added_cb), props_table);
+
+ priv->sources_to_load = g_list_prepend (priv->sources_to_load, src);
+ }
+
+ } else {
+ GESEffect *effect;
+ gchar *active = (gchar *) g_hash_table_lookup (props_table, "active");
+
+ effect = ges_effect_new ((gchar *)
+ g_hash_table_lookup (props_table, (gchar *) "effect_name"));
+ ges_track_element_set_track_type (GES_TRACK_ELEMENT (effect),
+ (video ? GES_TRACK_TYPE_VIDEO : GES_TRACK_TYPE_AUDIO));
+ effect_table =
+ g_hash_table_lookup (props_table, (gchar *) "effect_props");
+
+ ges_container_add (GES_CONTAINER (src), GES_TIMELINE_ELEMENT (effect));
+
+ if (!g_strcmp0 (active, (gchar *) "(bool)False"))
+ ges_track_element_set_active (GES_TRACK_ELEMENT (effect), FALSE);
+
+ /* Set effect properties */
+ keys = g_hash_table_get_keys (effect_table);
+ for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
+ GstStructure *structure;
+ const GValue *value;
+ GParamSpec *spec;
+ GstCaps *caps;
+ gchar *prop_val;
+
+ prop_val = (gchar *) g_hash_table_lookup (effect_table,
+ (gchar *) tmp_key->data);
+
+ if (g_strstr_len (prop_val, -1, "(GEnum)")) {
+ gchar **val = g_strsplit (prop_val, ")", 2);
+
+ ges_track_element_set_child_properties (GES_TRACK_ELEMENT (effect),
+ (gchar *) tmp_key->data, atoi (val[1]), NULL);
+ g_strfreev (val);
+
+ } else if (ges_track_element_lookup_child (GES_TRACK_ELEMENT (effect),
+ (gchar *) tmp->data, NULL, &spec)) {
+ gchar *caps_str = g_strdup_printf ("structure1, property1=%s;",
+ prop_val);
+
+ caps = gst_caps_from_string (caps_str);
+ g_free (caps_str);
+ structure = gst_caps_get_structure (caps, 0);
+ value = gst_structure_get_value (structure, "property1");
+
+ ges_track_element_set_child_property_by_pspec (GES_TRACK_ELEMENT
+ (effect), spec, (GValue *) value);
+ gst_caps_unref (caps);
+ }
+ }
+ }
+ }
+
+ if (a_avail) {
+ ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
+ } else if (v_avail) {
+ ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
+ }
+}
+
+static gboolean
+make_clips (GESFormatter * self)
+{
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+ GHashTable *source_table;
+
+ GList *keys = NULL, *tmp = NULL, *reflist = NULL;
+
+ keys = g_hash_table_get_keys (priv->clips_table);
+
+ for (tmp = keys; tmp; tmp = tmp->next) {
+ gchar *fac_id = (gchar *) tmp->data;
+
+ reflist = g_hash_table_lookup (priv->clips_table, fac_id);
+ source_table = g_hash_table_lookup (priv->sources_table, fac_id);
+ make_source (self, reflist, source_table);
+ }
+
+ g_list_free (keys);
+ return TRUE;
+}
+
+static gboolean
+load_pitivi_file_from_uri (GESFormatter * self,
+ GESTimeline * timeline, const gchar * uri, GError ** error)
+{
+ xmlDocPtr doc;
+ GESLayer *layer;
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+
+ gboolean ret = TRUE;
+ gint *prio = malloc (sizeof (gint));
+
+ *prio = 0;
+ layer = ges_layer_new ();
+ g_object_set (layer, "auto-transition", TRUE, NULL);
+
+ g_hash_table_insert (priv->layers_table, prio, layer);
+ g_object_set (layer, "priority", (gint32) 0, NULL);
+
+ if (!ges_timeline_add_layer (timeline, layer)) {
+ GST_ERROR ("Couldn't add layer");
+ return FALSE;
+ }
+
+ if (!(doc = xmlParseFile (uri))) {
+ GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
+ uri);
+ return FALSE;
+ }
+
+ priv->xpathCtx = xmlXPathNewContext (doc);
+
+ if (self->project)
+ parse_metadatas (self);
+
+ if (!create_tracks (self)) {
+ GST_ERROR ("Couldn't create tracks");
+ return FALSE;
+ }
+
+ list_sources (self);
+
+ if (!parse_clips (self)) {
+ GST_ERROR ("Couldn't find clips markup in the xptv file");
+ return FALSE;
+ }
+
+ if (!parse_track_elements (self)) {
+ GST_ERROR ("Couldn't find track objects markup in the xptv file");
+ return FALSE;
+ }
+
+
+
+ /* If there are no clips to load we should emit
+ * 'project-loaded' signal.
+ */
+ if (!g_hash_table_size (priv->clips_table) && GES_FORMATTER (self)->project) {
+ ges_project_set_loaded (GES_FORMATTER (self)->project,
+ GES_FORMATTER (self));
+ } else {
+ if (!make_clips (self)) {
+ GST_ERROR ("Couldn't deserialise the project properly");
+ return FALSE;
+ }
+ }
+
+ xmlXPathFreeContext (priv->xpathCtx);
+ xmlFreeDoc (doc);
+ return ret;
+}
+
+/* Object functions */
+static void
+ges_pitivi_formatter_finalize (GObject * object)
+{
+ GESPitiviFormatter *self = GES_PITIVI_FORMATTER (object);
+ GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+
+ g_hash_table_destroy (priv->sources_table);
+ g_hash_table_destroy (priv->source_uris);
+
+ g_hash_table_destroy (priv->saving_source_table);
+ g_list_free (priv->sources_to_load);
+
+ if (priv->clips_table != NULL) {
+ g_hash_table_foreach (priv->clips_table,
+ (GHFunc) list_table_destroyer, NULL);
+ g_hash_table_destroy (priv->clips_table);
+ }
+
+ if (priv->layers_table != NULL)
+ g_hash_table_destroy (priv->layers_table);
+
+ if (priv->track_elements_table != NULL) {
+ g_hash_table_destroy (priv->track_elements_table);
+ }
+
+ G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object);
+}
+
+static void
+ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
+{
+ GESFormatterClass *formatter_klass;
+ GObjectClass *object_class;
+
+ GST_DEBUG_CATEGORY_INIT (ges_pitivi_formatter_debug, "ges_pitivi_formatter",
+ GST_DEBUG_FG_YELLOW, "ges pitivi formatter");
+
+ object_class = G_OBJECT_CLASS (klass);
+ formatter_klass = GES_FORMATTER_CLASS (klass);
+
+ formatter_klass->can_load_uri = pitivi_can_load_uri;
+ formatter_klass->save_to_uri = NULL;
+ formatter_klass->load_from_uri = load_pitivi_file_from_uri;
+ object_class->finalize = ges_pitivi_formatter_finalize;
+
+ ges_formatter_class_register_metas (formatter_klass, "pitivi",
+ "Legacy Pitivi project files", "xptv", "text/x-xptv",
+ DOUBLE_VERSION, GST_RANK_MARGINAL);
+}
+
+static void
+ges_pitivi_formatter_init (GESPitiviFormatter * self)
+{
+ GESPitiviFormatterPrivate *priv;
+
+ self->priv = ges_pitivi_formatter_get_instance_private (self);
+
+ priv = self->priv;
+
+ priv->track_elements_table =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) g_hash_table_destroy);
+
+ priv->clips_table =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ priv->layers_table =
+ g_hash_table_new_full (g_int_hash, g_str_equal, g_free, gst_object_unref);
+
+ priv->sources_table =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) g_hash_table_destroy);
+
+ priv->source_uris =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ priv->sources_to_load = NULL;
+
+ /* Saving context */
+ priv->saving_source_table =
+ g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
+ priv->nb_sources = 1;
+}
+
+GESPitiviFormatter *
+ges_pitivi_formatter_new (void)
+{
+ return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services Pitivi Formatter
+ * Copyright (C) 2011-2012 Mathieu Duponchelle <seeed@laposte.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_PITIVI_FORMATTER
+#define _GES_PITIVI_FORMATTER
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_PITIVI_FORMATTER ges_pitivi_formatter_get_type()
+
+#define GES_PITIVI_FORMATTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatter))
+
+#define GES_PITIVI_FORMATTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterClass))
+
+#define GES_IS_PITIVI_FORMATTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_PITIVI_FORMATTER))
+
+#define GES_IS_PITIVI_FORMATTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_PITIVI_FORMATTER))
+
+#define GES_PITIVI_FORMATTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterClass))
+
+typedef struct _GESPitiviFormatterPrivate GESPitiviFormatterPrivate;
+
+
+/**
+ * GESPitiviFormatter:
+ *
+ * Serializes a #GESTimeline to a file using the xptv Pitivi file format
+ */
+struct _GESPitiviFormatter {
+ GESFormatter parent;
+
+ /*< public > */
+ /*< private >*/
+ GESPitiviFormatterPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESPitiviFormatterClass
+{
+ /*< private >*/
+ GESFormatterClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_pitivi_formatter_get_type (void);
+GES_API
+GESPitiviFormatter *ges_pitivi_formatter_new (void);
+
+G_END_DECLS
+
+#endif /* _GES_PITIVI_FORMATTER */
--- /dev/null
+/* GStreamer GES Library
+ * Copyright (C) 2018 GStreamer developers
+ *
+ * ges-prelude.h: prelude include header for gst-ges library
+ *
+ * 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_GES_PRELUDE_H__
+#define __GST_GES_PRELUDE_H__
+
+#include <gst/gst.h>
+
+#ifndef GES_API
+# ifdef BUILDING_GES
+# define GES_API GST_API_EXPORT /* from config.h */
+# else
+# define GES_API GST_API_IMPORT
+# endif
+#endif
+
+#endif /* __GST_GES_PRELUDE_H__ */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+/**
+ * SECTION: gesproject
+ * @title: GESProject
+ * @short_description: A GESAsset that is used to manage projects
+ *
+ * The #GESProject is used to control a set of #GESAsset and is a
+ * #GESAsset with #GES_TYPE_TIMELINE as @extractable_type itself. That
+ * means that you can extract #GESTimeline from a project as followed:
+ *
+ * |[
+ * GESProject *project;
+ * GESTimeline *timeline;
+ *
+ * project = ges_project_new ("file:///path/to/a/valid/project/uri");
+ *
+ * // Here you can connect to the various signal to get more infos about
+ * // what is happening and recover from errors if possible
+ * ...
+ *
+ * timeline = ges_asset_extract (GES_ASSET (project));
+ * ]|
+ *
+ * The #GESProject class offers a higher level API to handle #GESAsset-s.
+ * It lets you request new asset, and it informs you about new assets through
+ * a set of signals. Also it handles problem such as missing files/missing
+ * #GstElement and lets you try to recover from those.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges.h"
+#include "ges-internal.h"
+
+static GPtrArray *new_paths = NULL;
+static GHashTable *tried_uris = NULL;
+
+/* TODO We should rely on both extractable_type and @id to identify
+ * a Asset, not only @id
+ */
+struct _GESProjectPrivate
+{
+ GHashTable *assets;
+ /* Set of asset ID being loaded */
+ GHashTable *loading_assets;
+ GHashTable *loaded_with_error;
+ GESAsset *formatter_asset;
+
+ GList *formatters;
+
+ gchar *uri;
+
+ GList *encoding_profiles;
+};
+
+typedef struct EmitLoadedInIdle
+{
+ GESProject *project;
+ GESTimeline *timeline;
+} EmitLoadedInIdle;
+
+enum
+{
+ LOADED_SIGNAL,
+ ERROR_LOADING_ASSET,
+ ASSET_ADDED_SIGNAL,
+ ASSET_REMOVED_SIGNAL,
+ MISSING_URI_SIGNAL,
+ ASSET_LOADING_SIGNAL,
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESProject, ges_project, GES_TYPE_ASSET);
+
+static guint _signals[LAST_SIGNAL] = { 0 };
+
+static guint nb_projects = 0;
+
+enum
+{
+ PROP_0,
+ PROP_URI,
+ PROP_LAST,
+};
+
+static GParamSpec *_properties[LAST_SIGNAL] = { 0 };
+
+static gboolean
+_emit_loaded_in_idle (EmitLoadedInIdle * data)
+{
+ g_signal_emit (data->project, _signals[LOADED_SIGNAL], 0, data->timeline);
+
+ gst_object_unref (data->project);
+ gst_object_unref (data->timeline);
+ g_slice_free (EmitLoadedInIdle, data);
+
+ return FALSE;
+}
+
+static void
+ges_project_add_formatter (GESProject * project, GESFormatter * formatter)
+{
+ GESProjectPrivate *priv = GES_PROJECT (project)->priv;
+
+ ges_formatter_set_project (formatter, project);
+ priv->formatters = g_list_append (priv->formatters, formatter);
+
+ gst_object_ref_sink (formatter);
+}
+
+static void
+ges_project_remove_formatter (GESProject * project, GESFormatter * formatter)
+{
+ GList *tmp;
+ GESProjectPrivate *priv = GES_PROJECT (project)->priv;
+
+ for (tmp = priv->formatters; tmp; tmp = tmp->next) {
+ if (tmp->data == formatter) {
+ gst_object_unref (formatter);
+ priv->formatters = g_list_delete_link (priv->formatters, tmp);
+
+ return;
+ }
+ }
+}
+
+static void
+ges_project_set_uri (GESProject * project, const gchar * uri)
+{
+ GESProjectPrivate *priv;
+
+ g_return_if_fail (GES_IS_PROJECT (project));
+
+ priv = project->priv;
+ if (priv->uri) {
+ GST_WARNING_OBJECT (project, "Trying to rest URI, this is prohibited");
+
+ return;
+ }
+
+ if (uri == NULL) {
+ GST_LOG_OBJECT (project, "Uri should not be NULL");
+ return;
+ }
+
+ priv->uri = g_strdup (uri);
+
+ /* We use that URI as ID */
+ ges_asset_set_id (GES_ASSET (project), uri);
+
+ return;
+}
+
+static gboolean
+_load_project (GESProject * project, GESTimeline * timeline, GError ** error)
+{
+ GError *lerr = NULL;
+ GESProjectPrivate *priv;
+ GESFormatter *formatter;
+
+ priv = GES_PROJECT (project)->priv;
+
+ if (priv->uri == NULL) {
+ EmitLoadedInIdle *data = g_slice_new (EmitLoadedInIdle);
+
+ GST_LOG_OBJECT (project, "%s, Loading an empty timeline %s"
+ " as no URI set yet", GST_OBJECT_NAME (timeline),
+ ges_asset_get_id (GES_ASSET (project)));
+
+ data->timeline = gst_object_ref (timeline);
+ data->project = gst_object_ref (project);
+
+ /* Make sure the signal is emitted after the functions ends */
+ g_idle_add ((GSourceFunc) _emit_loaded_in_idle, data);
+ return TRUE;
+ }
+
+ if (priv->formatter_asset == NULL)
+ priv->formatter_asset = _find_formatter_asset_for_id (priv->uri);
+
+ if (priv->formatter_asset == NULL) {
+ lerr = g_error_new (GES_ERROR, 0, "Could not find a suitable formatter");
+ goto failed;
+ }
+
+ formatter = GES_FORMATTER (ges_asset_extract (priv->formatter_asset, &lerr));
+ if (lerr) {
+ GST_WARNING_OBJECT (project, "Could not create the formatter: %s",
+ lerr->message);
+
+ goto failed;
+ }
+
+ ges_project_add_formatter (GES_PROJECT (project), formatter);
+ ges_formatter_load_from_uri (formatter, timeline, priv->uri, &lerr);
+ if (lerr) {
+ GST_WARNING_OBJECT (project, "Could not load the timeline,"
+ " returning: %s", lerr->message);
+ goto failed;
+ }
+
+ return TRUE;
+
+failed:
+ if (lerr)
+ g_propagate_error (error, lerr);
+ return FALSE;
+}
+
+static gboolean
+_uri_missing_accumulator (GSignalInvocationHint * ihint, GValue * return_accu,
+ const GValue * handler_return, gpointer data)
+{
+ const gchar *ret = g_value_get_string (handler_return);
+
+ if (ret) {
+ if (!gst_uri_is_valid (ret)) {
+ GST_INFO ("The uri %s was not valid, can not work with it!", ret);
+ return TRUE;
+ }
+
+ g_value_set_string (return_accu, ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* GESAsset vmethod implementation */
+static GESExtractable *
+ges_project_extract (GESAsset * project, GError ** error)
+{
+ GESTimeline *timeline = g_object_new (GES_TYPE_TIMELINE, NULL);
+
+ ges_extractable_set_asset (GES_EXTRACTABLE (timeline), GES_ASSET (project));
+ if (_load_project (GES_PROJECT (project), timeline, error))
+ return GES_EXTRACTABLE (timeline);
+
+ gst_object_unref (timeline);
+ return NULL;
+}
+
+static void
+_add_media_new_paths_recursing (const gchar * value)
+{
+ GFileInfo *info;
+ GFileEnumerator *fenum;
+ GFile *file = g_file_new_for_uri (value);
+
+ if (!(fenum = g_file_enumerate_children (file,
+ "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL))) {
+ GST_INFO ("%s is not a folder", value);
+
+ goto done;
+ }
+
+ GST_INFO ("Adding folder: %s", value);
+ g_ptr_array_add (new_paths, g_strdup (value));
+ info = g_file_enumerator_next_file (fenum, NULL, NULL);
+ while (info != NULL) {
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ GFile *f = g_file_enumerator_get_child (fenum, info);
+ gchar *uri = g_file_get_uri (f);
+
+ _add_media_new_paths_recursing (uri);
+ gst_object_unref (f);
+ g_free (uri);
+ }
+ g_object_unref (info);
+ info = g_file_enumerator_next_file (fenum, NULL, NULL);
+ }
+
+done:
+ gst_object_unref (file);
+ if (fenum)
+ gst_object_unref (fenum);
+}
+
+gboolean
+ges_add_missing_uri_relocation_uri (const gchar * uri, gboolean recurse)
+{
+ g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
+
+ if (new_paths == NULL)
+ new_paths = g_ptr_array_new_with_free_func (g_free);
+
+ if (recurse)
+ _add_media_new_paths_recursing (uri);
+ else
+ g_ptr_array_add (new_paths, g_strdup (uri));
+
+ return TRUE;
+}
+
+static gchar *
+ges_missing_uri_default (GESProject * self, GError * error,
+ GESAsset * wrong_asset)
+{
+ guint i;
+ const gchar *old_uri = ges_asset_get_id (wrong_asset);
+ gchar *new_id = NULL;
+
+ if (ges_asset_request_id_update (wrong_asset, &new_id, error) && new_id) {
+ GST_INFO_OBJECT (self, "Returned guessed new ID: %s", new_id);
+
+ return new_id;
+ }
+
+ if (new_paths == NULL)
+ return NULL;
+
+ if (tried_uris == NULL)
+ tried_uris = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ for (i = 0; i < new_paths->len; i++) {
+ gchar *basename, *res;
+
+ basename = g_path_get_basename (old_uri);
+ res = g_build_filename (new_paths->pdata[i], basename, NULL);
+ g_free (basename);
+
+ if (g_strcmp0 (old_uri, res) == 0) {
+ g_hash_table_add (tried_uris, res);
+ } else if (g_hash_table_lookup (tried_uris, res)) {
+ GST_DEBUG_OBJECT (self, "File already tried: %s", res);
+ g_free (res);
+ } else {
+ g_hash_table_add (tried_uris, g_strdup (res));
+ GST_DEBUG_OBJECT (self, "Trying: %s\n", res);
+ return res;
+ }
+ }
+
+ return NULL;
+}
+
+gchar *
+ges_uri_asset_try_update_id (GError * error, GESAsset * wrong_asset)
+{
+ return ges_missing_uri_default (NULL, error, wrong_asset);
+}
+
+static void
+ges_uri_assets_validate_uri (const gchar * nid)
+{
+ if (tried_uris)
+ g_hash_table_remove (tried_uris, nid);
+}
+
+/* GObject vmethod implementation */
+static void
+_dispose (GObject * object)
+{
+ GESProjectPrivate *priv = GES_PROJECT (object)->priv;
+
+ if (priv->assets)
+ g_hash_table_unref (priv->assets);
+ if (priv->loading_assets)
+ g_hash_table_unref (priv->loading_assets);
+ if (priv->loaded_with_error)
+ g_hash_table_unref (priv->loaded_with_error);
+ if (priv->formatter_asset)
+ gst_object_unref (priv->formatter_asset);
+
+ while (priv->formatters)
+ ges_project_remove_formatter (GES_PROJECT (object), priv->formatters->data);
+
+ G_OBJECT_CLASS (ges_project_parent_class)->dispose (object);
+}
+
+static void
+_finalize (GObject * object)
+{
+ GESProjectPrivate *priv = GES_PROJECT (object)->priv;
+
+ if (priv->uri)
+ g_free (priv->uri);
+ g_list_free_full (priv->encoding_profiles, g_object_unref);
+
+ G_OBJECT_CLASS (ges_project_parent_class)->finalize (object);
+}
+
+static void
+_get_property (GESProject * project, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESProjectPrivate *priv = project->priv;
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, priv->uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (project, property_id, pspec);
+ }
+}
+
+static void
+_set_property (GESProject * project, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ case PROP_URI:
+ project->priv->uri = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (project, property_id, pspec);
+ }
+}
+
+static void
+ges_project_class_init (GESProjectClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ klass->asset_added = NULL;
+ klass->missing_uri = ges_missing_uri_default;
+ klass->loading_error = NULL;
+ klass->asset_removed = NULL;
+ object_class->get_property = (GObjectGetPropertyFunc) _get_property;
+ object_class->set_property = (GObjectSetPropertyFunc) _set_property;
+
+ /**
+ * GESProject::uri:
+ *
+ * The location of the project to use.
+ */
+ _properties[PROP_URI] = g_param_spec_string ("uri", "URI",
+ "uri of the project", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, PROP_LAST, _properties);
+
+ /**
+ * GESProject::asset-added:
+ * @project: the #GESProject
+ * @asset: The #GESAsset that has been added to @project
+ */
+ _signals[ASSET_ADDED_SIGNAL] =
+ g_signal_new ("asset-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESProjectClass, asset_added),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_ASSET);
+
+ /**
+ * GESProject::asset-loading:
+ * @project: the #GESProject
+ * @asset: The #GESAsset that started loading
+ *
+ * Since: 1.8
+ */
+ _signals[ASSET_LOADING_SIGNAL] =
+ g_signal_new ("asset-loading", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESProjectClass, asset_loading),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_ASSET);
+
+ /**
+ * GESProject::asset-removed:
+ * @project: the #GESProject
+ * @asset: The #GESAsset that has been removed from @project
+ */
+ _signals[ASSET_REMOVED_SIGNAL] =
+ g_signal_new ("asset-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESProjectClass, asset_removed),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_ASSET);
+
+ /**
+ * GESProject::loaded:
+ * @project: the #GESProject that is done loading a project.
+ * @timeline: The #GESTimeline that complete loading
+ */
+ _signals[LOADED_SIGNAL] =
+ g_signal_new ("loaded", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESProjectClass, loaded),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+ 1, GES_TYPE_TIMELINE);
+
+ /**
+ * GESProject::missing-uri:
+ * @project: the #GESProject reporting that a file has moved
+ * @error: The error that happened
+ * @wrong_asset: The asset with the wrong ID, you should us it and its content
+ * only to find out what the new location is.
+ *
+ * |[
+ * static gchar
+ * source_moved_cb (GESProject *project, GError *error, GESAsset *asset_with_error)
+ * {
+ * return g_strdup ("file:///the/new/uri.ogg");
+ * }
+ *
+ * static int
+ * main (int argc, gchar ** argv)
+ * {
+ * GESTimeline *timeline;
+ * GESProject *project = ges_project_new ("file:///some/uri.xges");
+ *
+ * g_signal_connect (project, "missing-uri", source_moved_cb, NULL);
+ * timeline = ges_asset_extract (GES_ASSET (project));
+ * }
+ * ]|
+ *
+ * Returns: (transfer full) (allow-none): The new URI of @wrong_asset
+ */
+ _signals[MISSING_URI_SIGNAL] =
+ g_signal_new ("missing-uri", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESProjectClass, missing_uri),
+ _uri_missing_accumulator, NULL, g_cclosure_marshal_generic,
+ G_TYPE_STRING, 2, G_TYPE_ERROR, GES_TYPE_ASSET);
+
+ /**
+ * GESProject::error-loading-asset:
+ * @project: the #GESProject on which a problem happend when creted a #GESAsset
+ * @error: The #GError defining the error that occured, might be %NULL
+ * @id: The @id of the asset that failed loading
+ * @extractable_type: The @extractable_type of the asset that
+ * failed loading
+ *
+ * Informs you that a #GESAsset could not be created. In case of
+ * missing GStreamer plugins, the error will be set to #GST_CORE_ERROR
+ * #GST_CORE_ERROR_MISSING_PLUGIN
+ */
+ _signals[ERROR_LOADING_ASSET] =
+ g_signal_new ("error-loading-asset", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESProjectClass, loading_error),
+ NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 3, G_TYPE_ERROR, G_TYPE_STRING, G_TYPE_GTYPE);
+
+ object_class->dispose = _dispose;
+ object_class->finalize = _finalize;
+
+ GES_ASSET_CLASS (klass)->extract = ges_project_extract;
+}
+
+static void
+ges_project_init (GESProject * project)
+{
+ GESProjectPrivate *priv = project->priv =
+ ges_project_get_instance_private (project);
+
+ priv->uri = NULL;
+ priv->formatters = NULL;
+ priv->formatter_asset = NULL;
+ priv->encoding_profiles = NULL;
+ priv->assets = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, gst_object_unref);
+ priv->loading_assets = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, gst_object_unref);
+ priv->loaded_with_error = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+}
+
+static void
+_send_error_loading_asset (GESProject * project, GESAsset * asset,
+ GError * error)
+{
+ const gchar *id = ges_asset_get_id (asset);
+
+ GST_DEBUG_OBJECT (project, "Sending error loading asset for %s", id);
+ g_hash_table_remove (project->priv->loading_assets, id);
+ g_hash_table_add (project->priv->loaded_with_error, g_strdup (id));
+ g_signal_emit (project, _signals[ERROR_LOADING_ASSET], 0, error, id,
+ ges_asset_get_extractable_type (asset));
+}
+
+gchar *
+ges_project_try_updating_id (GESProject * project, GESAsset * asset,
+ GError * error)
+{
+ gchar *new_id = NULL;
+ const gchar *id;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), NULL);
+ g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+ g_return_val_if_fail (error, NULL);
+
+ id = ges_asset_get_id (asset);
+ GST_DEBUG_OBJECT (project, "Try to proxy %s", id);
+ if (ges_asset_request_id_update (asset, &new_id, error) == FALSE) {
+ GST_DEBUG_OBJECT (project, "Type: %s can not be proxied for id: %s "
+ "and error: %s", g_type_name (G_OBJECT_TYPE (asset)), id,
+ error->message);
+ _send_error_loading_asset (project, asset, error);
+
+ return NULL;
+ }
+
+ /* Always send the MISSING_URI signal if requesting new ID is possible
+ * so that subclasses of GESProject are aware of the missing-uri */
+ g_signal_emit (project, _signals[MISSING_URI_SIGNAL], 0, error, asset,
+ &new_id);
+
+ if (new_id) {
+ GST_DEBUG_OBJECT (project, "new id found: %s", new_id);
+ if (!ges_asset_try_proxy (asset, new_id)) {
+ g_free (new_id);
+ new_id = NULL;
+ }
+ } else {
+ GST_DEBUG_OBJECT (project, "No new id found for %s", id);
+ }
+
+ g_hash_table_remove (project->priv->loading_assets, id);
+
+ if (new_id == NULL)
+ _send_error_loading_asset (project, asset, error);
+
+
+ return new_id;
+}
+
+static void
+new_asset_cb (GESAsset * source, GAsyncResult * res, GESProject * project)
+{
+ GError *error = NULL;
+ gchar *possible_id = NULL;
+ GESAsset *asset = ges_asset_request_finish (res, &error);
+
+ if (error) {
+ possible_id = ges_project_try_updating_id (project, source, error);
+ g_clear_error (&error);
+
+ if (possible_id == NULL)
+ return;
+
+ ges_project_create_asset (project, possible_id,
+ ges_asset_get_extractable_type (source));
+
+ g_free (possible_id);
+ return;
+ }
+
+ ges_asset_set_proxy (NULL, asset);
+ ges_project_add_asset (project, asset);
+ if (asset)
+ gst_object_unref (asset);
+}
+
+/**
+ * ges_project_set_loaded:
+ * @project: The #GESProject from which to emit the "project-loaded" signal
+ *
+ * Emits the "loaded" signal. This method should be called by sublasses when
+ * the project is fully loaded.
+ *
+ * Returns: %TRUE if the signale could be emitted %FALSE otherwize
+ */
+gboolean
+ges_project_set_loaded (GESProject * project, GESFormatter * formatter)
+{
+ GST_INFO_OBJECT (project, "Emit project loaded");
+ if (GST_STATE (formatter->timeline) < GST_STATE_PAUSED) {
+ timeline_fill_gaps (formatter->timeline);
+ } else {
+ ges_timeline_commit (formatter->timeline);
+ }
+
+ g_signal_emit (project, _signals[LOADED_SIGNAL], 0, formatter->timeline);
+
+ /* We are now done with that formatter */
+ ges_project_remove_formatter (project, formatter);
+ return TRUE;
+}
+
+void
+ges_project_add_loading_asset (GESProject * project, GType extractable_type,
+ const gchar * id)
+{
+ GESAsset *asset;
+
+ if ((asset = ges_asset_cache_lookup (extractable_type, id))) {
+ if (g_hash_table_insert (project->priv->loading_assets, g_strdup (id),
+ gst_object_ref (asset)))
+ g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, asset);
+ }
+}
+
+/**************************************
+ * *
+ * API Implementation *
+ * *
+ **************************************/
+
+/**
+ * ges_project_create_asset:
+ * @project: A #GESProject
+ * @id: (allow-none): The id of the asset to create and add to @project
+ * @extractable_type: The #GType of the asset to create
+ *
+ * Create and add a #GESAsset to @project. You should connect to the
+ * "asset-added" signal to get the asset when it finally gets added to
+ * @project
+ *
+ * Returns: %TRUE if the asset started to be added %FALSE it was already
+ * in the project
+ */
+gboolean
+ges_project_create_asset (GESProject * project, const gchar * id,
+ GType extractable_type)
+{
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+ g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
+ FALSE);
+
+ if (id == NULL)
+ id = g_type_name (extractable_type);
+
+ if (g_hash_table_lookup (project->priv->assets, id) ||
+ g_hash_table_lookup (project->priv->loading_assets, id) ||
+ g_hash_table_lookup (project->priv->loaded_with_error, id))
+ return FALSE;
+
+ /* TODO Add a GCancellable somewhere in our API */
+ ges_asset_request_async (extractable_type, id, NULL,
+ (GAsyncReadyCallback) new_asset_cb, project);
+ ges_project_add_loading_asset (project, extractable_type, id);
+
+ return TRUE;
+}
+
+/**
+ * ges_project_create_asset_sync:
+ * @project: A #GESProject
+ * @id: (allow-none): The id of the asset to create and add to @project
+ * @extractable_type: The #GType of the asset to create
+ * @error: A #GError to be set in case of error
+ *
+ * Create and add a #GESAsset to @project. You should connect to the
+ * "asset-added" signal to get the asset when it finally gets added to
+ * @project
+ *
+ * Returns: (transfer full) (nullable): The newly created #GESAsset or %NULL.
+ */
+GESAsset *
+ges_project_create_asset_sync (GESProject * project, const gchar * id,
+ GType extractable_type, GError ** error)
+{
+ GESAsset *asset;
+ gchar *possible_id = NULL;
+ gboolean retry = TRUE;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+ g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
+ FALSE);
+
+ if (id == NULL)
+ id = g_type_name (extractable_type);
+
+ if ((asset = g_hash_table_lookup (project->priv->assets, id)))
+ return asset;
+ else if (g_hash_table_lookup (project->priv->loading_assets, id) ||
+ g_hash_table_lookup (project->priv->loaded_with_error, id))
+ return NULL;
+
+ /* TODO Add a GCancellable somewhere in our API */
+ while (retry) {
+
+ if (g_type_is_a (extractable_type, GES_TYPE_URI_CLIP)) {
+ asset = GES_ASSET (ges_uri_clip_asset_request_sync (id, error));
+ } else {
+ asset = ges_asset_request (extractable_type, id, error);
+ }
+
+ if (asset) {
+ retry = FALSE;
+
+ if ((!g_hash_table_lookup (project->priv->assets,
+ ges_asset_get_id (asset))))
+ g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, asset);
+
+ if (possible_id) {
+ g_free (possible_id);
+ ges_uri_assets_validate_uri (id);
+ }
+
+ break;
+ } else {
+ GESAsset *tmpasset;
+
+ tmpasset = ges_asset_cache_lookup (extractable_type, id);
+ possible_id = ges_project_try_updating_id (project, tmpasset, *error);
+
+ if (possible_id == NULL) {
+ g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, tmpasset);
+ g_signal_emit (project, _signals[ERROR_LOADING_ASSET], 0, *error, id,
+ extractable_type);
+ return NULL;
+ }
+
+
+ g_clear_error (error);
+
+ id = possible_id;
+ }
+ }
+
+ if (!ges_asset_get_proxy_target (asset))
+ ges_asset_set_proxy (NULL, asset);
+
+ ges_project_add_asset (project, asset);
+
+ return asset;
+}
+
+/**
+ * ges_project_add_asset:
+ * @project: A #GESProject
+ * @asset: (transfer none): A #GESAsset to add to @project
+ *
+ * Adds a #Asset to @project, the project will keep a reference on
+ * @asset.
+ *
+ * Returns: %TRUE if the asset could be added %FALSE it was already
+ * in the project
+ */
+gboolean
+ges_project_add_asset (GESProject * project, GESAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+
+ if (g_hash_table_lookup (project->priv->assets, ges_asset_get_id (asset)))
+ return TRUE;
+
+ g_hash_table_insert (project->priv->assets,
+ g_strdup (ges_asset_get_id (asset)), gst_object_ref (asset));
+
+ g_hash_table_remove (project->priv->loading_assets, ges_asset_get_id (asset));
+ GST_DEBUG_OBJECT (project, "Asset added: %s", ges_asset_get_id (asset));
+ g_signal_emit (project, _signals[ASSET_ADDED_SIGNAL], 0, asset);
+
+ return TRUE;
+}
+
+/**
+ * ges_project_remove_asset:
+ * @project: A #GESProject
+ * @asset: (transfer none): A #GESAsset to remove from @project
+ *
+ * remove a @asset to from @project.
+ *
+ * Returns: %TRUE if the asset could be removed %FALSE otherwise
+ */
+gboolean
+ges_project_remove_asset (GESProject * project, GESAsset * asset)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+
+ ret = g_hash_table_remove (project->priv->assets, ges_asset_get_id (asset));
+ g_signal_emit (project, _signals[ASSET_REMOVED_SIGNAL], 0, asset);
+
+ return ret;
+}
+
+/**
+ * ges_project_get_asset:
+ * @project: A #GESProject
+ * @id: The id of the asset to retrieve
+ * @extractable_type: The extractable_type of the asset
+ * to retrieve from @object
+ *
+ * Returns: (transfer full) (allow-none): The #GESAsset with
+ * @id or %NULL if no asset with @id as an ID
+ */
+GESAsset *
+ges_project_get_asset (GESProject * project, const gchar * id,
+ GType extractable_type)
+{
+ GESAsset *asset;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), NULL);
+ g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
+ NULL);
+
+ asset = g_hash_table_lookup (project->priv->assets, id);
+
+ if (asset)
+ return gst_object_ref (asset);
+
+ return NULL;
+}
+
+/**
+ * ges_project_list_assets:
+ * @project: A #GESProject
+ * @filter: Type of assets to list, #GES_TYPE_EXTRACTABLE will list
+ * all assets
+ *
+ * List all @asset contained in @project filtering per extractable_type
+ * as defined by @filter. It copies the asset and thus will not be updated
+ * in time.
+ *
+ * Returns: (transfer full) (element-type GESAsset): The list of
+ * #GESAsset the object contains
+ */
+GList *
+ges_project_list_assets (GESProject * project, GType filter)
+{
+ GList *ret = NULL;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), NULL);
+
+ g_hash_table_iter_init (&iter, project->priv->assets);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (g_type_is_a (ges_asset_get_extractable_type (GES_ASSET (value)),
+ filter))
+ ret = g_list_append (ret, gst_object_ref (value));
+ }
+
+ return ret;
+}
+
+/**
+ * ges_project_save:
+ * @project: A #GESProject to save
+ * @timeline: The #GESTimeline to save, it must have been extracted from @project
+ * @uri: The uri where to save @project and @timeline
+ * @formatter_asset: (allow-none): The formatter asset to use or %NULL. If %NULL,
+ * will try to save in the same format as the one from which the timeline as been loaded
+ * or default to the formatter with highest rank
+ * @overwrite: %TRUE to overwrite file if it exists
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Save the timeline of @project to @uri. You should make sure that @timeline
+ * is one of the timelines that have been extracted from @project
+ * (using ges_asset_extract (@project);)
+ *
+ * Returns: %TRUE if the project could be save, %FALSE otherwize
+ */
+gboolean
+ges_project_save (GESProject * project, GESTimeline * timeline,
+ const gchar * uri, GESAsset * formatter_asset, gboolean overwrite,
+ GError ** error)
+{
+ GESAsset *tl_asset;
+ gboolean ret = TRUE;
+ GESFormatter *formatter = NULL;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+ g_return_val_if_fail (formatter_asset == NULL ||
+ g_type_is_a (ges_asset_get_extractable_type (formatter_asset),
+ GES_TYPE_FORMATTER), FALSE);
+ g_return_val_if_fail ((error == NULL || *error == NULL), FALSE);
+
+ tl_asset = ges_extractable_get_asset (GES_EXTRACTABLE (timeline));
+ if (tl_asset == NULL && project->priv->uri == NULL) {
+ GESAsset *asset = ges_asset_cache_lookup (GES_TYPE_PROJECT, uri);
+
+ if (asset) {
+ GST_WARNING_OBJECT (project, "Trying to save project to %s but we already"
+ "have %" GST_PTR_FORMAT " for that uri, can not save", uri, asset);
+ goto out;
+ }
+
+ GST_DEBUG_OBJECT (project, "Timeline %" GST_PTR_FORMAT " has no asset"
+ " we have no uri set, so setting ourself as asset", timeline);
+
+ ges_extractable_set_asset (GES_EXTRACTABLE (timeline), GES_ASSET (project));
+ } else if (tl_asset != GES_ASSET (project)) {
+ GST_WARNING_OBJECT (project, "Timeline %" GST_PTR_FORMAT
+ " not created by this project can not save", timeline);
+
+ ret = FALSE;
+ goto out;
+ }
+
+ if (formatter_asset == NULL)
+ formatter_asset = gst_object_ref (ges_formatter_get_default ());
+
+ formatter = GES_FORMATTER (ges_asset_extract (formatter_asset, error));
+ if (formatter == NULL) {
+ GST_WARNING_OBJECT (project, "Could not create the formatter %p %s: %s",
+ formatter_asset, ges_asset_get_id (formatter_asset),
+ (error && *error) ? (*error)->message : "Unknown Error");
+
+ ret = FALSE;
+ goto out;
+ }
+
+ ges_project_add_formatter (project, formatter);
+ ret = ges_formatter_save_to_uri (formatter, timeline, uri, overwrite, error);
+ if (ret && project->priv->uri == NULL)
+ ges_project_set_uri (project, uri);
+
+out:
+ if (formatter_asset)
+ gst_object_unref (formatter_asset);
+ ges_project_remove_formatter (project, formatter);
+
+ return ret;
+}
+
+/**
+ * ges_project_new:
+ * @uri: (allow-none): The uri to be set after creating the project.
+ *
+ * Creates a new #GESProject and sets its uri to @uri if provided. Note that
+ * if @uri is not valid or %NULL, the uri of the project will then be set
+ * the first time you save the project. If you then save the project to
+ * other locations, it will never be updated again and the first valid URI is
+ * the URI it will keep refering to.
+ *
+ * Returns: A newly created #GESProject
+ */
+GESProject *
+ges_project_new (const gchar * uri)
+{
+ gchar *id = (gchar *) uri;
+ GESProject *project;
+
+ if (uri == NULL)
+ id = g_strdup_printf ("project-%i", nb_projects++);
+
+ project = GES_PROJECT (ges_asset_request (GES_TYPE_TIMELINE, id, NULL));
+
+ if (project && uri)
+ ges_project_set_uri (project, uri);
+
+ if (uri == NULL)
+ g_free (id);
+
+ return project;
+}
+
+/**
+ * ges_project_load:
+ * @project: A #GESProject that has an @uri set already
+ * @timeline: A blank timeline to load @project into
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Loads @project into @timeline
+ *
+ * Returns: %TRUE if the project could be loaded %FALSE otherwize.
+ */
+gboolean
+ges_project_load (GESProject * project, GESTimeline * timeline, GError ** error)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+ g_return_val_if_fail (ges_project_get_uri (project), FALSE);
+ g_return_val_if_fail (timeline->tracks == NULL, FALSE);
+
+ if (!_load_project (project, timeline, error))
+ return FALSE;
+
+ ges_extractable_set_asset (GES_EXTRACTABLE (timeline), GES_ASSET (project));
+
+ return TRUE;
+}
+
+/**
+ * ges_project_get_uri:
+ * @project: A #GESProject
+ *
+ * Retrieve the uri that is currently set on @project
+ *
+ * Returns: (transfer full) (nullable): a newly allocated string representing uri.
+ */
+gchar *
+ges_project_get_uri (GESProject * project)
+{
+ GESProjectPrivate *priv;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+
+ priv = project->priv;
+ if (priv->uri)
+ return g_strdup (priv->uri);
+ return NULL;
+}
+
+/**
+ * ges_project_add_encoding_profile:
+ * @project: A #GESProject
+ * @profile: A #GstEncodingProfile to add to the project. If a profile with
+ * the same name already exists, it will be replaced
+ *
+ * Adds @profile to the project. It lets you save in what format
+ * the project has been renders and keep a reference to those formats.
+ * Also, those formats will be saves to the project file when possible.
+ *
+ * Returns: %TRUE if @profile could be added, %FALSE otherwize
+ */
+gboolean
+ges_project_add_encoding_profile (GESProject * project,
+ GstEncodingProfile * profile)
+{
+ GList *tmp;
+ GESProjectPrivate *priv;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+ g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE);
+
+ priv = project->priv;
+ for (tmp = priv->encoding_profiles; tmp; tmp = tmp->next) {
+ GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data);
+
+ if (g_strcmp0 (gst_encoding_profile_get_name (tmpprofile),
+ gst_encoding_profile_get_name (profile)) == 0) {
+ GST_INFO_OBJECT (project, "Already have profile: %s, replacing it",
+ gst_encoding_profile_get_name (profile));
+
+ gst_object_unref (tmp->data);
+ tmp->data = gst_object_ref (profile);
+ return TRUE;
+ }
+ }
+
+ priv->encoding_profiles = g_list_prepend (priv->encoding_profiles,
+ gst_object_ref (profile));
+
+ return TRUE;
+}
+
+/**
+ * ges_project_list_encoding_profiles:
+ * @project: A #GESProject
+ *
+ * Lists the encoding profile that have been set to @project. The first one
+ * is the latest added.
+ *
+ * Returns: (transfer none) (element-type GstPbutils.EncodingProfile) (allow-none): The
+ * list of #GstEncodingProfile used in @project
+ */
+const GList *
+ges_project_list_encoding_profiles (GESProject * project)
+{
+ g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
+
+ return project->priv->encoding_profiles;
+}
+
+/**
+ * ges_project_get_loading_assets:
+ * @project: A #GESProject
+ *
+ * Get the assets that are being loaded
+ *
+ * Returns: (transfer full) (element-type GES.Asset): A set of loading asset
+ * that will be added to @project. Note that those Asset are *not* loaded yet,
+ * and thus can not be used
+ */
+GList *
+ges_project_get_loading_assets (GESProject * project)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ GList *ret = NULL;
+
+ g_return_val_if_fail (GES_IS_PROJECT (project), NULL);
+
+ g_hash_table_iter_init (&iter, project->priv->loading_assets);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ ret = g_list_prepend (ret, gst_object_ref (value));
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2012> 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 _GES_PROJECT_
+#define _GES_PROJECT_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <ges/ges-types.h>
+#include <ges/ges-asset.h>
+#include <gst/pbutils/encoding-profile.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_PROJECT ges_project_get_type()
+#define GES_PROJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_PROJECT, GESProject))
+#define GES_PROJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_PROJECT, GESProjectClass))
+#define GES_IS_PROJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_PROJECT))
+#define GES_IS_PROJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_PROJECT))
+#define GES_PROJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_PROJECT, GESProjectClass))
+
+typedef struct _GESProjectPrivate GESProjectPrivate;
+
+GES_API
+GType ges_project_get_type (void);
+
+struct _GESProject
+{
+ GESAsset parent;
+
+ /* <private> */
+ GESProjectPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer __ges_reserved[GES_PADDING_LARGE];
+};
+
+struct _GESProjectClass
+{
+ GESAssetClass parent_class;
+
+ /* Signals */
+ void (*asset_added) (GESProject * self,
+ GESAsset * asset);
+ void (*asset_loading) (GESProject * self,
+ GESAsset * asset);
+ void (*asset_removed) (GESProject * self,
+ GESAsset * asset);
+ gchar * (*missing_uri) (GESProject * self,
+ GError * error,
+ GESAsset * wrong_asset);
+ gboolean (*loading_error) (GESProject * self,
+ GError * error,
+ gchar * id,
+ GType extractable_type);
+ gboolean (*loaded) (GESProject * self,
+ GESTimeline * timeline);
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+gboolean ges_project_add_asset (GESProject* project,
+ GESAsset *asset);
+GES_API
+gboolean ges_project_remove_asset (GESProject *project,
+ GESAsset * asset);
+GES_API
+GList * ges_project_list_assets (GESProject * project,
+ GType filter);
+GES_API
+gboolean ges_project_save (GESProject * project,
+ GESTimeline * timeline,
+ const gchar *uri,
+ GESAsset * formatter_asset,
+ gboolean overwrite,
+ GError **error);
+GES_API
+gboolean ges_project_load (GESProject * project,
+ GESTimeline * timeline,
+ GError **error);
+GES_API
+GESProject * ges_project_new (const gchar *uri);
+GES_API
+gchar * ges_project_get_uri (GESProject *project);
+GES_API
+GESAsset * ges_project_get_asset (GESProject * project,
+ const gchar *id,
+ GType extractable_type);
+GES_API
+gboolean ges_project_create_asset (GESProject * project,
+ const gchar *id,
+ GType extractable_type);
+
+GES_API
+GESAsset * ges_project_create_asset_sync (GESProject * project,
+ const gchar * id,
+ GType extractable_type,
+ GError **error);
+GES_API
+GList * ges_project_get_loading_assets (GESProject * project);
+
+GES_API
+gboolean ges_project_add_encoding_profile (GESProject *project,
+ GstEncodingProfile *profile);
+GES_API
+const GList *ges_project_list_encoding_profiles (GESProject *project);
+GES_API
+gboolean ges_add_missing_uri_relocation_uri (const gchar * uri,
+ gboolean recurse);
+
+G_END_DECLS
+
+#endif /* _GES_PROJECT */
--- /dev/null
+/* Small helper element for format conversion
+ * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include "ges-screenshot.h"
+#include "ges-internal.h"
+
+/**
+ * ges_play_sink_convert_frame:
+ * @playsink: The playsink to get last frame from
+ * @caps: The caps defining the format the return value will have
+ *
+ * Get the last buffer @playsink showed
+ *
+ * Returns: (transfer full): A #GstSample containing the last frame from
+ * @playsink in the format defined by the @caps
+ */
+GstSample *
+ges_play_sink_convert_frame (GstElement * playsink, GstCaps * caps)
+{
+ GstSample *sample = NULL;
+
+ g_signal_emit_by_name (playsink, "convert-sample", caps, &sample);
+
+ return sample;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 __GES_SCREENSHOT_H__
+#define __GES_SCREENSHOT_H__
+
+#include <gst/gst.h>
+#include <ges/ges-prelude.h>
+
+G_BEGIN_DECLS
+
+GES_API GstSample *
+ges_play_sink_convert_frame (GstElement * playsink, GstCaps * caps);
+
+G_END_DECLS
+
+#endif /* __GES_SCREENSHOT_H__ */
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gst-editing-services
+ *
+ * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org>
+
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/audio/audio.h>
+
+#include "ges-types.h"
+#include "ges-internal.h"
+#include "ges-smart-adder.h"
+
+G_DEFINE_TYPE (GESSmartAdder, ges_smart_adder, GST_TYPE_BIN);
+
+#define GET_LOCK(obj) (&((GESSmartAdder*)(obj))->lock)
+#define LOCK(obj) (g_mutex_lock (GET_LOCK(obj)))
+#define UNLOCK(obj) (g_mutex_unlock (GET_LOCK(obj)))
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw")
+ );
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS ("audio/x-raw")
+ );
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define DEFAULT_CAPS "audio/x-raw,format=(string)S32LE;"
+#else
+#define DEFAULT_CAPS "audio/x-raw,format=(string)S32BE;"
+#endif
+
+typedef struct _PadInfos
+{
+ GESSmartAdder *self;
+ GstPad *adder_pad;
+ GstElement *bin;
+} PadInfos;
+
+static void
+destroy_pad (PadInfos * infos)
+{
+ if (G_LIKELY (infos->bin)) {
+ gst_element_set_state (infos->bin, GST_STATE_NULL);
+ gst_element_unlink (infos->bin, infos->self->adder);
+ gst_bin_remove (GST_BIN (infos->self), infos->bin);
+ }
+
+ if (infos->adder_pad) {
+ gst_element_release_request_pad (infos->self->adder, infos->adder_pad);
+ gst_object_unref (infos->adder_pad);
+ }
+ g_slice_free (PadInfos, infos);
+}
+
+/****************************************************
+ * GstElement vmetods *
+ ****************************************************/
+static GstPad *
+_request_new_pad (GstElement * element, GstPadTemplate * templ,
+ const gchar * name, const GstCaps * caps)
+{
+ GstPad *audioresample_srcpad, *audioconvert_sinkpad, *tmpghost;
+ GstPad *ghost;
+ GstElement *audioconvert, *audioresample;
+ PadInfos *infos = g_slice_new0 (PadInfos);
+ GESSmartAdder *self = GES_SMART_ADDER (element);
+
+ infos->adder_pad = gst_element_request_pad (self->adder,
+ gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self->adder),
+ "sink_%u"), NULL, caps);
+
+ if (infos->adder_pad == NULL) {
+ GST_WARNING_OBJECT (element, "Could not get any pad from GstAdder");
+ g_slice_free (PadInfos, infos);
+
+ return NULL;
+ }
+
+ infos->self = self;
+
+ infos->bin = gst_bin_new (NULL);
+ audioconvert = gst_element_factory_make ("audioconvert", NULL);
+ audioresample = gst_element_factory_make ("audioresample", NULL);
+
+ gst_bin_add_many (GST_BIN (infos->bin), audioconvert, audioresample, NULL);
+ gst_element_link_many (audioconvert, audioresample, NULL);
+
+ audioconvert_sinkpad = gst_element_get_static_pad (audioconvert, "sink");
+ tmpghost = GST_PAD (gst_ghost_pad_new (NULL, audioconvert_sinkpad));
+ gst_object_unref (audioconvert_sinkpad);
+ gst_pad_set_active (tmpghost, TRUE);
+ gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
+
+ gst_bin_add (GST_BIN (self), infos->bin);
+ ghost = gst_ghost_pad_new (NULL, tmpghost);
+ gst_pad_set_active (ghost, TRUE);
+ if (!gst_element_add_pad (GST_ELEMENT (self), ghost))
+ goto could_not_add;
+
+ audioresample_srcpad = gst_element_get_static_pad (audioresample, "src");
+ tmpghost = GST_PAD (gst_ghost_pad_new (NULL, audioresample_srcpad));
+ gst_object_unref (audioresample_srcpad);
+ gst_pad_set_active (tmpghost, TRUE);
+ gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
+ gst_pad_link (tmpghost, infos->adder_pad);
+
+ LOCK (self);
+ g_hash_table_insert (self->pads_infos, ghost, infos);
+ UNLOCK (self);
+
+ GST_DEBUG_OBJECT (self, "Returning new pad %" GST_PTR_FORMAT, ghost);
+ return ghost;
+
+could_not_add:
+ {
+ GST_ERROR_OBJECT (self, "could not add pad");
+ destroy_pad (infos);
+ return NULL;
+ }
+}
+
+static void
+_release_pad (GstElement * element, GstPad * pad)
+{
+ GST_DEBUG_OBJECT (element, "Releasing pad %" GST_PTR_FORMAT, pad);
+
+ LOCK (element);
+ g_hash_table_remove (GES_SMART_ADDER (element)->pads_infos, pad);
+ UNLOCK (element);
+}
+
+/****************************************************
+ * GObject vmethods *
+ ****************************************************/
+static void
+ges_smart_adder_dispose (GObject * object)
+{
+ GESSmartAdder *self = GES_SMART_ADDER (object);
+
+ if (self->pads_infos) {
+ g_hash_table_unref (self->pads_infos);
+ self->pads_infos = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_smart_adder_parent_class)->dispose (object);
+}
+
+static void
+ges_smart_adder_finalize (GObject * object)
+{
+ GESSmartAdder *self = GES_SMART_ADDER (object);
+
+ g_mutex_clear (&self->lock);
+
+ G_OBJECT_CLASS (ges_smart_adder_parent_class)->finalize (object);
+}
+
+static void
+ges_smart_adder_class_init (GESSmartAdderClass * klass)
+{
+/* GstBinClass *parent_class = GST_BIN_CLASS (klass);
+ */
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ /* FIXME Make sure the AdderClass doesn get destroy before ourself */
+ gst_element_class_add_static_pad_template (element_class, &src_template);
+ gst_element_class_add_static_pad_template (element_class, &sink_template);
+ gst_element_class_set_static_metadata (element_class, "GES Smart adder",
+ "Generic/Audio",
+ "Use adder making use of GES informations",
+ "Thibault Saunier <thibault.saunier@collabora.com>");
+
+ element_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
+ element_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);
+
+ object_class->dispose = ges_smart_adder_dispose;
+ object_class->finalize = ges_smart_adder_finalize;
+}
+
+static void
+ges_smart_adder_init (GESSmartAdder * self)
+{
+ GstPad *pad;
+
+ g_mutex_init (&self->lock);
+
+ self->adder = gst_element_factory_make ("audiomixer", "smart-adder-adder");
+ gst_bin_add (GST_BIN (self), self->adder);
+
+ self->capsfilter =
+ gst_element_factory_make ("capsfilter", "smart-adder-capsfilter");
+ gst_bin_add (GST_BIN (self), self->capsfilter);
+
+ gst_element_link (self->adder, self->capsfilter);
+
+ pad = gst_element_get_static_pad (self->capsfilter, "src");
+ self->srcpad = gst_ghost_pad_new ("src", pad);
+ gst_pad_set_active (self->srcpad, TRUE);
+ gst_object_unref (pad);
+
+ gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
+
+ self->pads_infos = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) destroy_pad);
+}
+
+static void
+restriction_caps_cb (GESTrack * track,
+ GParamSpec * arg G_GNUC_UNUSED, GESSmartAdder * self)
+{
+ GstCaps *caps;
+
+ g_object_get (track, "restriction-caps", &caps, NULL);
+
+ if (!caps)
+ caps = gst_caps_from_string (DEFAULT_CAPS);
+
+ GST_DEBUG_OBJECT (self, "Setting adder caps to %" GST_PTR_FORMAT, caps);
+ g_object_set (self->capsfilter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+}
+
+GstElement *
+ges_smart_adder_new (GESTrack * track)
+{
+ GESSmartAdder *self;
+
+ self = g_object_new (GES_TYPE_SMART_ADDER, NULL);
+
+ self->track = track;
+
+ if (track) {
+ restriction_caps_cb (track, NULL, self);
+
+ g_signal_connect (track, "notify::restriction-caps",
+ G_CALLBACK (restriction_caps_cb), self);
+ }
+
+ /* FIXME Make adder smart and let it properly negotiate caps! */
+
+ return GST_ELEMENT (self);
+}
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GES_SMART_ADDER_H_
+#define _GES_SMART_ADDER_H_
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "ges-track.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_SMART_ADDER (ges_smart_adder_get_type ())
+#define GES_SMART_ADDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SMART_ADDER, GESSmartAdder))
+#define GES_SMART_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SMART_ADDER, GESSmartAdderClass))
+#define GES_IS_SMART_ADDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SMART_ADDER))
+#define GES_IS_SMART_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SMART_ADDER))
+#define GES_SMART_ADDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SMART_ADDER, GESSmartAdderClass))
+
+typedef struct _GESSmartAdderClass GESSmartAdderClass;
+typedef struct _GESSmartAdder GESSmartAdder;
+
+struct _GESSmartAdderClass
+{
+ GstBinClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESSmartAdder
+{
+ GstBin parent_instance;
+
+ GHashTable *pads_infos;
+ GstPad *srcpad;
+ GstElement *adder;
+ GstElement *capsfilter;
+ GMutex lock;
+
+ GstCaps *caps;
+
+ GESTrack *track;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_smart_adder_get_type (void) G_GNUC_CONST;
+
+GES_API
+GstElement* ges_smart_adder_new (GESTrack *track);
+
+G_END_DECLS
+#endif /* _GES_SMART_ADDER_H_ */
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gst-editing-services
+ *
+ * Copyright (C) 2013 Mathieu Duponchelle <mduponchelle1@gmail.com>
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstframepositioner.h"
+#include "ges-types.h"
+#include "ges-internal.h"
+#include "ges-smart-video-mixer.h"
+
+#define GES_TYPE_SMART_MIXER_PAD (ges_smart_mixer_pad_get_type ())
+#define GES_SMART_MIXER_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SMART_MIXER_PAD, GESSmartMixerPad))
+#define GES_SMART_MIXER_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SMART_MIXER_PAD, GESSmartMixerPadClass))
+#define GES_IS_SMART_MIXER_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SMART_MIXER_PAD))
+#define GES_IS_SMART_MIXER_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SMART_MIXER_PAD))
+#define GES_SMART_MIXER_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SMART_MIXER_PAD, GESSmartMixerPadClass))
+
+typedef struct _GESSmartMixerPad GESSmartMixerPad;
+typedef struct _GESSmartMixerPadClass GESSmartMixerPadClass;
+
+struct _GESSmartMixerPad
+{
+ GstGhostPad parent;
+
+ gdouble alpha;
+ GstSegment segment;
+};
+
+struct _GESSmartMixerPadClass
+{
+ GstGhostPadClass parent_class;
+};
+
+enum
+{
+ PROP_PAD_0,
+ PROP_PAD_ALPHA,
+};
+
+static GType ges_smart_mixer_pad_get_type (void);
+
+G_DEFINE_TYPE (GESSmartMixerPad, ges_smart_mixer_pad, GST_TYPE_GHOST_PAD);
+
+static void
+ges_smart_mixer_pad_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESSmartMixerPad *pad = GES_SMART_MIXER_PAD (object);
+
+ switch (prop_id) {
+ case PROP_PAD_ALPHA:
+ g_value_set_double (value, pad->alpha);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ges_smart_mixer_pad_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESSmartMixerPad *pad = GES_SMART_MIXER_PAD (object);
+
+ switch (prop_id) {
+ case PROP_PAD_ALPHA:
+ pad->alpha = g_value_get_double (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ges_smart_mixer_pad_init (GESSmartMixerPad * self)
+{
+ gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
+}
+
+static void
+ges_smart_mixer_pad_class_init (GESSmartMixerPadClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->get_property = ges_smart_mixer_pad_get_property;
+ gobject_class->set_property = ges_smart_mixer_pad_set_property;
+
+ g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
+ g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
+ 1.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
+}
+
+G_DEFINE_TYPE (GESSmartMixer, ges_smart_mixer, GST_TYPE_BIN);
+
+#define GET_LOCK(obj) (&((GESSmartMixer*)(obj))->lock)
+#define LOCK(obj) (g_mutex_lock (GET_LOCK(obj)))
+#define UNLOCK(obj) (g_mutex_unlock (GET_LOCK(obj)))
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw")
+ );
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS ("video/x-raw")
+ );
+
+typedef struct _PadInfos
+{
+ GESSmartMixer *self;
+ GstPad *mixer_pad;
+ GstElement *bin;
+ gulong probe_id;
+} PadInfos;
+
+static void
+destroy_pad (PadInfos * infos)
+{
+ gst_pad_remove_probe (infos->mixer_pad, infos->probe_id);
+
+ if (G_LIKELY (infos->bin)) {
+ gst_element_set_state (infos->bin, GST_STATE_NULL);
+ gst_element_unlink (infos->bin, infos->self->mixer);
+ gst_bin_remove (GST_BIN (infos->self), infos->bin);
+ }
+
+ if (infos->mixer_pad) {
+ gst_element_release_request_pad (infos->self->mixer, infos->mixer_pad);
+ gst_object_unref (infos->mixer_pad);
+ }
+
+ g_slice_free (PadInfos, infos);
+}
+
+GstPad *
+ges_smart_mixer_get_mixer_pad (GESSmartMixer * self, GstPad ** mixerpad)
+{
+ PadInfos *info;
+ GstPad *sinkpad;
+
+ sinkpad = gst_element_get_request_pad (GST_ELEMENT (self), "sink_%u");
+
+ if (sinkpad == NULL)
+ return NULL;
+
+ info = g_hash_table_lookup (self->pads_infos, sinkpad);
+ *mixerpad = gst_object_ref (info->mixer_pad);
+
+ return sinkpad;
+}
+
+/* These metadata will get set by the upstream framepositioner element,
+ added in the video sources' bin */
+static GstPadProbeReturn
+parse_metadata (GstPad * mixer_pad, GstPadProbeInfo * info,
+ GESSmartMixerPad * ghost)
+{
+ GstFramePositionerMeta *meta;
+ GESSmartMixer *self = GES_SMART_MIXER (GST_OBJECT_PARENT (ghost));
+
+ meta =
+ (GstFramePositionerMeta *) gst_buffer_get_meta ((GstBuffer *) info->data,
+ gst_frame_positioner_meta_api_get_type ());
+
+ if (!meta) {
+ GST_WARNING ("The current source should use a framepositioner");
+ return GST_PAD_PROBE_OK;
+ }
+
+ if (!self->disable_zorder_alpha) {
+ g_object_set (mixer_pad, "alpha", meta->alpha,
+ "zorder", meta->zorder, NULL);
+ } else {
+ gint64 stream_time;
+ gdouble transalpha;
+
+ GST_OBJECT_LOCK (ghost);
+ if (ghost->segment.format == GST_FORMAT_UNDEFINED) {
+ const GstSegment *seg;
+ GstEvent *segev;
+
+ GST_OBJECT_UNLOCK (ghost);
+ segev = gst_pad_get_sticky_event (GST_PAD (ghost), GST_EVENT_SEGMENT, 0);
+ gst_event_parse_segment (segev, &seg);
+ gst_event_unref (segev);
+ GST_OBJECT_LOCK (ghost);
+
+ ghost->segment = *seg;
+
+ }
+
+ stream_time = gst_segment_to_stream_time (&ghost->segment, GST_FORMAT_TIME,
+ GST_BUFFER_PTS (info->data));
+ GST_OBJECT_UNLOCK (ghost);
+
+ if (GST_CLOCK_TIME_IS_VALID (stream_time))
+ gst_object_sync_values (GST_OBJECT (ghost), stream_time);
+
+ g_object_get (ghost, "alpha", &transalpha, NULL);
+ g_object_set (mixer_pad, "alpha", meta->alpha * transalpha, NULL);
+ }
+
+ g_object_set (mixer_pad, "xpos", meta->posx, "ypos",
+ meta->posy, "width", meta->width, "height", meta->height, NULL);
+
+ return GST_PAD_PROBE_OK;
+}
+
+/****************************************************
+ * GstElement vmetods *
+ ****************************************************/
+static GstPad *
+_request_new_pad (GstElement * element, GstPadTemplate * templ,
+ const gchar * name, const GstCaps * caps)
+{
+ GstPad *videoconvert_srcpad, *videoconvert_sinkpad, *tmpghost;
+ PadInfos *infos = g_slice_new0 (PadInfos);
+ GESSmartMixer *self = GES_SMART_MIXER (element);
+ GstPad *ghost;
+ GstElement *videoconvert;
+
+ infos->mixer_pad = gst_element_request_pad (self->mixer,
+ gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self->mixer),
+ "sink_%u"), NULL, NULL);
+
+ if (infos->mixer_pad == NULL) {
+ GST_WARNING_OBJECT (element, "Could not get any pad from GstMixer");
+ g_slice_free (PadInfos, infos);
+
+ return NULL;
+ }
+
+ infos->self = self;
+
+ infos->bin = gst_bin_new (NULL);
+ videoconvert = gst_element_factory_make ("videoconvert", NULL);
+
+ gst_bin_add (GST_BIN (infos->bin), videoconvert);
+
+ videoconvert_sinkpad = gst_element_get_static_pad (videoconvert, "sink");
+ tmpghost = GST_PAD (gst_ghost_pad_new (NULL, videoconvert_sinkpad));
+ gst_object_unref (videoconvert_sinkpad);
+ gst_pad_set_active (tmpghost, TRUE);
+ gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
+
+ gst_bin_add (GST_BIN (self), infos->bin);
+ ghost = g_object_new (ges_smart_mixer_pad_get_type (), "name", name,
+ "direction", GST_PAD_DIRECTION (tmpghost), NULL);
+ gst_ghost_pad_construct (GST_GHOST_PAD (ghost));
+ gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghost), tmpghost);
+ gst_pad_set_active (ghost, TRUE);
+ if (!gst_element_add_pad (GST_ELEMENT (self), ghost))
+ goto could_not_add;
+
+ videoconvert_srcpad = gst_element_get_static_pad (videoconvert, "src");
+ tmpghost = GST_PAD (gst_ghost_pad_new (NULL, videoconvert_srcpad));
+ gst_object_unref (videoconvert_srcpad);
+ gst_pad_set_active (tmpghost, TRUE);
+ gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
+ gst_pad_link (tmpghost, infos->mixer_pad);
+
+ infos->probe_id =
+ gst_pad_add_probe (infos->mixer_pad, GST_PAD_PROBE_TYPE_BUFFER,
+ (GstPadProbeCallback) parse_metadata, ghost, NULL);
+
+ LOCK (self);
+ g_hash_table_insert (self->pads_infos, ghost, infos);
+ UNLOCK (self);
+
+ GST_DEBUG_OBJECT (self, "Returning new pad %" GST_PTR_FORMAT, ghost);
+ return ghost;
+
+could_not_add:
+ {
+ GST_ERROR_OBJECT (self, "could not add pad");
+ destroy_pad (infos);
+ return NULL;
+ }
+}
+
+static void
+_release_pad (GstElement * element, GstPad * pad)
+{
+ GstPad *peer;
+ GST_DEBUG_OBJECT (element, "Releasing pad %" GST_PTR_FORMAT, pad);
+
+ LOCK (element);
+ g_hash_table_remove (GES_SMART_MIXER (element)->pads_infos, pad);
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ gst_pad_unlink (peer, pad);
+
+ gst_object_unref (peer);
+ }
+ gst_pad_set_active (pad, FALSE);
+ gst_element_remove_pad (element, pad);
+ UNLOCK (element);
+}
+
+/****************************************************
+ * GObject vmethods *
+ ****************************************************/
+static void
+ges_smart_mixer_dispose (GObject * object)
+{
+ GESSmartMixer *self = GES_SMART_MIXER (object);
+
+ if (self->pads_infos != NULL) {
+ g_hash_table_unref (self->pads_infos);
+ self->pads_infos = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_smart_mixer_parent_class)->dispose (object);
+}
+
+static void
+ges_smart_mixer_finalize (GObject * object)
+{
+ GESSmartMixer *self = GES_SMART_MIXER (object);
+
+ g_mutex_clear (&self->lock);
+
+ G_OBJECT_CLASS (ges_smart_mixer_parent_class)->finalize (object);
+}
+
+static void
+ges_smart_mixer_constructed (GObject * obj)
+{
+ GstPad *pad;
+ GstElement *identity;
+
+ GESSmartMixer *self = GES_SMART_MIXER (obj);
+ gchar *cname = g_strdup_printf ("%s-compositor", GST_OBJECT_NAME (self));
+
+ self->mixer =
+ gst_element_factory_create (ges_get_compositor_factory (), cname);
+ g_free (cname);
+ g_object_set (self->mixer, "background", 1, NULL);
+
+ /* See https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/310 */
+ GST_FIXME ("Stop dropping allocation query when it is not required anymore.");
+ identity = gst_element_factory_make ("identity", NULL);
+ g_object_set (identity, "drop-allocation", TRUE, NULL);
+ g_assert (identity);
+
+ gst_bin_add_many (GST_BIN (self), self->mixer, identity, NULL);
+ gst_element_link (self->mixer, identity);
+
+ pad = gst_element_get_static_pad (identity, "src");
+ self->srcpad = gst_ghost_pad_new ("src", pad);
+ gst_pad_set_active (self->srcpad, TRUE);
+ gst_object_unref (pad);
+ gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
+}
+
+
+static void
+ges_smart_mixer_class_init (GESSmartMixerClass * klass)
+{
+/* GstBinClass *parent_class = GST_BIN_CLASS (klass);
+ */
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ /* FIXME Make sure the MixerClass doesn get destroy before ourself */
+ gst_element_class_add_static_pad_template (element_class, &src_template);
+ gst_element_class_add_static_pad_template (element_class, &sink_template);
+ gst_element_class_set_static_metadata (element_class, "GES Smart mixer",
+ "Generic/Audio",
+ "Use mixer making use of GES informations",
+ "Thibault Saunier <thibault.saunier@collabora.com>");
+
+ element_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
+ element_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);
+
+ object_class->dispose = ges_smart_mixer_dispose;
+ object_class->finalize = ges_smart_mixer_finalize;
+ object_class->constructed = ges_smart_mixer_constructed;
+}
+
+static void
+ges_smart_mixer_init (GESSmartMixer * self)
+{
+ g_mutex_init (&self->lock);
+ self->pads_infos = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) destroy_pad);
+}
+
+GstElement *
+ges_smart_mixer_new (GESTrack * track)
+{
+ GESSmartMixer *self = g_object_new (GES_TYPE_SMART_MIXER, NULL);
+
+ /* FIXME Make mixer smart and let it properly negotiate caps! */
+ return GST_ELEMENT (self);
+}
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Mathieu Duponchelle <mduponchelle1@gmail.com>
+ *
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GES_SMART_MIXER_H_
+#define _GES_SMART_MIXER_H_
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "ges-track.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_SMART_MIXER (ges_smart_mixer_get_type ())
+#define GES_SMART_MIXER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SMART_MIXER, GESSmartMixer))
+#define GES_SMART_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SMART_MIXER, GESSmartMixerClass))
+#define GES_IS_SMART_MIXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SMART_MIXER))
+#define GES_IS_SMART_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SMART_MIXER))
+#define GES_SMART_MIXER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SMART_MIXER, GESSmartMixerClass))
+
+typedef struct _GESSmartMixerClass GESSmartMixerClass;
+typedef struct _GESSmartMixer GESSmartMixer;
+
+struct _GESSmartMixerClass
+{
+ GstBinClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESSmartMixer
+{
+ GstBin parent_instance;
+
+ GHashTable *pads_infos;
+ GstPad *srcpad;
+ GstElement *mixer;
+ GMutex lock;
+
+ GstCaps *caps;
+ gboolean disable_zorder_alpha;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+G_GNUC_INTERNAL
+GType ges_smart_mixer_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL GstPad *
+ges_smart_mixer_get_mixer_pad (GESSmartMixer *self, GstPad **mixerpad);
+
+G_GNUC_INTERNAL
+GstElement* ges_smart_mixer_new (GESTrack *track);
+
+G_END_DECLS
+#endif /* _GES_SMART_MIXER_H_ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gessourceclip
+ * @title: GESSourceClip
+ * @short_description: Base Class for sources of a GESLayer
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-clip.h"
+#include "ges-source-clip.h"
+#include "ges-source.h"
+
+
+struct _GESSourceClipPrivate
+{
+ /* dummy variable */
+ void *nothing;
+};
+
+enum
+{
+ PROP_0,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESSourceClip, ges_source_clip, GES_TYPE_CLIP);
+
+static gboolean
+_set_start (GESTimelineElement * element, GstClockTime start)
+{
+ GESTimelineElement *toplevel =
+ ges_timeline_element_get_toplevel_parent (element);
+
+ gst_object_unref (toplevel);
+ if (element->timeline
+ && !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE)
+ && !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ ges_timeline_move_object_simple (element->timeline, element, NULL,
+ GES_EDGE_NONE, start);
+ return FALSE;
+ }
+
+ return
+ GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_start
+ (element, start);
+}
+
+static gboolean
+_set_duration (GESTimelineElement * element, GstClockTime duration)
+{
+ GESTimelineElement *toplevel =
+ ges_timeline_element_get_toplevel_parent (element);
+
+ gst_object_unref (toplevel);
+ if (element->timeline
+ && !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE)
+ && !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ return !timeline_trim_object (element->timeline, element,
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element), NULL, GES_EDGE_END,
+ element->start + duration);
+ }
+
+ return
+ GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_duration
+ (element, duration);
+}
+
+static void
+ges_source_clip_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_source_clip_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_source_clip_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ges_source_clip_parent_class)->finalize (object);
+}
+
+static void
+ges_source_clip_class_init (GESSourceClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_source_clip_get_property;
+ object_class->set_property = ges_source_clip_set_property;
+ object_class->finalize = ges_source_clip_finalize;
+
+ element_class->set_start = _set_start;
+ element_class->set_duration = _set_duration;
+}
+
+static void
+ges_source_clip_init (GESSourceClip * self)
+{
+ self->priv = ges_source_clip_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_SOURCE_CLIP
+#define _GES_SOURCE_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-clip.h>
+#include <ges/ges-enums.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_SOURCE_CLIP ges_source_clip_get_type()
+
+#define GES_SOURCE_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SOURCE_CLIP, GESSourceClip))
+
+#define GES_SOURCE_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SOURCE_CLIP, GESSourceClipClass))
+
+#define GES_IS_SOURCE_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SOURCE_CLIP))
+
+#define GES_IS_SOURCE_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SOURCE_CLIP))
+
+#define GES_SOURCE_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SOURCE_CLIP, GESSourceClipClass))
+
+typedef struct _GESSourceClipPrivate GESSourceClipPrivate;
+
+/**
+ * GESSourceClip:
+ *
+ * Base class for sources of a #GESLayer
+ */
+
+struct _GESSourceClip {
+ GESClip parent;
+
+ /*< private >*/
+ GESSourceClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESSourceClipClass:
+ */
+
+struct _GESSourceClipClass {
+ /*< private >*/
+ GESClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_source_clip_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_SOURCE_CLIP */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gessource
+ * @title: GESSource
+ * @short_description: Base Class for single-media sources
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges/ges-meta-container.h"
+#include "ges-track-element.h"
+#include "ges-source.h"
+#include "ges-layer.h"
+#include "gstframepositioner.h"
+struct _GESSourcePrivate
+{
+ /* Dummy variable */
+ GstFramePositioner *positioner;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESSource, ges_source, GES_TYPE_TRACK_ELEMENT);
+
+/******************************
+ * Internal helper methods *
+ ******************************/
+static void
+_pad_added_cb (GstElement * element, GstPad * srcpad, GstPad * sinkpad)
+{
+ GstPadLinkReturn res;
+ gst_element_no_more_pads (element);
+ res = gst_pad_link (srcpad, sinkpad);
+#ifndef GST_DISABLE_GST_DEBUG
+ if (res != GST_PAD_LINK_OK) {
+ GstCaps *srccaps = NULL;
+ GstCaps *sinkcaps = NULL;
+
+ srccaps = gst_pad_query_caps (srcpad, NULL);
+ sinkcaps = gst_pad_query_caps (sinkpad, NULL);
+
+ GST_WARNING_OBJECT (element, "Could not link source with "
+ "conversion bin: %s (srcpad caps %" GST_PTR_FORMAT
+ " sinkpad caps: %" GST_PTR_FORMAT ")",
+ gst_pad_link_get_name (res), srccaps, sinkcaps);
+ gst_caps_unref (srccaps);
+ gst_caps_unref (sinkcaps);
+ }
+#endif
+}
+
+static void
+_ghost_pad_added_cb (GstElement * element, GstPad * srcpad, GstElement * bin)
+{
+ GstPad *ghost;
+
+ ghost = gst_ghost_pad_new ("src", srcpad);
+ gst_pad_set_active (ghost, TRUE);
+ gst_element_add_pad (bin, ghost);
+ gst_element_no_more_pads (element);
+}
+
+GstElement *
+ges_source_create_topbin (const gchar * bin_name, GstElement * sub_element, ...)
+{
+ va_list argp;
+
+ GstElement *element;
+ GstElement *prev = NULL;
+ GstElement *first = NULL;
+ GstElement *bin;
+ GstPad *sub_srcpad;
+
+ va_start (argp, sub_element);
+ bin = gst_bin_new (bin_name);
+ gst_bin_add (GST_BIN (bin), sub_element);
+
+ while ((element = va_arg (argp, GstElement *)) != NULL) {
+ gst_bin_add (GST_BIN (bin), element);
+ if (prev) {
+ if (!gst_element_link_pads_full (prev, "src", element, "sink",
+ GST_PAD_LINK_CHECK_NOTHING)) {
+ g_error ("Could not link %s and %s",
+ GST_OBJECT_NAME (prev), GST_OBJECT_NAME (element));
+ }
+
+ }
+ prev = element;
+ if (first == NULL)
+ first = element;
+ }
+
+ va_end (argp);
+
+ sub_srcpad = gst_element_get_static_pad (sub_element, "src");
+
+ if (prev != NULL) {
+ GstPad *srcpad, *sinkpad, *ghost;
+
+ srcpad = gst_element_get_static_pad (prev, "src");
+ ghost = gst_ghost_pad_new ("src", srcpad);
+ gst_pad_set_active (ghost, TRUE);
+ gst_element_add_pad (bin, ghost);
+
+ sinkpad = gst_element_get_static_pad (first, "sink");
+ if (sub_srcpad)
+ gst_pad_link_full (sub_srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING);
+ else
+ g_signal_connect (sub_element, "pad-added", G_CALLBACK (_pad_added_cb),
+ sinkpad);
+
+ gst_object_unref (srcpad);
+ gst_object_unref (sinkpad);
+
+ } else if (sub_srcpad) {
+ GstPad *ghost;
+
+ ghost = gst_ghost_pad_new ("src", sub_srcpad);
+ gst_pad_set_active (ghost, TRUE);
+ gst_element_add_pad (bin, ghost);
+ } else {
+ g_signal_connect (sub_element, "pad-added",
+ G_CALLBACK (_ghost_pad_added_cb), bin);
+ }
+
+ if (sub_srcpad)
+ gst_object_unref (sub_srcpad);
+
+ return bin;
+}
+
+static void
+ges_source_class_init (GESSourceClass * klass)
+{
+ GESTrackElementClass *track_class = GES_TRACK_ELEMENT_CLASS (klass);
+
+ track_class->nleobject_factorytype = "nlesource";
+ track_class->create_element = NULL;
+}
+
+static void
+ges_source_init (GESSource * self)
+{
+ self->priv = ges_source_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_SOURCE
+#define _GES_SOURCE
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-track-element.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_SOURCE ges_source_get_type()
+
+#define GES_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SOURCE, GESSource))
+
+#define GES_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SOURCE, GESSourceClass))
+
+#define GES_IS_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SOURCE))
+
+#define GES_IS_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SOURCE))
+
+#define GES_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SOURCE, GESSourceClass))
+
+typedef struct _GESSourcePrivate GESSourcePrivate;
+
+/**
+ * GESSource:
+ *
+ * Base class for single-media sources
+ */
+
+struct _GESSource {
+ /*< private >*/
+ GESTrackElement parent;
+
+ GESSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESSourceClass:
+ * @create_source: method to return the GstElement to put in the source topbin.
+ */
+
+struct _GESSourceClass {
+ /*< private >*/
+ GESTrackElementClass parent_class;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_SOURCE */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2015> Mathieu Duponchelle <mathieu.duponchelle@opencreed.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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-structure-parser.h"
+
+#include <ges/ges.h>
+
+G_DEFINE_TYPE (GESStructureParser, ges_structure_parser, G_TYPE_OBJECT);
+
+static void
+ges_structure_parser_init (GESStructureParser * self)
+{
+}
+
+static void
+_finalize (GObject * self)
+{
+ GESStructureParser *parser = GES_STRUCTURE_PARSER (self);
+
+ g_list_free_full (parser->structures, (GDestroyNotify) gst_structure_free);
+ g_list_free_full (parser->wrong_strings, (GDestroyNotify) g_free);
+
+ G_OBJECT_CLASS (ges_structure_parser_parent_class)->finalize (self);
+}
+
+static void
+ges_structure_parser_class_init (GESStructureParserClass * klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = _finalize;
+}
+
+void
+ges_structure_parser_parse_string (GESStructureParser * self,
+ const gchar * text, gboolean is_symbol)
+{
+ gchar *new_string = NULL;
+
+ if (self->current_string) {
+ new_string = g_strconcat (self->current_string, text, NULL);
+ } else if (is_symbol) {
+ new_string = g_strdup (text);
+ }
+
+ g_free (self->current_string);
+ self->current_string = new_string;
+}
+
+void
+ges_structure_parser_parse_default (GESStructureParser * self,
+ const gchar * text)
+{
+ gchar *new_string = NULL;
+
+ if (self->add_comma && self->current_string) {
+ new_string = g_strconcat (self->current_string, ",", text, NULL);
+ g_free (self->current_string);
+ self->current_string = new_string;
+ self->add_comma = FALSE;
+ } else {
+ ges_structure_parser_parse_string (self, text, FALSE);
+ }
+}
+
+void
+ges_structure_parser_parse_whitespace (GESStructureParser * self)
+{
+ self->add_comma = TRUE;
+}
+
+static void
+_finish_structure (GESStructureParser * self)
+{
+ if (self->current_string) {
+ GstStructure *structure =
+ gst_structure_new_from_string (self->current_string);
+
+ if (structure == NULL) {
+ GST_ERROR ("Could not parse %s", self->current_string);
+
+ self->wrong_strings = g_list_append (self->wrong_strings,
+ g_strdup (self->current_string));
+
+ return;
+ }
+
+ self->structures = g_list_append (self->structures, structure);
+ g_free (self->current_string);
+ self->current_string = NULL;
+ }
+}
+
+void
+ges_structure_parser_end_of_file (GESStructureParser * self)
+{
+ _finish_structure (self);
+}
+
+void
+ges_structure_parser_parse_symbol (GESStructureParser * self,
+ const gchar * symbol)
+{
+ _finish_structure (self);
+
+ while (*symbol == ' ' || *symbol == '+')
+ symbol++;
+
+ self->add_comma = FALSE;
+ if (!g_ascii_strncasecmp (symbol, "clip", 4))
+ ges_structure_parser_parse_string (self, "clip, uri=", TRUE);
+ else if (!g_ascii_strncasecmp (symbol, "test-clip", 9))
+ ges_structure_parser_parse_string (self, "test-clip, pattern=", TRUE);
+ else if (!g_ascii_strncasecmp (symbol, "effect", 6))
+ ges_structure_parser_parse_string (self, "effect, bin-description=", TRUE);
+ else if (!g_ascii_strncasecmp (symbol, "transition", 10))
+ ges_structure_parser_parse_string (self, "transition, type=", TRUE);
+ else if (!g_ascii_strncasecmp (symbol, "title", 5))
+ ges_structure_parser_parse_string (self, "title, text=(string)", TRUE);
+}
+
+void
+ges_structure_parser_parse_setter (GESStructureParser * self,
+ const gchar * setter)
+{
+ gchar *parsed_setter;
+
+ _finish_structure (self);
+
+ while (*setter == '-' || *setter == ' ')
+ setter++;
+
+ while (*setter != '-')
+ setter++;
+
+ setter++;
+
+ parsed_setter = g_strdup_printf ("set-property, property=%s, value=", setter);
+ self->add_comma = FALSE;
+ ges_structure_parser_parse_string (self, parsed_setter, TRUE);
+ g_free (parsed_setter);
+}
+
+GESStructureParser *
+ges_structure_parser_new (void)
+{
+ return (g_object_new (GES_TYPE_STRUCTURE_PARSER, NULL));
+}
+
+GError *
+ges_structure_parser_get_error (GESStructureParser * self)
+{
+ GList *tmp;
+ GError *error = NULL;
+ GString *msg = NULL;
+
+ if (self->wrong_strings == NULL)
+ return NULL;
+
+ msg = g_string_new ("Could not parse: ");
+
+
+ for (tmp = self->wrong_strings; tmp; tmp = tmp->next) {
+ g_string_append_printf (msg, " %s", (gchar *) tmp->data);
+ }
+
+ error = g_error_new_literal (GES_ERROR, 0, msg->str);
+ g_string_free (msg, TRUE);
+
+ GST_ERROR ("BoOOOM ");
+ return error;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2015> Mathieu Duponchelle <mathieu.duponchelle@opencreed.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 _GES_STRUCTURE_PARSER_H
+#define _GES_STRUCTURE_PARSER_H
+
+#include <glib.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_STRUCTURE_PARSER ges_structure_parser_get_type()
+#define GES_STRUCTURE_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_STRUCTURE_PARSER, GESStructureParser))
+
+typedef struct _GESStructureParser GESStructureParser;
+typedef struct _GESStructureParserClass GESStructureParserClass;
+
+struct _GESStructureParser
+{
+ GObject parent;
+ GList *structures;
+
+ GList *wrong_strings;
+
+ /*< private > */
+ gchar *current_string;
+ gboolean add_comma;
+};
+
+struct _GESStructureParserClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+};
+
+G_GNUC_INTERNAL GType ges_structure_parser_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL GError * ges_structure_parser_get_error (GESStructureParser *self);
+G_GNUC_INTERNAL void ges_structure_parser_parse_string (GESStructureParser *self, const gchar *string, gboolean is_symbol);
+G_GNUC_INTERNAL void ges_structure_parser_parse_default (GESStructureParser *self, const gchar *text);
+G_GNUC_INTERNAL void ges_structure_parser_parse_whitespace (GESStructureParser *self);
+G_GNUC_INTERNAL void ges_structure_parser_parse_symbol (GESStructureParser *self, const gchar *symbol);
+G_GNUC_INTERNAL void ges_structure_parser_parse_setter (GESStructureParser *self, const gchar *setter);
+G_GNUC_INTERNAL void ges_structure_parser_end_of_file (GESStructureParser *self);
+
+G_GNUC_INTERNAL GESStructureParser *ges_structure_parser_new(void);
+G_END_DECLS
+
+#endif /* _GES_STRUCTURE_PARSER_H */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-structured-interface.h"
+
+#include <string.h>
+
+
+#define LAST_CONTAINER_QDATA g_quark_from_string("ges-structured-last-container")
+#define LAST_CHILD_QDATA g_quark_from_string("ges-structured-last-child")
+
+static gboolean
+_get_clocktime (GstStructure * structure, const gchar * name, gpointer var)
+{
+ gboolean found = FALSE;
+ GstClockTime *val = (GstClockTime *) var;
+
+ const GValue *gvalue = gst_structure_get_value (structure, name);
+
+ if (gvalue) {
+ if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME) {
+ *val = (GstClockTime) g_value_get_uint64 (gvalue);
+ found = TRUE;
+ } else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT64) {
+ *val = (GstClockTime) g_value_get_uint64 (gvalue);
+ found = TRUE;
+ } else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) {
+ *val = (GstClockTime) g_value_get_uint (gvalue);
+ found = TRUE;
+ } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) {
+ *val = (GstClockTime) g_value_get_int (gvalue);
+ found = TRUE;
+ } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) {
+ *val = (GstClockTime) g_value_get_int64 (gvalue);
+ found = TRUE;
+ } else if (G_VALUE_TYPE (gvalue) == G_TYPE_DOUBLE) {
+ gdouble d = g_value_get_double (gvalue);
+
+ found = TRUE;
+ if (d == -1.0)
+ *val = GST_CLOCK_TIME_NONE;
+ else {
+ *val = d * GST_SECOND;
+ *val = GST_ROUND_UP_4 (*val);
+ }
+ }
+ }
+
+ return found;
+}
+
+#define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
+ gboolean found = FALSE; \
+\
+ if (type == GST_TYPE_CLOCK_TIME) {\
+ found = _get_clocktime(structure,name,var);\
+ }\
+ else { \
+ found = gst_structure_get (structure, name, type, var, NULL); \
+ }\
+ if (!found) {\
+ gchar *struct_str = gst_structure_to_string (structure); \
+ *error = g_error_new (GES_ERROR, 0, \
+ "Could not get the mandatory field '%s'" \
+ " fields in %s", name, struct_str); \
+ g_free (struct_str); \
+ goto label;\
+ } \
+} G_STMT_END
+
+#define TRY_GET_STRING(name,var,def) G_STMT_START {\
+ *var = gst_structure_get_string (structure, name); \
+ if (*var == NULL) \
+ *var = def; \
+} G_STMT_END
+
+#define TRY_GET(name,type,var,def) G_STMT_START {\
+ if (type == GST_TYPE_CLOCK_TIME) {\
+ if (!_get_clocktime(structure,name,var))\
+ *var = def; \
+ } else if (!gst_structure_get (structure, name, type, var, NULL)) {\
+ *var = def; \
+ } \
+} G_STMT_END
+
+typedef struct
+{
+ const gchar **fields;
+ GList *invalid_fields;
+} FieldsError;
+
+static gboolean
+_check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
+{
+ guint i;
+ const gchar *field = g_quark_to_string (field_id);
+
+ for (i = 0; fields_error->fields[i]; i++) {
+ if (g_strcmp0 (fields_error->fields[i], field) == 0) {
+
+ return TRUE;
+ }
+ }
+
+ fields_error->invalid_fields =
+ g_list_append (fields_error->invalid_fields, (gpointer) field);
+
+ return TRUE;
+}
+
+static gboolean
+_check_fields (GstStructure * structure, FieldsError fields_error,
+ GError ** error)
+{
+ gst_structure_foreach (structure,
+ (GstStructureForeachFunc) _check_field, &fields_error);
+
+ if (fields_error.invalid_fields) {
+ GList *tmp;
+ const gchar *struct_name = gst_structure_get_name (structure);
+ GString *msg = g_string_new (NULL);
+
+ g_string_append_printf (msg, "Unknown propert%s in %s%s:",
+ g_list_length (fields_error.invalid_fields) > 1 ? "ies" : "y",
+ strlen (struct_name) > 1 ? "--" : "-",
+ gst_structure_get_name (structure));
+
+ for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
+ g_string_append_printf (msg, " %s", (gchar *) tmp->data);
+
+ if (error)
+ *error = g_error_new_literal (GES_ERROR, 0, msg->str);
+ GST_ERROR ("%s", msg->str);
+
+ g_string_free (msg, TRUE);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+_ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ GESTrackElement *element;
+
+ gdouble value;
+ GstClockTime timestamp;
+ GstControlBinding *binding = NULL;
+ GstTimedValueControlSource *source = NULL;
+ gchar *element_name = NULL, *property_name = NULL;
+
+ gboolean ret = FALSE;
+
+ const gchar *valid_fields[] =
+ { "element-name", "property-name", "value", "timestamp",
+ NULL
+ };
+
+ FieldsError fields_error = { valid_fields, NULL };
+
+ if (!_check_fields (structure, fields_error, error))
+ return FALSE;
+
+ GET_AND_CHECK ("element-name", G_TYPE_STRING, &element_name, done);
+ GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
+ GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
+ GET_AND_CHECK ("timestamp", GST_TYPE_CLOCK_TIME, ×tamp, done);
+
+ element =
+ GES_TRACK_ELEMENT (ges_timeline_get_element (timeline, element_name));
+
+ if (!GES_IS_TRACK_ELEMENT (element)) {
+ *error =
+ g_error_new (GES_ERROR, 0, "Could not find TrackElement %s",
+ element_name);
+
+ goto done;
+ }
+
+ binding = ges_track_element_get_control_binding (element, property_name);
+ if (binding == NULL) {
+ *error = g_error_new (GES_ERROR, 0, "No control binding found for %s:%s"
+ " you should first set-control-binding on it",
+ element_name, property_name);
+
+ goto done;
+ }
+
+ g_object_get (binding, "control-source", &source, NULL);
+
+ if (source == NULL) {
+ *error = g_error_new (GES_ERROR, 0, "No control source found for %s:%s"
+ " you should first set-control-binding on it",
+ element_name, property_name);
+
+ goto done;
+ }
+
+ if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
+ *error = g_error_new (GES_ERROR, 0, "You can use add-keyframe"
+ " only on GstTimedValueControlSource not %s",
+ G_OBJECT_TYPE_NAME (source));
+
+ goto done;
+ }
+
+ if (!g_strcmp0 (gst_structure_get_name (structure), "add-keyframe"))
+ ret = gst_timed_value_control_source_set (source, timestamp, value);
+ else {
+ ret = gst_timed_value_control_source_unset (source, timestamp);
+
+ if (!ret) {
+ *error =
+ g_error_new (GES_ERROR, 0,
+ "Could not unset value for timestamp: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp));
+ }
+ }
+
+done:
+ if (source)
+ gst_object_unref (source);
+ g_free (element_name);
+ g_free (property_name);
+
+ return ret;
+
+}
+
+GESAsset *
+_ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
+ const gchar * id, GError ** error)
+{
+ GESAsset *asset;
+ GESProject *project = ges_timeline_get_project (timeline);
+ GError *err = NULL;
+
+ asset = ges_project_create_asset_sync (project, id, type, &err);
+
+ if (err)
+ g_propagate_error (error, err);
+ if (!asset || (error && *error)) {
+
+ if (error && !*error) {
+ *error = g_error_new (GES_ERROR, 0,
+ "There was an error requesting the asset with id %s and type %s (%s)",
+ id, g_type_name (type), "unknown");
+ }
+
+ GST_ERROR
+ ("There was an error requesting the asset with id %s and type %s (%s)",
+ id, g_type_name (type), error ? (*error)->message : "unknown");
+
+ return NULL;
+ }
+
+ return asset;
+}
+
+/* Unref after usage */
+GESLayer *
+_ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
+{
+ GList *layers;
+ gint nlayers;
+ GESLayer *layer = NULL;
+
+ priority = MAX (priority, 0);
+ layers = ges_timeline_get_layers (timeline);
+ nlayers = (gint) g_list_length (layers);
+ if (priority >= nlayers) {
+ gint i = nlayers;
+
+ while (i <= priority) {
+ layer = ges_timeline_append_layer (timeline);
+
+ i++;
+ }
+
+ layer = gst_object_ref (layer);
+
+ goto done;
+ }
+
+ layer = ges_timeline_get_layer (timeline, priority);
+
+done:
+ g_list_free_full (layers, gst_object_unref);
+
+ return layer;
+}
+
+static gchar *
+ensure_uri (gchar * location)
+{
+ if (gst_uri_is_valid (location))
+ return g_strdup (location);
+ else
+ return gst_filename_to_uri (location, NULL);
+}
+
+gboolean
+_ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
+ GError ** error)
+{
+ GESAsset *asset;
+ GESLayer *layer;
+ GESClip *clip;
+ gint layer_priority;
+ const gchar *name;
+ const gchar *text;
+ const gchar *pattern;
+ gchar *asset_id = NULL;
+ gchar *check_asset_id = NULL;
+ const gchar *type_string;
+ GType type;
+ gboolean res = FALSE;
+
+ GstClockTime duration = 1 * GST_SECOND, inpoint = 0, start =
+ GST_CLOCK_TIME_NONE;
+
+ const gchar *valid_fields[] =
+ { "asset-id", "pattern", "name", "layer-priority", "layer", "type",
+ "start", "inpoint", "duration", "text", NULL
+ };
+
+ FieldsError fields_error = { valid_fields, NULL };
+
+ if (!_check_fields (structure, fields_error, error))
+ return FALSE;
+
+ GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
+
+ TRY_GET_STRING ("pattern", &pattern, NULL);
+ TRY_GET_STRING ("text", &text, NULL);
+ TRY_GET_STRING ("name", &name, NULL);
+ TRY_GET ("layer-priority", G_TYPE_INT, &layer_priority, -1);
+ if (layer_priority == -1)
+ TRY_GET ("layer", G_TYPE_INT, &layer_priority, -1);
+ TRY_GET_STRING ("type", &type_string, "GESUriClip");
+ TRY_GET ("start", GST_TYPE_CLOCK_TIME, &start, GST_CLOCK_TIME_NONE);
+ TRY_GET ("inpoint", GST_TYPE_CLOCK_TIME, &inpoint, 0);
+ TRY_GET ("duration", GST_TYPE_CLOCK_TIME, &duration, GST_CLOCK_TIME_NONE);
+
+ if (!(type = g_type_from_name (type_string))) {
+ *error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
+ type_string);
+
+ goto beach;
+ }
+
+ if (type == GES_TYPE_URI_CLIP) {
+ asset_id = ensure_uri (check_asset_id);
+ } else {
+ asset_id = g_strdup (check_asset_id);
+ }
+
+ asset = _ges_get_asset_from_timeline (timeline, type, asset_id, error);
+ if (!asset) {
+ res = FALSE;
+
+ goto beach;
+ }
+
+ if (layer_priority == -1) {
+ GESContainer *container;
+
+ container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
+ if (!container || !GES_IS_CLIP (container))
+ layer = _ges_get_layer_by_priority (timeline, 0);
+ else
+ layer = ges_clip_get_layer (GES_CLIP (container));
+
+ if (!layer)
+ layer = _ges_get_layer_by_priority (timeline, 0);
+ } else {
+ layer = _ges_get_layer_by_priority (timeline, layer_priority);
+ }
+
+ if (!layer) {
+ *error =
+ g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
+ goto beach;
+ }
+
+ if (GES_IS_URI_CLIP_ASSET (asset) && !GST_CLOCK_TIME_IS_VALID (duration)) {
+ duration = GST_CLOCK_DIFF (inpoint,
+ ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset)));
+ }
+
+ clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
+ GES_TRACK_TYPE_UNKNOWN);
+
+ if (clip) {
+ res = TRUE;
+
+ if (GES_TIMELINE_ELEMENT_DURATION (clip) == 0) {
+ *error = g_error_new (GES_ERROR, 0,
+ "Clip %s has 0 as duration, please provide a proper duration",
+ asset_id);
+ res = FALSE;
+ goto beach;
+ }
+
+
+ if (GES_IS_TEST_CLIP (clip)) {
+ if (pattern) {
+ GEnumClass *enum_class =
+ G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
+ GEnumValue *value = g_enum_get_value_by_nick (enum_class, pattern);
+
+ if (!value) {
+ res = FALSE;
+ goto beach;
+ }
+
+ ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
+ g_type_class_unref (enum_class);
+ }
+ }
+
+ if (GES_IS_TITLE_CLIP (clip) && text)
+ ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
+ "text", text, NULL);
+
+ if (name
+ && !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
+ res = FALSE;
+ *error =
+ g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
+ name, asset_id);
+ }
+ } else {
+ *error = g_error_new (GES_ERROR, 0,
+ "Couldn't add clip with id %s to layer with priority %d", asset_id,
+ layer_priority);
+ }
+
+ if (res) {
+ g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
+ g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
+ }
+
+ gst_object_unref (layer);
+
+beach:
+ g_free (asset_id);
+ g_free (check_asset_id);
+ return res;
+}
+
+gboolean
+_ges_container_add_child_from_struct (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ GESAsset *asset;
+ GESContainer *container;
+ GESTimelineElement *child = NULL;
+ const gchar *container_name, *child_name, *child_type, *id;
+
+ gboolean res = TRUE;
+ const gchar *valid_fields[] = { "container-name", "asset-id",
+ "child-type", "child-name", NULL
+ };
+
+ FieldsError fields_error = { valid_fields, NULL };
+
+ if (!_check_fields (structure, fields_error, error))
+ return FALSE;
+
+ container_name = gst_structure_get_string (structure, "container-name");
+
+ if (container_name == NULL) {
+ container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
+ } else {
+ container =
+ GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
+ }
+
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+
+ id = gst_structure_get_string (structure, "asset-id");
+ child_type = gst_structure_get_string (structure, "child-type");
+
+ if (id && child_type) {
+ asset =
+ _ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
+ id, error);
+
+ if (asset == NULL) {
+ res = FALSE;
+ goto beach;
+ }
+
+ child = GES_TIMELINE_ELEMENT (ges_asset_extract (asset, NULL));
+ if (!GES_IS_TIMELINE_ELEMENT (child)) {
+ g_error_new (GES_ERROR, 0, "Could not extract child element");
+
+ goto beach;
+ }
+ }
+
+ child_name = gst_structure_get_string (structure, "child-name");
+ if (!child && child_name) {
+ child = ges_timeline_get_element (timeline, child_name);
+ if (!GES_IS_TIMELINE_ELEMENT (child)) {
+ g_error_new (GES_ERROR, 0, "Could not find child element");
+
+ goto beach;
+ }
+ }
+
+ if (!child) {
+ g_error_new (GES_ERROR, 0, "Wong parametters, could not get a child");
+
+ return FALSE;
+ }
+
+ if (child_name)
+ ges_timeline_element_set_name (child, child_name);
+ else
+ child_name = GES_TIMELINE_ELEMENT_NAME (child);
+
+ res = ges_container_add (container, child);
+ if (res == FALSE) {
+ g_error_new (GES_ERROR, 0, "Could not add child to container");
+ } else {
+ g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
+ }
+
+beach:
+ return res;
+}
+
+gboolean
+_ges_set_child_property_from_struct (GESTimeline * timeline,
+ GstStructure * structure, GError ** error)
+{
+ const GValue *value;
+ GESTimelineElement *element;
+ const gchar *property_name, *element_name;
+
+ const gchar *valid_fields[] = { "element-name", "property", "value", NULL };
+
+ FieldsError fields_error = { valid_fields, NULL };
+
+ if (!_check_fields (structure, fields_error, error))
+ return FALSE;
+
+ element_name = gst_structure_get_string (structure, "element-name");
+ if (element_name == NULL)
+ element = g_object_get_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA);
+ else
+ element = ges_timeline_get_element (timeline, element_name);
+
+ property_name = gst_structure_get_string (structure, "property");
+ if (property_name == NULL) {
+ const gchar *name = gst_structure_get_name (structure);
+
+ if (g_str_has_prefix (name, "set-"))
+ property_name = &name[4];
+ else {
+ gchar *struct_str = gst_structure_to_string (structure);
+
+ *error =
+ g_error_new (GES_ERROR, 0, "Could not find any property name in %s",
+ struct_str);
+ g_free (struct_str);
+
+ return FALSE;
+ }
+ }
+
+ if (element) {
+ if (!ges_track_element_lookup_child (GES_TRACK_ELEMENT (element),
+ property_name, NULL, NULL))
+ element = NULL;
+ }
+
+ if (!element) {
+ element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
+
+ if (element == NULL) {
+ *error =
+ g_error_new (GES_ERROR, 0,
+ "Could not find anywhere to set property: %s", property_name);
+
+ return FALSE;
+ }
+ }
+
+ if (!GES_IS_TIMELINE_ELEMENT (element)) {
+ *error =
+ g_error_new (GES_ERROR, 0, "Could not find child %s", element_name);
+
+ return FALSE;
+ }
+
+ value = gst_structure_get_value (structure, "value");
+
+ GST_DEBUG ("%s Setting %s property to %p",
+ element->name, property_name, value);
+
+ ges_timeline_element_set_child_property (element, property_name,
+ (GValue *) value);
+
+ return TRUE;
+}
+
+#undef GET_AND_CHECK
+#undef TRY_GET
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GES_STRUCTURED_INTERFACE__
+#define __GES_STRUCTURED_INTERFACE__
+
+#include <ges/ges.h>
+
+G_BEGIN_DECLS
+
+typedef gboolean (*ActionFromStructureFunc) (GESTimeline * timeline,
+ GstStructure * structure,
+ GError ** error);
+
+G_GNUC_INTERNAL gboolean
+_ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
+ GstStructure * structure,
+ GError ** error);
+G_GNUC_INTERNAL gboolean
+_ges_add_clip_from_struct (GESTimeline * timeline,
+ GstStructure * structure,
+ GError ** error);
+
+G_GNUC_INTERNAL gboolean
+_ges_container_add_child_from_struct (GESTimeline * timeline,
+ GstStructure * structure,
+ GError ** error);
+
+G_GNUC_INTERNAL gboolean
+_ges_set_child_property_from_struct (GESTimeline * timeline,
+ GstStructure * structure,
+ GError ** error);
+
+G_GNUC_INTERNAL GESAsset *
+_ges_get_asset_from_timeline (GESTimeline * timeline,
+ GType type,
+ const gchar * id,
+ GError **error);
+G_GNUC_INTERNAL GESLayer *
+_ges_get_layer_by_priority (GESTimeline * timeline,
+ gint priority);
+
+G_END_DECLS
+
+#endif /* __GES_STRUCTURED_INTERFACE__*/
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestestclip
+ * @title: GESTestClip
+ * @short_description: Render video and audio test patterns in a GESLayer
+ *
+ * Useful for testing purposes.
+ *
+ * You can use the ges_asset_request_simple API to create an Asset
+ * capable of extracting GESTestClip-s
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-test-clip.h"
+#include "ges-source-clip.h"
+#include "ges-track-element.h"
+#include "ges-video-test-source.h"
+#include "ges-audio-test-source.h"
+#include <string.h>
+
+#define DEFAULT_VOLUME 1.0
+#define DEFAULT_VPATTERN GES_VIDEO_TEST_PATTERN_SMPTE
+
+struct _GESTestClipPrivate
+{
+ gboolean mute;
+ GESVideoTestPattern vpattern;
+ gdouble freq;
+ gdouble volume;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MUTE,
+ PROP_VPATTERN,
+ PROP_FREQ,
+ PROP_VOLUME,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESTestClip, ges_test_clip, GES_TYPE_SOURCE_CLIP);
+
+static GESTrackElement
+ * ges_test_clip_create_track_element (GESClip * clip, GESTrackType type);
+
+static void
+ges_test_clip_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTestClipPrivate *priv = GES_TEST_CLIP (object)->priv;
+
+ switch (property_id) {
+ case PROP_MUTE:
+ g_value_set_boolean (value, priv->mute);
+ break;
+ case PROP_VPATTERN:
+ g_value_set_enum (value, priv->vpattern);
+ break;
+ case PROP_FREQ:
+ g_value_set_double (value, priv->freq);
+ break;
+ case PROP_VOLUME:
+ g_value_set_double (value, priv->volume);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_test_clip_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTestClip *uriclip = GES_TEST_CLIP (object);
+
+ switch (property_id) {
+ case PROP_MUTE:
+ ges_test_clip_set_mute (uriclip, g_value_get_boolean (value));
+ break;
+ case PROP_VPATTERN:
+ ges_test_clip_set_vpattern (uriclip, g_value_get_enum (value));
+ break;
+ case PROP_FREQ:
+ ges_test_clip_set_frequency (uriclip, g_value_get_double (value));
+ break;
+ case PROP_VOLUME:
+ ges_test_clip_set_volume (uriclip, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_test_clip_class_init (GESTestClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESClipClass *timobj_class = GES_CLIP_CLASS (klass);
+
+ object_class->get_property = ges_test_clip_get_property;
+ object_class->set_property = ges_test_clip_set_property;
+
+ /**
+ * GESTestClip:vpattern:
+ *
+ * Video pattern to display in video track elements.
+ */
+ g_object_class_install_property (object_class, PROP_VPATTERN,
+ g_param_spec_enum ("vpattern", "VPattern",
+ "Which video pattern to display. See videotestsrc element",
+ GES_VIDEO_TEST_PATTERN_TYPE,
+ DEFAULT_VPATTERN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * GESTestClip:freq:
+ *
+ * The frequency to generate for audio track elements.
+ */
+ g_object_class_install_property (object_class, PROP_FREQ,
+ g_param_spec_double ("freq", "Audio Frequency",
+ "The frequency to generate. See audiotestsrc element",
+ 0, 20000, 440, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * GESTestClip:volume:
+ *
+ * The volume for the audio track elements.
+ */
+ g_object_class_install_property (object_class, PROP_VOLUME,
+ g_param_spec_double ("volume", "Audio Volume",
+ "The volume of the test audio signal.",
+ 0, 1, DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+
+ /**
+ * GESTestClip:mute:
+ *
+ * Whether the sound will be played or not.
+ */
+ g_object_class_install_property (object_class, PROP_MUTE,
+ g_param_spec_boolean ("mute", "Mute", "Mute audio track",
+ FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ timobj_class->create_track_element = ges_test_clip_create_track_element;
+}
+
+static void
+ges_test_clip_init (GESTestClip * self)
+{
+ self->priv = ges_test_clip_get_instance_private (self);
+
+ self->priv->freq = 0;
+ self->priv->volume = 0;
+ GES_TIMELINE_ELEMENT (self)->duration = 0;
+}
+
+/**
+ * ges_test_clip_set_mute:
+ * @self: the #GESTestClip on which to mute or unmute the audio track
+ * @mute: %TRUE to mute the audio track, %FALSE to unmute it
+ *
+ * Sets whether the audio track of this clip is muted or not.
+ *
+ */
+void
+ges_test_clip_set_mute (GESTestClip * self, gboolean mute)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, mute:%d", self, mute);
+
+ self->priv->mute = mute;
+
+ /* Go over tracked objects, and update 'active' status on all audio objects */
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_AUDIO)
+ ges_track_element_set_active (trackelement, !mute);
+ }
+}
+
+/**
+ * ges_test_clip_set_vpattern:
+ * @self: the #GESTestClip to set the pattern on
+ * @vpattern: the #GESVideoTestPattern to use on @self
+ *
+ * Sets which video pattern to display on @self.
+ *
+ */
+void
+ges_test_clip_set_vpattern (GESTestClip * self, GESVideoTestPattern vpattern)
+{
+ GList *tmp;
+
+ self->priv->vpattern = vpattern;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+ if (GES_IS_VIDEO_TEST_SOURCE (trackelement))
+ ges_video_test_source_set_pattern (
+ (GESVideoTestSource *) trackelement, vpattern);
+ }
+}
+
+/**
+ * ges_test_clip_set_frequency:
+ * @self: the #GESTestClip to set the frequency on
+ * @freq: the frequency you want to use on @self
+ *
+ * Sets the frequency to generate. See audiotestsrc element.
+ *
+ */
+void
+ges_test_clip_set_frequency (GESTestClip * self, gdouble freq)
+{
+ GList *tmp;
+
+ self->priv->freq = freq;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+ if (GES_IS_AUDIO_TEST_SOURCE (trackelement))
+ ges_audio_test_source_set_freq (
+ (GESAudioTestSource *) trackelement, freq);
+ }
+}
+
+/**
+ * ges_test_clip_set_volume:
+ * @self: the #GESTestClip to set the volume on
+ * @volume: the volume of the audio signal you want to use on @self
+ *
+ * Sets the volume of the test audio signal.
+ *
+ */
+void
+ges_test_clip_set_volume (GESTestClip * self, gdouble volume)
+{
+ GList *tmp;
+
+ self->priv->volume = volume;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+ if (GES_IS_AUDIO_TEST_SOURCE (trackelement))
+ ges_audio_test_source_set_volume (
+ (GESAudioTestSource *) trackelement, volume);
+ }
+}
+
+/**
+ * ges_test_clip_get_vpattern:
+ * @self: a #GESTestClip
+ *
+ * Get the #GESVideoTestPattern which is applied on @self.
+ *
+ * Returns: The #GESVideoTestPattern which is applied on @self.
+ */
+GESVideoTestPattern
+ges_test_clip_get_vpattern (GESTestClip * self)
+{
+ return self->priv->vpattern;
+}
+
+/**
+ * ges_test_clip_is_muted:
+ * @self: a #GESTestClip
+ *
+ * Let you know if the audio track of @self is muted or not.
+ *
+ * Returns: Whether the audio track of @self is muted or not.
+ */
+gboolean
+ges_test_clip_is_muted (GESTestClip * self)
+{
+ return self->priv->mute;
+}
+
+/**
+ * ges_test_clip_get_frequency:
+ * @self: a #GESTestClip
+ *
+ * Get the frequency @self generates.
+ *
+ * Returns: The frequency @self generates. See audiotestsrc element.
+ */
+gdouble
+ges_test_clip_get_frequency (GESTestClip * self)
+{
+ return self->priv->freq;
+}
+
+/**
+ * ges_test_clip_get_volume:
+ * @self: a #GESTestClip
+ *
+ * Get the volume of the test audio signal applied on @self.
+ *
+ * Returns: The volume of the test audio signal applied on @self.
+ */
+gdouble
+ges_test_clip_get_volume (GESTestClip * self)
+{
+ return self->priv->volume;
+}
+
+static GESTrackElement *
+ges_test_clip_create_track_element (GESClip * clip, GESTrackType type)
+{
+ GESTestClipPrivate *priv = GES_TEST_CLIP (clip)->priv;
+ GESTrackElement *res = NULL;
+
+ GST_DEBUG ("Creating a GESTrackTestSource for type: %s",
+ ges_track_type_name (type));
+
+ if (type == GES_TRACK_TYPE_VIDEO) {
+ res = (GESTrackElement *) ges_video_test_source_new ();
+ ges_video_test_source_set_pattern (
+ (GESVideoTestSource *) res, priv->vpattern);
+ } else if (type == GES_TRACK_TYPE_AUDIO) {
+ res = (GESTrackElement *) ges_audio_test_source_new ();
+
+ if (priv->mute)
+ ges_track_element_set_active (res, FALSE);
+
+ ges_audio_test_source_set_freq ((GESAudioTestSource *) res, priv->freq);
+ ges_audio_test_source_set_volume ((GESAudioTestSource *) res, priv->volume);
+ }
+
+ return res;
+}
+
+/**
+ * ges_test_clip_new:
+ *
+ * Creates a new #GESTestClip.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESTestClip,
+ * or %NULL if there was an error.
+ */
+GESTestClip *
+ges_test_clip_new (void)
+{
+ GESTestClip *new_clip;
+ GESAsset *asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ new_clip = GES_TEST_CLIP (ges_asset_extract (asset, NULL));
+ gst_object_unref (asset);
+
+ return new_clip;
+}
+
+/**
+ * ges_test_clip_new_for_nick:
+ * @nick: the nickname for which to create the #GESTestClip
+ *
+ * Creates a new #GESTestClip for the provided @nick.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESTestClip,
+ * or %NULL if there was an error.
+ */
+GESTestClip *
+ges_test_clip_new_for_nick (gchar * nick)
+{
+ GEnumValue *value;
+ GEnumClass *klass;
+ GESTestClip *ret = NULL;
+
+ klass = G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
+ if (!klass)
+ return NULL;
+
+ value = g_enum_get_value_by_nick (klass, nick);
+ if (value) {
+ ret = ges_test_clip_new ();
+ ges_test_clip_set_vpattern (ret, value->value);
+ }
+
+ g_type_class_unref (klass);
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_TL_TESTSOURCE
+#define _GES_TL_TESTSOURCE
+
+#include <glib-object.h>
+#include <ges/ges-enums.h>
+#include <ges/ges-types.h>
+#include <ges/ges-source-clip.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TEST_CLIP ges_test_clip_get_type()
+
+#define GES_TEST_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TEST_CLIP, GESTestClip))
+
+#define GES_TEST_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TEST_CLIP, GESTestClipClass))
+
+#define GES_IS_TEST_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TEST_CLIP))
+
+#define GES_IS_TEST_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TEST_CLIP))
+
+#define GES_TEST_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TEST_CLIP, GESTestClipClass))
+
+typedef struct _GESTestClipPrivate GESTestClipPrivate;
+
+/**
+ * GESTestClip:
+ */
+
+struct _GESTestClip {
+
+ GESSourceClip parent;
+
+ /*< private >*/
+ GESTestClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTestClipClass:
+ */
+
+struct _GESTestClipClass {
+ /*< private >*/
+ GESSourceClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_test_clip_get_type (void);
+
+GES_API void
+ges_test_clip_set_mute (GESTestClip * self, gboolean mute);
+
+GES_API void
+ges_test_clip_set_vpattern (GESTestClip * self,
+ GESVideoTestPattern vpattern);
+
+GES_API void
+ges_test_clip_set_frequency (GESTestClip * self, gdouble freq);
+
+GES_API void
+ges_test_clip_set_volume (GESTestClip * self,
+ gdouble volume);
+
+
+GES_API GESVideoTestPattern
+ges_test_clip_get_vpattern (GESTestClip * self);
+
+GES_API
+gboolean ges_test_clip_is_muted (GESTestClip * self);
+GES_API
+gdouble ges_test_clip_get_frequency (GESTestClip * self);
+GES_API
+gdouble ges_test_clip_get_volume (GESTestClip * self);
+
+GES_API
+GESTestClip* ges_test_clip_new (void);
+GES_API
+GESTestClip* ges_test_clip_new_for_nick(gchar * nick);
+
+G_END_DECLS
+
+#endif /* _GES_TL_TESTSOURCE */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestextoverlayclip
+ * @title: GESTextOverlayClip
+ * @short_description: Render text onto another stream in a GESLayer
+ *
+ * Renders text onto the next lower priority stream using textrender.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-text-overlay-clip.h"
+#include "ges-track-element.h"
+#include "ges-text-overlay.h"
+#include <string.h>
+
+
+#define DEFAULT_PROP_TEXT ""
+#define DEFAULT_PROP_FONT_DESC "Serif 36"
+#define DEFAULT_PROP_VALIGNMENT GES_TEXT_VALIGN_BASELINE
+#define DEFAULT_PROP_HALIGNMENT GES_TEXT_HALIGN_CENTER
+
+struct _GESTextOverlayClipPrivate
+{
+ gchar *text;
+ gchar *font_desc;
+ GESTextHAlign halign;
+ GESTextVAlign valign;
+ guint32 color;
+ gdouble xpos;
+ gdouble ypos;
+};
+
+enum
+{
+ PROP_0,
+ PROP_TEXT,
+ PROP_FONT_DESC,
+ PROP_HALIGNMENT,
+ PROP_VALIGNMENT,
+ PROP_COLOR,
+ PROP_XPOS,
+ PROP_YPOS,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESTextOverlayClip, ges_text_overlay_clip,
+ GES_TYPE_OVERLAY_CLIP);
+
+static GESTrackElement
+ * ges_text_overlay_clip_create_track_element (GESClip * clip,
+ GESTrackType type);
+
+static void
+ges_text_overlay_clip_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTextOverlayClipPrivate *priv = GES_OVERLAY_TEXT_CLIP (object)->priv;
+
+ switch (property_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, priv->text);
+ break;
+ case PROP_FONT_DESC:
+ g_value_set_string (value, priv->font_desc);
+ break;
+ case PROP_HALIGNMENT:
+ g_value_set_enum (value, priv->halign);
+ break;
+ case PROP_VALIGNMENT:
+ g_value_set_enum (value, priv->valign);
+ break;
+ case PROP_COLOR:
+ g_value_set_uint (value, priv->color);
+ break;
+ case PROP_XPOS:
+ g_value_set_double (value, priv->xpos);
+ break;
+ case PROP_YPOS:
+ g_value_set_double (value, priv->ypos);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_text_overlay_clip_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTextOverlayClip *uriclip = GES_OVERLAY_TEXT_CLIP (object);
+
+ switch (property_id) {
+ case PROP_TEXT:
+ ges_text_overlay_clip_set_text (uriclip, g_value_get_string (value));
+ break;
+ case PROP_FONT_DESC:
+ ges_text_overlay_clip_set_font_desc (uriclip, g_value_get_string (value));
+ break;
+ case PROP_HALIGNMENT:
+ ges_text_overlay_clip_set_halign (uriclip, g_value_get_enum (value));
+ break;
+ case PROP_VALIGNMENT:
+ ges_text_overlay_clip_set_valign (uriclip, g_value_get_enum (value));
+ break;
+ case PROP_COLOR:
+ ges_text_overlay_clip_set_color (uriclip, g_value_get_uint (value));
+ break;
+ case PROP_XPOS:
+ ges_text_overlay_clip_set_xpos (uriclip, g_value_get_double (value));
+ break;
+ case PROP_YPOS:
+ ges_text_overlay_clip_set_ypos (uriclip, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_text_overlay_clip_dispose (GObject * object)
+{
+ GESTextOverlayClipPrivate *priv = GES_OVERLAY_TEXT_CLIP (object)->priv;
+
+ if (priv->text)
+ g_free (priv->text);
+ if (priv->font_desc)
+ g_free (priv->font_desc);
+
+ G_OBJECT_CLASS (ges_text_overlay_clip_parent_class)->dispose (object);
+}
+
+static void
+ges_text_overlay_clip_class_init (GESTextOverlayClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESClipClass *timobj_class = GES_CLIP_CLASS (klass);
+
+ object_class->get_property = ges_text_overlay_clip_get_property;
+ object_class->set_property = ges_text_overlay_clip_set_property;
+ object_class->dispose = ges_text_overlay_clip_dispose;
+
+ /**
+ * GESTextOverlayClip:text:
+ *
+ * The text to diplay
+ */
+
+ g_object_class_install_property (object_class, PROP_TEXT,
+ g_param_spec_string ("text", "Text", "The text to display",
+ DEFAULT_PROP_TEXT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * GESTextOverlayClip:font-desc:
+ *
+ * Pango font description string
+ */
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
+ g_param_spec_string ("font-desc", "font description",
+ "Pango font description of font to be used for rendering. "
+ "See documentation of pango_font_description_from_string "
+ "for syntax.", DEFAULT_PROP_FONT_DESC,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GESTextOverlayClip:valignment:
+ *
+ * Vertical alignent of the text
+ */
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
+ g_param_spec_enum ("valignment", "vertical alignment",
+ "Vertical alignment of the text", GES_TEXT_VALIGN_TYPE,
+ DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * GESTextOverlayClip:halignment:
+ *
+ * Horizontal alignment of the text
+ */
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
+ g_param_spec_enum ("halignment", "horizontal alignment",
+ "Horizontal alignment of the text",
+ GES_TEXT_HALIGN_TYPE, DEFAULT_PROP_HALIGNMENT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ timobj_class->create_track_element =
+ ges_text_overlay_clip_create_track_element;
+
+ /**
+ * GESTextOverlayClip:color:
+ *
+ * The color of the text
+ */
+
+ g_object_class_install_property (object_class, PROP_COLOR,
+ g_param_spec_uint ("color", "Color", "The color of the text",
+ 0, G_MAXUINT32, G_MAXUINT32, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * GESTextOverlayClip:xpos:
+ *
+ * The horizontal position of the text
+ */
+
+ g_object_class_install_property (object_class, PROP_XPOS,
+ g_param_spec_double ("xpos", "Xpos", "The horizontal position",
+ 0, 1, 0.5, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * GESTextOverlayClip:ypos:
+ *
+ * The vertical position of the text
+ */
+
+ g_object_class_install_property (object_class, PROP_YPOS,
+ g_param_spec_double ("ypos", "Ypos", "The vertical position",
+ 0, 1, 0.5, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+ges_text_overlay_clip_init (GESTextOverlayClip * self)
+{
+ self->priv = ges_text_overlay_clip_get_instance_private (self);
+
+ GES_TIMELINE_ELEMENT (self)->duration = 0;
+ /* Not 100% needed since gobject contents are memzero'd when created */
+ self->priv->text = NULL;
+ self->priv->font_desc = NULL;
+ self->priv->halign = DEFAULT_PROP_HALIGNMENT;
+ self->priv->valign = DEFAULT_PROP_VALIGNMENT;
+ self->priv->color = G_MAXUINT32;
+ self->priv->xpos = 0.5;
+ self->priv->ypos = 0.5;
+}
+
+/**
+ * ges_text_overlay_clip_set_text:
+ * @self: the #GESTextOverlayClip* to set text on
+ * @text: the text to render. an internal copy of this text will be
+ * made.
+ *
+ * Sets the text this clip will render.
+ *
+ */
+void
+ges_text_overlay_clip_set_text (GESTextOverlayClip * self, const gchar * text)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, text:%s", self, text);
+
+ if (self->priv->text)
+ g_free (self->priv->text);
+
+ self->priv->text = g_strdup (text);
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_text (GES_TEXT_OVERLAY (trackelement),
+ self->priv->text);
+ }
+}
+
+/**
+ * ges_text_overlay_clip_set_font_desc:
+ * @self: the #GESTextOverlayClip*
+ * @font_desc: the pango font description
+ *
+ * Sets the pango font description of the text
+ *
+ */
+void
+ges_text_overlay_clip_set_font_desc (GESTextOverlayClip * self,
+ const gchar * font_desc)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, font_desc:%s", self, font_desc);
+
+ if (self->priv->font_desc)
+ g_free (self->priv->font_desc);
+
+ self->priv->font_desc = g_strdup (font_desc);
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_font_desc (GES_TEXT_OVERLAY
+ (trackelement), self->priv->font_desc);
+ }
+
+}
+
+/**
+ * ges_text_overlay_clip_set_halign:
+ * @self: the #GESTextOverlayClip* to set horizontal alignement of text on
+ * @halign: #GESTextHAlign
+ *
+ * Sets the horizontal aligment of the text.
+ *
+ */
+void
+ges_text_overlay_clip_set_halign (GESTextOverlayClip * self,
+ GESTextHAlign halign)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, halign:%d", self, halign);
+
+ self->priv->halign = halign;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_halignment (GES_TEXT_OVERLAY
+ (trackelement), self->priv->halign);
+ }
+
+}
+
+/**
+ * ges_text_overlay_clip_set_valign:
+ * @self: the #GESTextOverlayClip* to set vertical alignement of text on
+ * @valign: #GESTextVAlign
+ *
+ * Sets the vertical aligment of the text.
+ *
+ */
+void
+ges_text_overlay_clip_set_valign (GESTextOverlayClip * self,
+ GESTextVAlign valign)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, valign:%d", self, valign);
+
+ self->priv->valign = valign;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_valignment (GES_TEXT_OVERLAY
+ (trackelement), self->priv->valign);
+ }
+
+}
+
+/**
+ * ges_text_overlay_clip_set_color:
+ * @self: the #GESTextOverlayClip* to set
+ * @color: The color @self is being set to
+ *
+ * Sets the color of the text.
+ */
+void
+ges_text_overlay_clip_set_color (GESTextOverlayClip * self, guint32 color)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, color:%d", self, color);
+
+ self->priv->color = color;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_color (GES_TEXT_OVERLAY (trackelement),
+ self->priv->color);
+ }
+}
+
+/**
+ * ges_text_overlay_clip_set_xpos:
+ * @self: the #GESTextOverlayClip* to set
+ * @position: The horizontal position @self is being set to
+ *
+ * Sets the horizontal position of the text.
+ */
+void
+ges_text_overlay_clip_set_xpos (GESTextOverlayClip * self, gdouble position)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, xpos:%f", self, position);
+
+ self->priv->xpos = position;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_xpos (GES_TEXT_OVERLAY (trackelement),
+ self->priv->xpos);
+ }
+}
+
+/**
+ * ges_text_overlay_clip_set_ypos:
+ * @self: the #GESTextOverlayClip* to set
+ * @position: The vertical position @self is being set to
+ *
+ * Sets the vertical position of the text.
+ */
+void
+ges_text_overlay_clip_set_ypos (GESTextOverlayClip * self, gdouble position)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, ypos:%f", self, position);
+
+ self->priv->ypos = position;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_VIDEO)
+ ges_text_overlay_set_ypos (GES_TEXT_OVERLAY (trackelement),
+ self->priv->ypos);
+ }
+}
+
+/**
+ * ges_text_overlay_clip_get_text:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the text currently set on @self.
+ *
+ * Returns: The text currently set on @self.
+ *
+ */
+const gchar *
+ges_text_overlay_clip_get_text (GESTextOverlayClip * self)
+{
+ return self->priv->text;
+}
+
+/**
+ * ges_text_overlay_clip_get_font_desc:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the pango font description used by @self.
+ *
+ * Returns: The pango font description used by @self.
+ */
+const char *
+ges_text_overlay_clip_get_font_desc (GESTextOverlayClip * self)
+{
+ return self->priv->font_desc;
+}
+
+/**
+ * ges_text_overlay_clip_get_halignment:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the horizontal aligment used by @self.
+ *
+ * Returns: The horizontal aligment used by @self.
+ */
+GESTextHAlign
+ges_text_overlay_clip_get_halignment (GESTextOverlayClip * self)
+{
+ return self->priv->halign;
+}
+
+/**
+ * ges_text_overlay_clip_get_valignment:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the vertical aligment used by @self.
+ *
+ * Returns: The vertical aligment used by @self.
+ */
+GESTextVAlign
+ges_text_overlay_clip_get_valignment (GESTextOverlayClip * self)
+{
+ return self->priv->valign;
+}
+
+/**
+ * ges_text_overlay_clip_get_color:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the color used by @source.
+ *
+ * Returns: The color used by @source.
+ */
+
+const guint32
+ges_text_overlay_clip_get_color (GESTextOverlayClip * self)
+{
+ return self->priv->color;
+}
+
+/**
+ * ges_text_overlay_clip_get_xpos:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the horizontal position used by @source.
+ *
+ * Returns: The horizontal position used by @source.
+ */
+
+const gdouble
+ges_text_overlay_clip_get_xpos (GESTextOverlayClip * self)
+{
+ return self->priv->xpos;
+}
+
+/**
+ * ges_text_overlay_clip_get_ypos:
+ * @self: a #GESTextOverlayClip
+ *
+ * Get the vertical position used by @source.
+ *
+ * Returns: The vertical position used by @source.
+ */
+
+const gdouble
+ges_text_overlay_clip_get_ypos (GESTextOverlayClip * self)
+{
+ return self->priv->ypos;
+}
+
+static GESTrackElement *
+ges_text_overlay_clip_create_track_element (GESClip * clip, GESTrackType type)
+{
+
+ GESTextOverlayClipPrivate *priv = GES_OVERLAY_TEXT_CLIP (clip)->priv;
+ GESTrackElement *res = NULL;
+
+ GST_DEBUG ("Creating a GESTrackOverlay");
+
+ if (type == GES_TRACK_TYPE_VIDEO) {
+ res = (GESTrackElement *) ges_text_overlay_new ();
+ GST_DEBUG ("Setting text property");
+ ges_text_overlay_set_text ((GESTextOverlay *) res, priv->text);
+ ges_text_overlay_set_font_desc ((GESTextOverlay *) res, priv->font_desc);
+ ges_text_overlay_set_halignment ((GESTextOverlay *) res, priv->halign);
+ ges_text_overlay_set_valignment ((GESTextOverlay *) res, priv->valign);
+ ges_text_overlay_set_color ((GESTextOverlay *) res, priv->color);
+ ges_text_overlay_set_xpos ((GESTextOverlay *) res, priv->xpos);
+ ges_text_overlay_set_ypos ((GESTextOverlay *) res, priv->ypos);
+ }
+
+ return res;
+}
+
+/**
+ * ges_text_overlay_clip_new:
+ *
+ * Creates a new #GESTextOverlayClip
+ *
+ * Returns: (transfer floating) (nullable): The newly created
+ * #GESTextOverlayClip, or %NULL if there was an error.
+ */
+GESTextOverlayClip *
+ges_text_overlay_clip_new (void)
+{
+ GESTextOverlayClip *new_clip;
+ GESAsset *asset = ges_asset_request (GES_TYPE_OVERLAY_TEXT_CLIP, NULL, NULL);
+
+ new_clip = GES_OVERLAY_TEXT_CLIP (ges_asset_extract (asset, NULL));
+ gst_object_unref (asset);
+
+ return new_clip;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_OVERLAY_TEXT_CLIP
+#define _GES_OVERLAY_TEXT_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-overlay-clip.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+#define GES_TYPE_OVERLAY_TEXT_CLIP ges_text_overlay_clip_get_type()
+#define GES_OVERLAY_TEXT_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_OVERLAY_TEXT_CLIP, GESTextOverlayClip))
+#define GES_OVERLAY_TEXT_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_OVERLAY_TEXT_CLIP, GESTextOverlayClipClass))
+#define GES_IS_OVERLAY_TEXT_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_OVERLAY_TEXT_CLIP))
+#define GES_IS_OVERLAY_TEXT_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_OVERLAY_TEXT_CLIP))
+#define GES_OVERLAY_TEXT_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_OVERLAY_TEXT_CLIP, GESTextOverlayClipClass))
+typedef struct _GESTextOverlayClipPrivate GESTextOverlayClipPrivate;
+
+/**
+ * GESTextOverlayClip:
+ */
+
+struct _GESTextOverlayClip
+{
+ GESOverlayClip parent;
+
+ /*< private > */
+ GESTextOverlayClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTextOverlayClipClass:
+ */
+
+struct _GESTextOverlayClipClass
+{
+ /*< private > */
+
+ GESOverlayClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_text_overlay_clip_get_type (void);
+
+GES_API void
+ges_text_overlay_clip_set_text (GESTextOverlayClip * self,
+ const gchar * text);
+
+GES_API void
+ges_text_overlay_clip_set_font_desc (GESTextOverlayClip * self,
+ const gchar * font_desc);
+
+GES_API void
+ges_text_overlay_clip_set_valign (GESTextOverlayClip * self,
+ GESTextVAlign valign);
+
+GES_API void
+ges_text_overlay_clip_set_halign (GESTextOverlayClip * self,
+ GESTextHAlign halign);
+
+GES_API void
+ges_text_overlay_clip_set_color (GESTextOverlayClip * self,
+ guint32 color);
+
+GES_API void
+ges_text_overlay_clip_set_xpos (GESTextOverlayClip * self,
+ gdouble position);
+
+GES_API void
+ges_text_overlay_clip_set_ypos (GESTextOverlayClip * self,
+ gdouble position);
+
+GES_API
+const gchar *ges_text_overlay_clip_get_text (GESTextOverlayClip * self);
+
+GES_API
+const gchar *ges_text_overlay_clip_get_font_desc (GESTextOverlayClip *
+ self);
+
+GES_API GESTextVAlign
+ges_text_overlay_clip_get_valignment (GESTextOverlayClip * self);
+
+GES_API const guint32
+ges_text_overlay_clip_get_color (GESTextOverlayClip * self);
+
+GES_API const gdouble
+ges_text_overlay_clip_get_xpos (GESTextOverlayClip * self);
+
+GES_API const gdouble
+ges_text_overlay_clip_get_ypos (GESTextOverlayClip * self);
+
+GES_API GESTextHAlign
+ges_text_overlay_clip_get_halignment (GESTextOverlayClip * self);
+
+GES_API
+GESTextOverlayClip *ges_text_overlay_clip_new (void);
+
+G_END_DECLS
+#endif /* _GES_TL_OVERLAY */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestextoverlay
+ * @title: GESTextOverlay
+ * @short_description: render text onto another video stream in a GESLayer
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-title-source.h"
+#include "ges-text-overlay.h"
+
+struct _GESTextOverlayPrivate
+{
+ gchar *text;
+ gchar *font_desc;
+ GESTextHAlign halign;
+ GESTextVAlign valign;
+ guint32 color;
+ gdouble xpos;
+ gdouble ypos;
+ GstElement *text_el;
+};
+
+enum
+{
+ PROP_0,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESTextOverlay, ges_text_overlay,
+ GES_TYPE_OPERATION);
+
+static void ges_text_overlay_dispose (GObject * object);
+
+static void ges_text_overlay_finalize (GObject * object);
+
+static void ges_text_overlay_get_property (GObject * object, guint
+ property_id, GValue * value, GParamSpec * pspec);
+
+static void ges_text_overlay_set_property (GObject * object, guint
+ property_id, const GValue * value, GParamSpec * pspec);
+
+static GstElement *ges_text_overlay_create_element (GESTrackElement * self);
+
+static void
+ges_text_overlay_class_init (GESTextOverlayClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESTrackElementClass *bg_class = GES_TRACK_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_text_overlay_get_property;
+ object_class->set_property = ges_text_overlay_set_property;
+ object_class->dispose = ges_text_overlay_dispose;
+ object_class->finalize = ges_text_overlay_finalize;
+
+ bg_class->create_element = ges_text_overlay_create_element;
+}
+
+static void
+ges_text_overlay_init (GESTextOverlay * self)
+{
+ self->priv = ges_text_overlay_get_instance_private (self);
+
+ self->priv->text = NULL;
+ self->priv->font_desc = NULL;
+ self->priv->text_el = NULL;
+ self->priv->halign = DEFAULT_HALIGNMENT;
+ self->priv->valign = DEFAULT_VALIGNMENT;
+ self->priv->color = G_MAXUINT32;
+ self->priv->xpos = 0.5;
+ self->priv->ypos = 0.5;
+}
+
+static void
+ges_text_overlay_dispose (GObject * object)
+{
+ GESTextOverlay *self = GES_TEXT_OVERLAY (object);
+ if (self->priv->text) {
+ g_free (self->priv->text);
+ }
+
+ if (self->priv->font_desc) {
+ g_free (self->priv->font_desc);
+ }
+
+ if (self->priv->text_el) {
+ gst_object_unref (self->priv->text_el);
+ self->priv->text_el = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_text_overlay_parent_class)->dispose (object);
+}
+
+static void
+ges_text_overlay_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ges_text_overlay_parent_class)->finalize (object);
+}
+
+static void
+ges_text_overlay_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_text_overlay_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GstElement *
+ges_text_overlay_create_element (GESTrackElement * track_element)
+{
+ GstElement *ret, *text, *iconv, *oconv;
+ GstPad *src_target, *sink_target;
+ GstPad *src, *sink;
+ GESTextOverlay *self = GES_TEXT_OVERLAY (track_element);
+ const gchar *child_props[] =
+ { "xpos", "ypos", "deltax", "deltay", "auto-resize", "outline-color",
+ NULL
+ };
+
+ text = gst_element_factory_make ("textoverlay", NULL);
+ iconv = gst_element_factory_make ("videoconvert", NULL);
+ oconv = gst_element_factory_make ("videoconvert", NULL);
+ self->priv->text_el = text;
+ gst_object_ref (text);
+
+ if (self->priv->text)
+ g_object_set (text, "text", (gchar *) self->priv->text, NULL);
+ if (self->priv->font_desc)
+ g_object_set (text, "font-desc", (gchar *) self->priv->font_desc, NULL);
+
+ g_object_set (text, "halignment", (gint) self->priv->halign, "valignment",
+ (gint) self->priv->valign, NULL);
+ g_object_set (text, "color", (guint) self->priv->color, NULL);
+ g_object_set (text, "xpos", (gdouble) self->priv->xpos, NULL);
+ g_object_set (text, "ypos", (gdouble) self->priv->ypos, NULL);
+
+ ges_track_element_add_children_props (track_element, text, NULL, NULL,
+ child_props);
+
+ ret = gst_bin_new ("overlay-bin");
+ gst_bin_add_many (GST_BIN (ret), text, iconv, oconv, NULL);
+ gst_element_link_many (iconv, text, oconv, NULL);
+
+ src_target = gst_element_get_static_pad (oconv, "src");
+ sink_target = gst_element_get_static_pad (iconv, "sink");
+
+ src = gst_ghost_pad_new ("src", src_target);
+ sink = gst_ghost_pad_new ("video_sink", sink_target);
+ gst_object_unref (src_target);
+ gst_object_unref (sink_target);
+
+ gst_element_add_pad (ret, src);
+ gst_element_add_pad (ret, sink);
+
+ return ret;
+}
+
+/**
+ * ges_text_overlay_set_text:
+ * @self: the #GESTextOverlay* to set text on
+ * @text: the text to render. an internal copy of this text will be
+ * made.
+ *
+ * Sets the text this track element will render.
+ *
+ */
+void
+ges_text_overlay_set_text (GESTextOverlay * self, const gchar * text)
+{
+ GST_DEBUG ("self:%p, text:%s", self, text);
+
+ if (self->priv->text)
+ g_free (self->priv->text);
+
+ self->priv->text = g_strdup (text);
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "text", text, NULL);
+}
+
+/**
+ * ges_text_overlay_set_font_desc:
+ * @self: the #GESTextOverlay
+ * @font_desc: the pango font description
+ *
+ * Sets the pango font description of the text this track element
+ * will render.
+ *
+ */
+void
+ges_text_overlay_set_font_desc (GESTextOverlay * self, const gchar * font_desc)
+{
+ GST_DEBUG ("self:%p, font_desc:%s", self, font_desc);
+
+ if (self->priv->font_desc)
+ g_free (self->priv->font_desc);
+
+ self->priv->font_desc = g_strdup (font_desc);
+ GST_LOG ("setting font-desc to '%s'", font_desc);
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "font-desc", font_desc, NULL);
+}
+
+/**
+ * ges_text_overlay_set_valignment:
+ * @self: the #GESTextOverlay* to set text on
+ * @valign: The #GESTextVAlign defining the vertical alignment
+ * of the text render by @self.
+ *
+ * Sets the vertical aligment of the text.
+ *
+ */
+void
+ges_text_overlay_set_valignment (GESTextOverlay * self, GESTextVAlign valign)
+{
+ GST_DEBUG ("self:%p, halign:%d", self, valign);
+
+ self->priv->valign = valign;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "valignment", valign, NULL);
+}
+
+/**
+ * ges_text_overlay_set_halignment:
+ * @self: the #GESTextOverlay* to set text on
+ * @halign: The #GESTextHAlign defining the horizontal alignment
+ * of the text render by @self.
+ *
+ * Sets the horizontal aligment of the text.
+ *
+ */
+void
+ges_text_overlay_set_halignment (GESTextOverlay * self, GESTextHAlign halign)
+{
+ GST_DEBUG ("self:%p, halign:%d", self, halign);
+
+ self->priv->halign = halign;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "halignment", halign, NULL);
+}
+
+/**
+ * ges_text_overlay_set_color:
+ * @self: the #GESTextOverlay* to set
+ * @color: The color @self is being set to
+ *
+ * Sets the color of the text.
+ */
+void
+ges_text_overlay_set_color (GESTextOverlay * self, guint32 color)
+{
+ GST_DEBUG ("self:%p, color:%d", self, color);
+
+ self->priv->color = color;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "color", color, NULL);
+}
+
+/**
+ * ges_text_overlay_set_xpos:
+ * @self: the #GESTextOverlay* to set
+ * @position: The horizontal position @self is being set to
+ *
+ * Sets the horizontal position of the text.
+ */
+void
+ges_text_overlay_set_xpos (GESTextOverlay * self, gdouble position)
+{
+ GST_DEBUG ("self:%p, xpos:%f", self, position);
+
+ self->priv->xpos = position;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "xpos", position, NULL);
+}
+
+/**
+ * ges_text_overlay_set_ypos:
+ * @self: the #GESTextOverlay* to set
+ * @position: The vertical position @self is being set to
+ *
+ * Sets the vertical position of the text.
+ */
+void
+ges_text_overlay_set_ypos (GESTextOverlay * self, gdouble position)
+{
+ GST_DEBUG ("self:%p, ypos:%f", self, position);
+
+ self->priv->ypos = position;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "ypos", position, NULL);
+}
+
+/**
+ * ges_text_overlay_get_text:
+ * @self: a GESTextOverlay
+ *
+ * Get the text currently set on @source.
+ *
+ * Returns: The text currently set on @source.
+ */
+const gchar *
+ges_text_overlay_get_text (GESTextOverlay * self)
+{
+ return self->priv->text;
+}
+
+/**
+ * ges_text_overlay_get_font_desc:
+ * @self: a GESTextOverlay
+ *
+ * Get the pango font description currently set on @source.
+ *
+ * Returns: The pango font description currently set on @source.
+ */
+const char *
+ges_text_overlay_get_font_desc (GESTextOverlay * self)
+{
+ return self->priv->font_desc;
+}
+
+/**
+ * ges_text_overlay_get_halignment:
+ * @self: a GESTextOverlay
+ *
+ * Get the horizontal aligment used by @source.
+ *
+ * Returns: The horizontal aligment used by @source.
+ */
+GESTextHAlign
+ges_text_overlay_get_halignment (GESTextOverlay * self)
+{
+ return self->priv->halign;
+}
+
+/**
+ * ges_text_overlay_get_valignment:
+ * @self: a GESTextOverlay
+ *
+ * Get the vertical aligment used by @source.
+ *
+ * Returns: The vertical aligment used by @source.
+ */
+GESTextVAlign
+ges_text_overlay_get_valignment (GESTextOverlay * self)
+{
+ return self->priv->valign;
+}
+
+/**
+ * ges_text_overlay_get_color:
+ * @self: a GESTextOverlay
+ *
+ * Get the color used by @source.
+ *
+ * Returns: The color used by @source.
+ */
+const guint32
+ges_text_overlay_get_color (GESTextOverlay * self)
+{
+ return self->priv->color;
+}
+
+/**
+ * ges_text_overlay_get_xpos:
+ * @self: a GESTextOverlay
+ *
+ * Get the horizontal position used by @source.
+ *
+ * Returns: The horizontal position used by @source.
+ */
+const gdouble
+ges_text_overlay_get_xpos (GESTextOverlay * self)
+{
+ return self->priv->xpos;
+}
+
+/**
+ * ges_text_overlay_get_ypos:
+ * @self: a GESTextOverlay
+ *
+ * Get the vertical position used by @source.
+ *
+ * Returns: The vertical position used by @source.
+ */
+const gdouble
+ges_text_overlay_get_ypos (GESTextOverlay * self)
+{
+ return self->priv->ypos;
+}
+
+/**
+ * ges_text_overlay_new:
+ *
+ * Creates a new #GESTextOverlay.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESTextOverlay or
+ * %NULL if something went wrong.
+ */
+GESTextOverlay *
+ges_text_overlay_new (void)
+{
+ return g_object_new (GES_TYPE_TEXT_OVERLAY, "track-type",
+ GES_TRACK_TYPE_VIDEO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_TEXT_OVERLAY
+#define _GES_TEXT_OVERLAY
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-title-source.h>
+#include <ges/ges-operation.h>
+
+G_BEGIN_DECLS
+#define GES_TYPE_TEXT_OVERLAY ges_text_overlay_get_type()
+#define GES_TEXT_OVERLAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TEXT_OVERLAY, GESTextOverlay))
+#define GES_TEXT_OVERLAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TEXT_OVERLAY, GESTextOverlayClass))
+#define GES_IS_TEXT_OVERLAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TEXT_OVERLAY))
+#define GES_IS_TEXT_OVERLAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TEXT_OVERLAY))
+#define GES_TEXT_OVERLAY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TEXT_OVERLAY, GESTextOverlayClass))
+typedef struct _GESTextOverlayPrivate GESTextOverlayPrivate;
+
+/**
+ * GESTextOverlay:
+ */
+struct _GESTextOverlay
+{
+ GESOperation parent;
+
+ /*< private > */
+ GESTextOverlayPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESTextOverlayClass
+{
+ GESOperationClass parent_class;
+
+ /*< private > */
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_text_overlay_get_type (void);
+
+GES_API
+void ges_text_overlay_set_text (GESTextOverlay * self,
+ const gchar * text);
+GES_API
+void ges_text_overlay_set_font_desc (GESTextOverlay * self,
+ const gchar * font_desc);
+
+GES_API
+void ges_text_overlay_set_halignment (GESTextOverlay * self,
+ GESTextHAlign halign);
+
+GES_API
+void ges_text_overlay_set_valignment (GESTextOverlay * self,
+ GESTextVAlign valign);
+GES_API
+void ges_text_overlay_set_color (GESTextOverlay * self,
+ guint32 color);
+GES_API
+void ges_text_overlay_set_xpos (GESTextOverlay * self,
+ gdouble position);
+GES_API
+void ges_text_overlay_set_ypos (GESTextOverlay * self,
+ gdouble position);
+
+GES_API
+const gchar *ges_text_overlay_get_text (GESTextOverlay * self);
+GES_API
+const char *ges_text_overlay_get_font_desc (GESTextOverlay * self);
+GES_API
+GESTextHAlign ges_text_overlay_get_halignment (GESTextOverlay *
+ self);
+GES_API
+GESTextVAlign ges_text_overlay_get_valignment (GESTextOverlay *
+ self);
+GES_API
+const guint32 ges_text_overlay_get_color (GESTextOverlay * self);
+GES_API
+const gdouble ges_text_overlay_get_xpos (GESTextOverlay * self);
+GES_API
+const gdouble ges_text_overlay_get_ypos (GESTextOverlay * self);
+
+GES_API
+GESTextOverlay *ges_text_overlay_new (void);
+
+G_END_DECLS
+#endif /* _GES_TEXT_OVERLAY */
--- /dev/null
+/* gst-editing-services
+ * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
+ * <2013> Collabora Ltd.
+ *
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gestimelineelement
+ * @title: GESTimelineElement
+ * @short_description: Base Class for all elements that will be in a way or
+ * another inside a GESTimeline.
+ *
+ * The GESTimelineElement base class implements the notion of timing as well
+ * as priority. A GESTimelineElement can have a parent object which will be
+ * responsible for controlling its timing properties.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-utils.h"
+#include "ges-timeline-element.h"
+#include "ges-extractable.h"
+#include "ges-meta-container.h"
+#include "ges-internal.h"
+#include "ges-effect.h"
+
+#include <string.h>
+#include <gobject/gvaluecollector.h>
+
+/* maps type name quark => count */
+static GData *object_name_counts = NULL;
+
+static void
+extractable_set_asset (GESExtractable * extractable, GESAsset * asset)
+{
+ GES_TIMELINE_ELEMENT (extractable)->asset = asset;
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->set_asset = extractable_set_asset;
+}
+
+enum
+{
+ PROP_0,
+ PROP_PARENT,
+ PROP_TIMELINE,
+ PROP_START,
+ PROP_INPOINT,
+ PROP_DURATION,
+ PROP_MAX_DURATION,
+ PROP_PRIORITY,
+ PROP_NAME,
+ PROP_SERIALIZE,
+ PROP_LAST
+};
+
+enum
+{
+ DEEP_NOTIFY,
+ LAST_SIGNAL
+};
+
+static guint ges_timeline_element_signals[LAST_SIGNAL] = { 0 };
+
+static GParamSpec *properties[PROP_LAST] = { NULL, };
+
+typedef struct
+{
+ GObject *child;
+ gulong handler_id;
+} ChildPropHandler;
+
+struct _GESTimelineElementPrivate
+{
+ gboolean serialize;
+
+ /* We keep a link between properties name and elements internally
+ * The hashtable should look like
+ * {GParamaSpec ---> child}*/
+ GHashTable *children_props;
+
+ GESTimelineElement *copied_from;
+
+ GESTimelineElementFlags flags;
+};
+
+typedef struct
+{
+ GObject *child;
+ GParamSpec *arg;
+ GESTimelineElement *self;
+} EmitDeepNotifyInIdleData;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESTimelineElement, ges_timeline_element,
+ G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (GESTimelineElement)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, ges_extractable_interface_init)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
+
+static void
+_set_child_property (GESTimelineElement * self G_GNUC_UNUSED, GObject * child,
+ GParamSpec * pspec, GValue * value)
+{
+ g_object_set_property (child, pspec->name, value);
+}
+
+static gboolean
+_lookup_child (GESTimelineElement * self, const gchar * prop_name,
+ GObject ** child, GParamSpec ** pspec)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ gchar **names, *name, *classename;
+ gboolean res;
+
+ classename = NULL;
+ res = FALSE;
+
+ names = g_strsplit (prop_name, "::", 2);
+ if (names[1] != NULL) {
+ classename = names[0];
+ name = names[1];
+ } else
+ name = names[0];
+
+ g_hash_table_iter_init (&iter, self->priv->children_props);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
+ ChildPropHandler *handler = (ChildPropHandler *) value;
+ if (classename == NULL ||
+ g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (handler->child)),
+ classename) == 0 ||
+ g_strcmp0 (g_type_name (G_PARAM_SPEC (key)->owner_type),
+ classename) == 0) {
+ GST_DEBUG_OBJECT (self, "The %s property from %s has been found", name,
+ classename);
+ if (child)
+ *child = gst_object_ref (handler->child);
+
+ if (pspec)
+ *pspec = g_param_spec_ref (key);
+ res = TRUE;
+ break;
+ }
+ }
+ }
+ g_strfreev (names);
+
+ return res;
+}
+
+static GParamSpec **
+default_list_children_properties (GESTimelineElement * self,
+ guint * n_properties)
+{
+ GParamSpec **pspec, *spec;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ guint i = 0;
+
+ *n_properties = g_hash_table_size (self->priv->children_props);
+ pspec = g_new (GParamSpec *, *n_properties);
+
+ g_hash_table_iter_init (&iter, self->priv->children_props);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ spec = G_PARAM_SPEC (key);
+ pspec[i] = g_param_spec_ref (spec);
+ i++;
+ }
+
+ return pspec;
+}
+
+static void
+_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
+
+ switch (property_id) {
+ case PROP_PARENT:
+ g_value_take_object (value, self->parent);
+ break;
+ case PROP_TIMELINE:
+ g_value_take_object (value, self->timeline);
+ break;
+ case PROP_START:
+ g_value_set_uint64 (value, self->start);
+ break;
+ case PROP_INPOINT:
+ g_value_set_uint64 (value, self->inpoint);
+ break;
+ case PROP_DURATION:
+ g_value_set_uint64 (value, self->duration);
+ break;
+ case PROP_MAX_DURATION:
+ g_value_set_uint64 (value, self->maxduration);
+ break;
+ case PROP_PRIORITY:
+ g_value_set_uint (value, self->priority);
+ break;
+ case PROP_NAME:
+ g_value_take_string (value, ges_timeline_element_get_name (self));
+ break;
+ case PROP_SERIALIZE:
+ g_value_set_boolean (value, self->priv->serialize);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ }
+}
+
+static void
+_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
+
+ switch (property_id) {
+ case PROP_PARENT:
+ ges_timeline_element_set_parent (self, g_value_get_object (value));
+ break;
+ case PROP_TIMELINE:
+ ges_timeline_element_set_timeline (self, g_value_get_object (value));
+ break;
+ case PROP_START:
+ ges_timeline_element_set_start (self, g_value_get_uint64 (value));
+ break;
+ case PROP_INPOINT:
+ ges_timeline_element_set_inpoint (self, g_value_get_uint64 (value));
+ break;
+ case PROP_DURATION:
+ ges_timeline_element_set_duration (self, g_value_get_uint64 (value));
+ break;
+ case PROP_PRIORITY:
+ ges_timeline_element_set_priority (self, g_value_get_uint (value));
+ break;
+ case PROP_MAX_DURATION:
+ ges_timeline_element_set_max_duration (self, g_value_get_uint64 (value));
+ break;
+ case PROP_NAME:
+ ges_timeline_element_set_name (self, g_value_get_string (value));
+ break;
+ case PROP_SERIALIZE:
+ self->priv->serialize = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ }
+}
+
+static void
+ges_timeline_element_dispose (GObject * object)
+{
+ GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
+
+ if (self->priv->children_props) {
+ g_hash_table_unref (self->priv->children_props);
+ self->priv->children_props = NULL;
+ }
+
+ g_clear_object (&self->priv->copied_from);
+
+ G_OBJECT_CLASS (ges_timeline_element_parent_class)->dispose (object);
+}
+
+static void
+ges_timeline_element_finalize (GObject * self)
+{
+ GESTimelineElement *tle = GES_TIMELINE_ELEMENT (self);
+
+ g_free (tle->name);
+
+ G_OBJECT_CLASS (ges_timeline_element_parent_class)->finalize (self);
+}
+
+static void
+_child_prop_handler_free (ChildPropHandler * handler)
+{
+ g_object_freeze_notify (handler->child);
+ if (handler->handler_id)
+ g_signal_handler_disconnect (handler->child, handler->handler_id);
+ g_object_thaw_notify (handler->child);
+ gst_object_unref (handler->child);
+ g_slice_free (ChildPropHandler, handler);
+}
+
+static void
+ges_timeline_element_init (GESTimelineElement * self)
+{
+ self->priv = ges_timeline_element_get_instance_private (self);
+
+ self->priv->serialize = TRUE;
+
+ self->priv->children_props =
+ g_hash_table_new_full ((GHashFunc) ges_pspec_hash, ges_pspec_equal,
+ (GDestroyNotify) g_param_spec_unref,
+ (GDestroyNotify) _child_prop_handler_free);
+}
+
+static void
+ges_timeline_element_class_init (GESTimelineElementClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = _get_property;
+ object_class->set_property = _set_property;
+
+ /**
+ * GESTimelineElement:parent:
+ *
+ * The parent container of the object
+ */
+ properties[PROP_PARENT] =
+ g_param_spec_object ("parent", "Parent",
+ "The parent container of the object", GES_TYPE_TIMELINE_ELEMENT,
+ G_PARAM_READWRITE);
+
+ /**
+ * GESTimelineElement:timeline:
+ *
+ * The timeline in which @element is
+ */
+ properties[PROP_TIMELINE] =
+ g_param_spec_object ("timeline", "Timeline",
+ "The timeline the object is in", GES_TYPE_TIMELINE, G_PARAM_READWRITE);
+
+ /**
+ * GESTimelineElement:start:
+ *
+ * The position of the object in its container (in nanoseconds).
+ */
+ properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
+ "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
+
+ /**
+ * GESTimelineElement:in-point:
+ *
+ * The in-point at which this #GESTimelineElement will start outputting data
+ * from its contents (in nanoseconds).
+ *
+ * Ex : an in-point of 5 seconds means that the first outputted buffer will
+ * be the one located 5 seconds in the controlled resource.
+ */
+ properties[PROP_INPOINT] =
+ g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
+ G_MAXUINT64, 0, G_PARAM_READWRITE);
+
+ /**
+ * GESTimelineElement:duration:
+ *
+ * The duration (in nanoseconds) which will be used in the container
+ */
+ properties[PROP_DURATION] =
+ g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
+ G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
+
+ /**
+ * GESTimelineElement:max-duration:
+ *
+ * The maximum duration (in nanoseconds) of the #GESTimelineElement.
+ */
+ properties[PROP_MAX_DURATION] =
+ g_param_spec_uint64 ("max-duration", "Maximum duration",
+ "The maximum duration of the object", 0, G_MAXUINT64, GST_CLOCK_TIME_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ /**
+ * GESTimelineElement:priority:
+ *
+ * The priority of the object.
+ *
+ * Setting GESTimelineElement priorities is deprecated
+ * as all priority management is done by GES itself now.
+ */
+ properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
+ "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
+
+ /**
+ * GESTimelineElement:name:
+ *
+ * The name of the object
+ */
+ properties[PROP_NAME] =
+ g_param_spec_string ("name", "Name", "The name of the timeline object",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GESTimelineElement:serialize:
+ *
+ * Whether the element should be serialized.
+ */
+ properties[PROP_SERIALIZE] = g_param_spec_boolean ("serialize", "Serialize",
+ "Whether the element should be serialized", TRUE,
+ G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+
+ /**
+ * GESTimelineElement::deep-notify:
+ * @timeline_element: a #GESTtimelineElement
+ * @prop_object: the object that originated the signal
+ * @prop: the property that changed
+ *
+ * The deep notify signal is used to be notified of property changes of all
+ * the childs of @timeline_element
+ */
+ ges_timeline_element_signals[DEEP_NOTIFY] =
+ g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
+ G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_PARAM);
+
+ object_class->dispose = ges_timeline_element_dispose;
+ object_class->finalize = ges_timeline_element_finalize;
+
+ klass->set_parent = NULL;
+ klass->set_start = NULL;
+ klass->set_inpoint = NULL;
+ klass->set_duration = NULL;
+ klass->set_max_duration = NULL;
+ klass->set_priority = NULL;
+
+ klass->ripple = NULL;
+ klass->ripple_end = NULL;
+ klass->roll_start = NULL;
+ klass->roll_end = NULL;
+ klass->trim = NULL;
+
+ klass->list_children_properties = default_list_children_properties;
+ klass->lookup_child = _lookup_child;
+ klass->set_child_property = _set_child_property;
+}
+
+static void
+_set_name (GESTimelineElement * self, const gchar * wanted_name)
+{
+ const gchar *type_name;
+ gchar *lowcase_type;
+ gint count;
+ GQuark q;
+ guint i, l;
+ gchar *name = NULL;
+
+ if (!object_name_counts) {
+ g_datalist_init (&object_name_counts);
+ }
+
+ q = g_type_qname (G_OBJECT_TYPE (self));
+ count = GPOINTER_TO_INT (g_datalist_id_get_data (&object_name_counts, q));
+
+ /* GstFooSink -> foosink<N> */
+ type_name = g_quark_to_string (q);
+ if (strncmp (type_name, "GES", 3) == 0)
+ type_name += 3;
+
+ lowcase_type = g_strdup (type_name);
+ l = strlen (lowcase_type);
+ for (i = 0; i < l; i++)
+ lowcase_type[i] = g_ascii_tolower (lowcase_type[i]);
+
+ if (wanted_name == NULL) {
+ /* give the 20th "uriclip" element and the first "uriclip2" (if needed in the future)
+ * different names */
+ l = strlen (type_name);
+ if (l > 0 && g_ascii_isdigit (type_name[l - 1])) {
+ name = g_strdup_printf ("%s-%d", lowcase_type, count++);
+ } else {
+ name = g_strdup_printf ("%s%d", lowcase_type, count++);
+ }
+ } else {
+ /* If the wanted name uses the same 'namespace' as default, make
+ * sure it does not badly interfere with our counting system */
+
+ if (g_str_has_prefix (wanted_name, lowcase_type)) {
+ guint64 tmpcount =
+ g_ascii_strtoull (&wanted_name[strlen (lowcase_type)], NULL, 10);
+
+ if (tmpcount > count) {
+ count = tmpcount + 1;
+ GST_DEBUG_OBJECT (self, "Using same naming %s but updated count to %i",
+ wanted_name, count);
+ } else if (tmpcount < count) {
+ name = g_strdup_printf ("%s%d", lowcase_type, count);
+ count++;
+ GST_DEBUG_OBJECT (self, "Name %s already allocated, giving: %s instead"
+ " New count is %i", wanted_name, name, count);
+ } else {
+ count++;
+ GST_DEBUG_OBJECT (self, "Perfect name, just bumping object count");
+ }
+ }
+
+ if (name == NULL)
+ name = g_strdup (wanted_name);
+ }
+
+ g_free (lowcase_type);
+ g_datalist_id_set_data (&object_name_counts, q, GINT_TO_POINTER (count));
+
+ g_free (self->name);
+ self->name = name;
+}
+
+/*********************************************
+ * API implementation *
+ *********************************************/
+
+/**
+ * ges_timeline_element_set_parent:
+ * @self: a #GESTimelineElement
+ * @parent: new parent of self
+ *
+ * Sets the parent of @self to @parent. The parents needs to already
+ * own a hard reference on @self.
+ *
+ * Returns: %TRUE if @parent could be set or %FALSE when @self
+ * already had a parent or @self and @parent are the same.
+ */
+gboolean
+ges_timeline_element_set_parent (GESTimelineElement * self,
+ GESTimelineElement * parent)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+ g_return_val_if_fail (parent == NULL
+ || GES_IS_TIMELINE_ELEMENT (parent), FALSE);
+
+ if (self == parent) {
+ GST_INFO_OBJECT (self, "Trying to add %p in itself, not a good idea!",
+ self);
+ gst_object_ref_sink (self);
+ gst_object_unref (self);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (self, "set parent to %" GST_PTR_FORMAT, parent);
+
+ if (self->parent != NULL && parent != NULL)
+ goto had_parent;
+
+ if (GES_TIMELINE_ELEMENT_GET_CLASS (self)->set_parent) {
+ if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->set_parent (self, parent))
+ return FALSE;
+ }
+
+ self->parent = parent;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PARENT]);
+ return TRUE;
+
+ /* ERROR handling */
+had_parent:
+ {
+ GST_WARNING_OBJECT (self, "set parent failed, object already had a parent");
+ gst_object_ref_sink (self);
+ gst_object_unref (self);
+ return FALSE;
+ }
+}
+
+/**
+ * ges_timeline_element_get_parent:
+ * @self: a #GESTimelineElement
+ *
+ * Returns the parent of @self. This function increases the refcount
+ * of the parent object so you should gst_object_unref() it after usage.
+ *
+ * Returns: (transfer full) (nullable): parent of @self, this can be %NULL if
+ * @self has no parent. unref after usage.
+ */
+GESTimelineElement *
+ges_timeline_element_get_parent (GESTimelineElement * self)
+{
+ GESTimelineElement *result = NULL;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
+
+ result = self->parent;
+ if (G_LIKELY (result))
+ gst_object_ref (result);
+
+ return result;
+}
+
+/**
+ * ges_timeline_element_set_timeline:
+ * @self: a #GESTimelineElement
+ * @timeline: The #GESTimeline @self is in
+ *
+ * Sets the timeline of @self to @timeline.
+ *
+ * Returns: %TRUE if @timeline could be set or %FALSE when @timeline
+ * already had a timeline.
+ */
+gboolean
+ges_timeline_element_set_timeline (GESTimelineElement * self,
+ GESTimeline * timeline)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+ g_return_val_if_fail (timeline == NULL || GES_IS_TIMELINE (timeline), FALSE);
+
+ GST_DEBUG_OBJECT (self, "set timeline to %" GST_PTR_FORMAT, timeline);
+
+ if (timeline != NULL && G_UNLIKELY (self->timeline != NULL))
+ goto had_timeline;
+
+ if (timeline == NULL) {
+ if (self->timeline) {
+ if (!timeline_remove_element (self->timeline, self)) {
+ GST_INFO_OBJECT (self, "Could not remove from"
+ " currently set timeline %" GST_PTR_FORMAT, self->timeline);
+ return FALSE;
+ }
+ }
+ } else {
+ if (!timeline_add_element (timeline, self)) {
+ GST_INFO_OBJECT (self, "Could not add to timeline %" GST_PTR_FORMAT,
+ self);
+ return FALSE;
+ }
+ }
+
+ self->timeline = timeline;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TIMELINE]);
+ return TRUE;
+
+ /* ERROR handling */
+had_timeline:
+ {
+ GST_DEBUG_OBJECT (self, "set timeline failed, object already had a "
+ "timeline");
+ return FALSE;
+ }
+}
+
+/**
+ * ges_timeline_element_get_timeline:
+ * @self: a #GESTimelineElement
+ *
+ * Returns the timeline of @self. This function increases the refcount
+ * of the timeline so you should gst_object_unref() it after usage.
+ *
+ * Returns: (transfer full) (nullable): timeline of @self, this can be %NULL if
+ * @self has no timeline. unref after usage.
+ */
+GESTimeline *
+ges_timeline_element_get_timeline (GESTimelineElement * self)
+{
+ GESTimeline *result = NULL;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
+
+ result = self->timeline;
+ if (G_LIKELY (result))
+ gst_object_ref (result);
+
+ return result;
+}
+
+/**
+ * ges_timeline_element_set_start:
+ * @self: a #GESTimelineElement
+ * @start: the position in #GstClockTime
+ *
+ * Set the position of the object in its containing layer.
+ *
+ * Note that if the snapping-distance property of the timeline containing
+ * @self is set, @self will properly snap to the edges around @start.
+ *
+ * Returns: %TRUE if @start could be set.
+ */
+gboolean
+ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
+{
+ GESTimelineElementClass *klass;
+ GESTimelineElement *toplevel_container, *parent;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ if (self->start == start)
+ return TRUE;
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT
+ " new start: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start));
+
+ toplevel_container = ges_timeline_element_get_toplevel_parent (self);
+ parent = self->parent;
+
+ /* FIXME This should not belong to GESTimelineElement */
+ if (toplevel_container &&
+ ((gint64) (_START (toplevel_container) + start - _START (self))) < 0 &&
+ parent
+ && GES_CONTAINER (parent)->children_control_mode == GES_CHILDREN_UPDATE) {
+ GST_INFO_OBJECT (self,
+ "Can not move the object as it would imply its "
+ "container to have a negative start value");
+
+ gst_object_unref (toplevel_container);
+ return FALSE;
+ }
+
+ gst_object_unref (toplevel_container);
+ if (klass->set_start) {
+ gboolean res = klass->set_start (self, start);
+ if (res) {
+ self->start = start;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]);
+ }
+
+ GST_DEBUG_OBJECT (self, "New start: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)));
+ return res;
+ }
+
+ GST_WARNING_OBJECT (self, "No set_start virtual method implementation"
+ " on class %s. Can not set start %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_set_inpoint:
+ * @self: a #GESTimelineElement
+ * @inpoint: the in-point in #GstClockTime
+ *
+ * Set the in-point, that is the moment at which the @self will start
+ * outputting data from its contents.
+ *
+ * Returns: %TRUE if @inpoint could be set.
+ */
+gboolean
+ges_timeline_element_set_inpoint (GESTimelineElement * self,
+ GstClockTime inpoint)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ GST_DEBUG_OBJECT (self, "current inpoint: %" GST_TIME_FORMAT
+ " new inpoint: %" GST_TIME_FORMAT, GST_TIME_ARGS (inpoint),
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self)));
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (klass->set_inpoint) {
+ gboolean res = klass->set_inpoint (self, inpoint);
+ if (res) {
+ self->inpoint = inpoint;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INPOINT]);
+ }
+
+ return res;
+ }
+
+ GST_DEBUG_OBJECT (self, "No set_inpoint virtual method implementation"
+ " on class %s. Can not set inpoint %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (inpoint));
+
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_set_max_duration:
+ * @self: a #GESTimelineElement
+ * @maxduration: the maximum duration in #GstClockTime
+ *
+ * Set the maximun duration of the object
+ *
+ * Returns: %TRUE if @maxduration could be set.
+ */
+gboolean
+ges_timeline_element_set_max_duration (GESTimelineElement * self,
+ GstClockTime maxduration)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
+ " new duration: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_MAX_DURATION (self)),
+ GST_TIME_ARGS (maxduration));
+
+ if (klass->set_max_duration) {
+ if (klass->set_max_duration (self, maxduration) == FALSE)
+ return FALSE;
+ }
+
+ self->maxduration = maxduration;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_DURATION]);
+ return TRUE;
+}
+
+/**
+ * ges_timeline_element_set_duration:
+ * @self: a #GESTimelineElement
+ * @duration: the duration in #GstClockTime
+ *
+ * Set the duration of the object
+ *
+ * Note that if the timeline snap-distance property of the timeline containing
+ * @self is set, @self will properly snap to its neighboors.
+ *
+ * Returns: %TRUE if @duration could be set.
+ */
+gboolean
+ges_timeline_element_set_duration (GESTimelineElement * self,
+ GstClockTime duration)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
+ " new duration: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
+ GST_TIME_ARGS (duration));
+
+ if (klass->set_duration) {
+ gboolean res = klass->set_duration (self, duration);
+ if (res) {
+ self->duration = duration;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
+ }
+
+ return res;
+ }
+
+ GST_WARNING_OBJECT (self, "No set_duration virtual method implementation"
+ " on class %s. Can not set duration %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (duration));
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_get_start:
+ * @self: a #GESTimelineElement
+ *
+ * Returns: The @start of @self
+ */
+GstClockTime
+ges_timeline_element_get_start (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
+
+ return self->start;
+}
+
+/**
+ * ges_timeline_element_get_inpoint:
+ * @self: a #GESTimelineElement
+ *
+ * Returns: The @inpoint of @self
+ */
+GstClockTime
+ges_timeline_element_get_inpoint (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
+
+ return self->inpoint;
+}
+
+/**
+ * ges_timeline_element_get_duration:
+ * @self: a #GESTimelineElement
+ *
+ * Returns: The @duration of @self
+ */
+GstClockTime
+ges_timeline_element_get_duration (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
+
+ return self->duration;
+}
+
+/**
+ * ges_timeline_element_get_max_duration:
+ * @self: a #GESTimelineElement
+ *
+ * Returns: The @maxduration of @self
+ */
+GstClockTime
+ges_timeline_element_get_max_duration (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
+
+ return self->maxduration;
+}
+
+/**
+ * ges_timeline_element_get_priority:
+ * @self: a #GESTimelineElement
+ *
+ * Returns: The @priority of @self
+ */
+guint32
+ges_timeline_element_get_priority (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), 0);
+
+ return self->priority;
+}
+
+/**
+ * ges_timeline_element_set_priority:
+ * @self: a #GESTimelineElement
+ * @priority: the priority
+ *
+ * Sets the priority of the object within the containing layer
+ *
+ * Deprecated: All priority management is done by GES itself now.
+ * To set #GESEffect priorities #ges_clip_set_top_effect_index should
+ * be used.
+ *
+ * Returns: %TRUE if @priority could be set.
+ */
+gboolean
+ges_timeline_element_set_priority (GESTimelineElement * self, guint32 priority)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ GST_DEBUG_OBJECT (self, "current priority: %d new priority: %d",
+ self->priority, priority);
+
+ if (klass->set_priority) {
+ gboolean res = klass->set_priority (self, priority);
+ if (res) {
+ self->priority = priority;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRIORITY]);
+ }
+
+ return res;
+ }
+
+ GST_WARNING_OBJECT (self, "No set_priority virtual method implementation"
+ " on class %s. Can not set priority %d", G_OBJECT_CLASS_NAME (klass),
+ priority);
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_ripple:
+ * @self: The #GESTimelineElement to ripple.
+ * @start: The new start of @self in ripple mode.
+ *
+ * Edits @self in ripple mode. It allows you to modify the
+ * start of @self and move the following neighbours accordingly.
+ * This will change the overall timeline duration.
+ *
+ * Returns: %TRUE if the self as been rippled properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_timeline_element_ripple (GESTimelineElement * self, GstClockTime start)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (klass->ripple)
+ return klass->ripple (self, start);
+
+ GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
+ " on class %s. Can not ripple to %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
+
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_ripple_end:
+ * @self: The #GESTimelineElement to ripple.
+ * @end: The new end (start + duration) of @self in ripple mode. It will
+ * basically only change the duration of @self.
+ *
+ * Edits @self in ripple mode. It allows you to modify the
+ * duration of a @self and move the following neighbours accordingly.
+ * This will change the overall timeline duration.
+ *
+ * Returns: %TRUE if the self as been rippled properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_timeline_element_ripple_end (GESTimelineElement * self, GstClockTime end)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (klass->ripple_end) {
+ return klass->ripple_end (self, end);
+ }
+
+ GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
+ " on class %s. Can not ripple end to %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (end));
+
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_roll_start:
+ * @self: The #GESTimelineElement to roll
+ * @start: The new start of @self in roll mode, it will also adapat
+ * the in-point of @self according
+ *
+ * Edits @self in roll mode. It allows you to modify the
+ * start and inpoint of a @self and "resize" (basicly change the duration
+ * in this case) of the previous neighbours accordingly.
+ * This will not change the overall timeline duration.
+ *
+ * Returns: %TRUE if the self as been roll properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_timeline_element_roll_start (GESTimelineElement * self, GstClockTime start)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (klass->roll_start) {
+ return klass->roll_start (self, start);
+ }
+
+ GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
+ " on class %s. Can not roll to %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
+
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_roll_end:
+ * @self: The #GESTimelineElement to roll.
+ * @end: The new end (start + duration) of @self in roll mode
+ *
+ * Edits @self in roll mode. It allows you to modify the
+ * duration of a @self and trim (basicly change the start + inpoint
+ * in this case) the following neighbours accordingly.
+ * This will not change the overall timeline duration.
+ *
+ * Returns: %TRUE if the self as been rolled properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_timeline_element_roll_end (GESTimelineElement * self, GstClockTime end)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (klass->roll_end)
+ return klass->roll_end (self, end);
+
+ GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
+ " on class %s. Can not roll end to %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (end));
+
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_trim:
+ * @self: The #GESTimelineElement to trim.
+ * @start: The new start of @self in trim mode, will adapt the inpoint
+ * of @self accordingly
+ *
+ * Edits @self in trim mode. It allows you to modify the
+ * inpoint and start of @self.
+ * This will not change the overall timeline duration.
+ *
+ * Note that to trim the end of an self you can just set its duration. The same way
+ * as this method, it will take into account the snapping-distance property of the
+ * timeline in which @self is.
+ *
+ * Returns: %TRUE if the self as been trimmed properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_timeline_element_trim (GESTimelineElement * self, GstClockTime start)
+{
+ GESTimelineElementClass *klass;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (klass->trim)
+ return klass->trim (self, start);
+
+ GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
+ " on class %s. Can not trim to %" GST_TIME_FORMAT,
+ G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
+
+ return FALSE;
+}
+
+/**
+ * ges_timeline_element_copy:
+ * @self: The #GESTimelineElement to copy
+ * @deep: whether we want to create the elements @self contains or not
+ *
+ * Copies @self
+ *
+ * Returns: (transfer floating): The newly create #GESTimelineElement, copied from @self
+ */
+GESTimelineElement *
+ges_timeline_element_copy (GESTimelineElement * self, gboolean deep)
+{
+ GESAsset *asset;
+ GParameter *params;
+ GParamSpec **specs;
+ GESTimelineElementClass *klass;
+ guint n, n_specs, n_params;
+
+ GESTimelineElement *ret = NULL;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_specs);
+ params = g_new0 (GParameter, n_specs);
+ n_params = 0;
+
+ for (n = 0; n < n_specs; ++n) {
+ /* We do not want the timeline or the name to be copied */
+ if (g_strcmp0 (specs[n]->name, "parent") &&
+ g_strcmp0 (specs[n]->name, "timeline") &&
+ g_strcmp0 (specs[n]->name, "name") &&
+ (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
+ params[n_params].name = g_intern_string (specs[n]->name);
+ g_value_init (¶ms[n_params].value, specs[n]->value_type);
+ g_object_get_property (G_OBJECT (self), specs[n]->name,
+ ¶ms[n_params].value);
+ ++n_params;
+ }
+ }
+
+#if GLIB_CHECK_VERSION(2, 53, 1)
+ {
+ gint i;
+ GValue *values;
+ const gchar **names;
+ values = g_malloc0 (sizeof (GValue) * n_specs);
+ names = g_malloc0 (sizeof (gchar *) * n_specs);
+
+ for (i = 0; i < n_params; i++) {
+ values[i] = params[i].value;
+ names[i] = params[i].name;
+ }
+
+ ret =
+ GES_TIMELINE_ELEMENT (g_object_new_with_properties (G_OBJECT_TYPE
+ (self), n_params, names, values));
+ g_free (names);
+ g_free (values);
+ }
+#else
+ ret = g_object_newv (G_OBJECT_TYPE (self), n_params, params);
+#endif
+
+ while (n_params--)
+ g_value_unset (¶ms[n_params].value);
+
+ g_free (specs);
+ g_free (params);
+
+
+ asset = ges_extractable_get_asset (GES_EXTRACTABLE (self));
+ if (asset)
+ ges_extractable_set_asset (GES_EXTRACTABLE (ret), asset);
+ if (deep) {
+ if (klass->deep_copy)
+ klass->deep_copy (self, ret);
+ else
+ GST_WARNING_OBJECT (self, "No deep_copy virtual method implementation"
+ " on class %s. Can not finish the copy", G_OBJECT_CLASS_NAME (klass));
+ }
+
+ if (deep) {
+ ret->priv->copied_from = gst_object_ref (self);
+ }
+
+ return ret;
+}
+
+/**
+ * ges_timeline_element_get_toplevel_parent:
+ * @self: The #GESTimelineElement to get the toplevel parent from
+ *
+ * Gets the toplevel #GESTimelineElement controlling @self
+ *
+ * Returns: (transfer full): The toplevel controlling parent of @self
+ */
+GESTimelineElement *
+ges_timeline_element_get_toplevel_parent (GESTimelineElement * self)
+{
+ GESTimelineElement *toplevel = self;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
+
+ while (GES_TIMELINE_ELEMENT_PARENT (toplevel))
+ toplevel = GES_TIMELINE_ELEMENT_PARENT (toplevel);
+
+ return gst_object_ref (toplevel);
+}
+
+/**
+ * ges_timeline_element_get_name:
+ * @self: a #GESTimelineElement
+ *
+ * Returns a copy of the name of @self.
+ * Caller should g_free() the return value after usage.
+ *
+ * Returns: (transfer full): The name of @self
+ */
+gchar *
+ges_timeline_element_get_name (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
+
+ return g_strdup (self->name);
+}
+
+/**
+ * ges_timeline_element_set_name:
+ * @self: a #GESTimelineElement
+ * @name: (allow-none): The name @self should take (if avalaible<)
+ *
+ * Sets the name of object, or gives @self a guaranteed unique name (if name is NULL).
+ * This function makes a copy of the provided name, so the caller retains ownership
+ * of the name it sent.
+ */
+gboolean
+ges_timeline_element_set_name (GESTimelineElement * self, const gchar * name)
+{
+ gboolean result = TRUE, readd_to_timeline = FALSE;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ if (name != NULL && !g_strcmp0 (name, self->name)) {
+ GST_DEBUG_OBJECT (self, "Same name!");
+ return TRUE;
+ }
+
+ /* parented objects cannot be renamed */
+ if (self->timeline != NULL && name) {
+ GESTimelineElement *tmp = ges_timeline_get_element (self->timeline, name);
+
+ if (tmp) {
+ gst_object_unref (tmp);
+ goto had_timeline;
+ }
+
+ timeline_remove_element (self->timeline, self);
+ readd_to_timeline = TRUE;
+ }
+
+ _set_name (self, name);
+
+ if (readd_to_timeline)
+ timeline_add_element (self->timeline, self);
+
+ return result;
+
+ /* error */
+had_timeline:
+ {
+ GST_WARNING ("Object %s already in a timeline can't be renamed to %s",
+ self->name, name);
+ return FALSE;
+ }
+}
+
+static gboolean
+emit_deep_notify_in_idle (EmitDeepNotifyInIdleData * data)
+{
+ g_signal_emit (data->self, ges_timeline_element_signals[DEEP_NOTIFY], 0,
+ data->child, data->arg);
+
+ gst_object_unref (data->child);
+ g_param_spec_unref (data->arg);
+ gst_object_unref (data->self);
+ g_slice_free (EmitDeepNotifyInIdleData, data);
+
+ return FALSE;
+}
+
+static void
+child_prop_changed_cb (GObject * child, GParamSpec * arg
+ G_GNUC_UNUSED, GESTimelineElement * self)
+{
+ EmitDeepNotifyInIdleData *data;
+
+ /* Emit "deep-notify" right away if in main thread */
+ if (g_main_context_acquire (g_main_context_default ())) {
+ g_main_context_release (g_main_context_default ());
+ g_signal_emit (self, ges_timeline_element_signals[DEEP_NOTIFY], 0,
+ child, arg);
+ return;
+ }
+
+ data = g_slice_new (EmitDeepNotifyInIdleData);
+
+ data->child = gst_object_ref (child);
+ data->arg = g_param_spec_ref (arg);
+ data->self = gst_object_ref (self);
+
+ g_idle_add ((GSourceFunc) emit_deep_notify_in_idle, data);
+}
+
+gboolean
+ges_timeline_element_add_child_property (GESTimelineElement * self,
+ GParamSpec * pspec, GObject * child)
+{
+ gchar *signame;
+ ChildPropHandler *handler;
+
+ if (g_hash_table_contains (self->priv->children_props, pspec)) {
+ GST_INFO_OBJECT (self, "Child property already exists: %s", pspec->name);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (self, "Adding child property: %" GST_PTR_FORMAT "::%s",
+ child, pspec->name);
+
+ signame = g_strconcat ("notify::", pspec->name, NULL);
+ handler = (ChildPropHandler *) g_slice_new0 (ChildPropHandler);
+ handler->child = gst_object_ref (child);
+ handler->handler_id =
+ g_signal_connect (child, signame, G_CALLBACK (child_prop_changed_cb),
+ self);
+ g_hash_table_insert (self->priv->children_props, g_param_spec_ref (pspec),
+ handler);
+
+ g_free (signame);
+
+ return TRUE;
+}
+
+/**
+ * ges_timeline_element_get_child_property_by_pspec:
+ * @self: a #GESTrackElement
+ * @pspec: The #GParamSpec that specifies the property you want to get
+ * @value: (out): return location for the value
+ *
+ * Gets a property of a child of @self.
+ */
+void
+ges_timeline_element_get_child_property_by_pspec (GESTimelineElement * self,
+ GParamSpec * pspec, GValue * value)
+{
+ ChildPropHandler *handler;
+
+ g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
+
+ handler = g_hash_table_lookup (self->priv->children_props, pspec);
+ if (!handler)
+ goto not_found;
+
+ g_object_get_property (G_OBJECT (handler->child), pspec->name, value);
+
+ return;
+
+not_found:
+ {
+ GST_ERROR_OBJECT (self, "The %s property doesn't exist", pspec->name);
+ return;
+ }
+}
+
+/**
+ * ges_timeline_element_set_child_property_by_pspec:
+ * @self: a #GESTimelineElement
+ * @pspec: The #GParamSpec that specifies the property you want to set
+ * @value: the value
+ *
+ * Sets a property of a child of @self.
+ */
+void
+ges_timeline_element_set_child_property_by_pspec (GESTimelineElement * self,
+ GParamSpec * pspec, const GValue * value)
+{
+ ChildPropHandler *handler;
+ GESTimelineElementClass *klass;
+
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (self));
+
+ handler = g_hash_table_lookup (self->priv->children_props, pspec);
+
+ if (!handler)
+ goto not_found;
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+ g_assert (klass->set_child_property);
+ klass->set_child_property (self, handler->child, pspec, (GValue *) value);
+
+ return;
+
+not_found:
+ {
+ GST_ERROR ("The %s property doesn't exist", pspec->name);
+ return;
+ }
+}
+
+/**
+ * ges_timeline_element_set_child_property:
+ * @self: The origin #GESTimelineElement
+ * @property_name: The name of the property
+ * @value: the value
+ *
+ * Sets a property of a child of @self
+ *
+ * Note that #ges_timeline_element_set_child_property is really
+ * intended for language bindings, #ges_timeline_element_set_child_properties
+ * is much more convenient for C programming.
+ *
+ * Returns: %TRUE if the property was set, %FALSE otherwize
+ */
+gboolean
+ges_timeline_element_set_child_property (GESTimelineElement * self,
+ const gchar * property_name, const GValue * value)
+{
+ GParamSpec *pspec;
+ GESTimelineElementClass *klass;
+ GObject *child;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ if (!ges_timeline_element_lookup_child (self, property_name, &child, &pspec))
+ goto not_found;
+
+ klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+ g_assert (klass->set_child_property);
+ klass->set_child_property (self, child, pspec, (GValue *) value);
+
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+
+ return TRUE;
+
+not_found:
+ {
+ GST_WARNING_OBJECT (self, "The %s property doesn't exist", property_name);
+
+ return FALSE;
+ }
+}
+
+/**
+* ges_timeline_element_get_child_property:
+* @self: The origin #GESTimelineElement
+* @property_name: The name of the property
+* @value: (out): return location for the property value, it will
+* be initialized if it is initialized with 0
+*
+* In general, a copy is made of the property contents and
+* the caller is responsible for freeing the memory by calling
+* g_value_unset().
+*
+* Gets a property of a GstElement contained in @object.
+*
+* Note that #ges_timeline_element_get_child_property is really
+* intended for language bindings, #ges_timeline_element_get_child_properties
+* is much more convenient for C programming.
+*
+* Returns: %TRUE if the property was found, %FALSE otherwize
+*/
+gboolean
+ges_timeline_element_get_child_property (GESTimelineElement * self,
+ const gchar * property_name, GValue * value)
+{
+ GParamSpec *pspec;
+ GObject *child;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ if (!ges_timeline_element_lookup_child (self, property_name, &child, &pspec))
+ goto not_found;
+
+ if (G_VALUE_TYPE (value) == G_TYPE_INVALID)
+ g_value_init (value, pspec->value_type);
+
+ g_object_get_property (child, pspec->name, value);
+
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+
+ return TRUE;
+
+not_found:
+ {
+ GST_WARNING_OBJECT (self, "The %s property doesn't exist", property_name);
+
+ return FALSE;
+ }
+}
+
+/**
+ * ges_timeline_element_lookup_child:
+ * @self: object to lookup the property in
+ * @prop_name: name of the property to look up. You can specify the name of the
+ * class as such: "ClassName::property-name", to guarantee that you get the
+ * proper GParamSpec in case various GstElement-s contain the same property
+ * name. If you don't do so, you will get the first element found, having
+ * this property and the and the corresponding GParamSpec.
+ * @child: (out) (allow-none) (transfer full): pointer to a #GstElement that
+ * takes the real object to set property on
+ * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
+ * describing the property
+ *
+ * Looks up which @element and @pspec would be effected by the given @name. If various
+ * contained elements have this property name you will get the first one, unless you
+ * specify the class name in @name.
+ *
+ * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
+ * case the values for @pspec and @element are not modified. Unref @element after
+ * usage.
+ */
+gboolean
+ges_timeline_element_lookup_child (GESTimelineElement * self,
+ const gchar * prop_name, GObject ** child, GParamSpec ** pspec)
+{
+ GESTimelineElementClass *class;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+ class = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+ g_return_val_if_fail (class->lookup_child, FALSE);
+
+ return class->lookup_child (self, prop_name, child, pspec);
+}
+
+/**
+ * ges_timeline_element_set_child_property_valist:
+ * @self: The #GESTimelineElement parent object
+ * @first_property_name: The name of the first property to set
+ * @var_args: value for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Sets a property of a child of @self. If there are various child elements
+ * that have the same property name, you can distinguish them using the following
+ * syntax: 'ClasseName::property_name' as property name. If you don't, the
+ * corresponding property of the first element found will be set.
+ */
+void
+ges_timeline_element_set_child_property_valist (GESTimelineElement * self,
+ const gchar * first_property_name, va_list var_args)
+{
+ const gchar *name;
+ GParamSpec *pspec;
+ GObject *child;
+
+ gchar *error = NULL;
+ GValue value = { 0, };
+
+ g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
+
+ name = first_property_name;
+
+ /* Note: This part is in big part copied from the gst_child_object_set_valist
+ * method. */
+
+ /* iterate over pairs */
+ while (name) {
+ if (!ges_timeline_element_lookup_child (self, name, &child, &pspec))
+ goto not_found;
+
+ G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
+ G_VALUE_NOCOPY_CONTENTS, &error);
+
+ if (error)
+ goto cant_copy;
+
+ g_object_set_property (child, pspec->name, &value);
+
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+ g_value_unset (&value);
+
+ name = va_arg (var_args, gchar *);
+ }
+ return;
+
+not_found:
+ {
+ GST_WARNING_OBJECT (self, "No property %s in OBJECT\n", name);
+ return;
+ }
+cant_copy:
+ {
+ GST_WARNING_OBJECT (self, "error copying value %s in %p: %s", pspec->name,
+ self, error);
+
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+ g_value_unset (&value);
+ return;
+ }
+}
+
+/**
+ * ges_timeline_element_set_child_properties:
+ * @self: The #GESTimelineElement parent object
+ * @first_property_name: The name of the first property to set
+ * @...: value for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Sets a property of a child of @self. If there are various child elements
+ * that have the same property name, you can distinguish them using the following
+ * syntax: 'ClasseName::property_name' as property name. If you don't, the
+ * corresponding property of the first element found will be set.
+ */
+void
+ges_timeline_element_set_child_properties (GESTimelineElement * self,
+ const gchar * first_property_name, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
+
+ va_start (var_args, first_property_name);
+ ges_timeline_element_set_child_property_valist (self, first_property_name,
+ var_args);
+ va_end (var_args);
+}
+
+/**
+ * ges_timeline_element_get_child_property_valist:
+ * @self: The #GESTimelineElement parent object
+ * @first_property_name: The name of the first property to get
+ * @var_args: value for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Gets a property of a child of @self. If there are various child elements
+ * that have the same property name, you can distinguish them using the following
+ * syntax: 'ClasseName::property_name' as property name. If you don't, the
+ * corresponding property of the first element found will be set.
+ */
+void
+ges_timeline_element_get_child_property_valist (GESTimelineElement * self,
+ const gchar * first_property_name, va_list var_args)
+{
+ const gchar *name;
+ gchar *error = NULL;
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ GObject *child;
+
+ g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
+
+ name = first_property_name;
+
+ /* This part is in big part copied from the gst_child_object_get_valist method */
+ while (name) {
+ if (!ges_timeline_element_lookup_child (self, name, &child, &pspec))
+ goto not_found;
+
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (child, pspec->name, &value);
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+
+ G_VALUE_LCOPY (&value, var_args, 0, &error);
+ if (error)
+ goto cant_copy;
+ g_value_unset (&value);
+ name = va_arg (var_args, gchar *);
+ }
+ return;
+
+not_found:
+ {
+ GST_WARNING_OBJECT (self, "no child property %s", name);
+ return;
+ }
+cant_copy:
+ {
+ GST_WARNING_OBJECT (self, "error copying value %s in %s", pspec->name,
+ error);
+
+ g_value_unset (&value);
+ return;
+ }
+}
+
+static gint
+compare_gparamspec (GParamSpec ** a, GParamSpec ** b, gpointer udata)
+{
+ return g_strcmp0 ((*a)->name, (*b)->name);
+}
+
+
+/**
+ * ges_timeline_element_list_children_properties:
+ * @self: The #GESTimelineElement to get the list of children properties from
+ * @n_properties: (out): return location for the length of the returned array
+ *
+ * Gets an array of #GParamSpec* for all configurable properties of the
+ * children of @self.
+ *
+ * Returns: (transfer full) (array length=n_properties): an array of #GParamSpec* which should be freed after use or
+ * %NULL if something went wrong
+ */
+GParamSpec **
+ges_timeline_element_list_children_properties (GESTimelineElement * self,
+ guint * n_properties)
+{
+ GParamSpec **ret;
+ GESTimelineElementClass *class;
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
+
+ class = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+
+ if (!class->list_children_properties) {
+ GST_INFO_OBJECT (self, "No %s->list_children_properties implementation",
+ G_OBJECT_TYPE_NAME (self));
+
+ *n_properties = 0;
+ return NULL;
+ }
+
+ ret = class->list_children_properties (self, n_properties);
+ g_qsort_with_data (ret, *n_properties, sizeof (GParamSpec *),
+ (GCompareDataFunc) compare_gparamspec, NULL);
+
+ return ret;
+}
+
+/**
+ * ges_timeline_element_get_child_properties:
+ * @self: The origin #GESTimelineElement
+ * @first_property_name: The name of the first property to get
+ * @...: return location for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Gets properties of a child of @self.
+ */
+void
+ges_timeline_element_get_child_properties (GESTimelineElement * self,
+ const gchar * first_property_name, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
+
+ va_start (var_args, first_property_name);
+ ges_timeline_element_get_child_property_valist (self, first_property_name,
+ var_args);
+ va_end (var_args);
+}
+
+gboolean
+ges_timeline_element_remove_child_property (GESTimelineElement * self,
+ GParamSpec * pspec)
+{
+ return g_hash_table_remove (self->priv->children_props, pspec);
+}
+
+/**
+ * ges_timeline_element_get_track_types:
+ * @self: A #GESTimelineElement
+ *
+ * Gets all the TrackTypes @self will interact with
+ *
+ * Since: 1.6.0
+ */
+GESTrackType
+ges_timeline_element_get_track_types (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), 0);
+ g_return_val_if_fail (GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types,
+ 0);
+
+ return GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types (self);
+}
+
+/**
+ * ges_timeline_element_paste:
+ * @self: The #GESTimelineElement to paste
+ * @paste_position: The position in the timeline the element should
+ * be copied to, meaning it will become the start of @self
+ *
+ * Paste @self inside the timeline. @self must have been created
+ * using ges_timeline_element_copy with recurse=TRUE set,
+ * otherwise it will fail.
+ *
+ * Returns: (transfer full): New element resulting of pasting @self
+ * or %NULL
+ *
+ * Since: 1.6.0
+ */
+GESTimelineElement *
+ges_timeline_element_paste (GESTimelineElement * self,
+ GstClockTime paste_position)
+{
+ GESTimelineElement *res;
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ if (!self->priv->copied_from) {
+ GST_ERROR_OBJECT (self, "Is not being 'deeply' copied!");
+
+ return NULL;
+ }
+
+ if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste) {
+ GST_ERROR_OBJECT (self, "No paste vmethod implemented");
+
+ return NULL;
+ }
+
+ res = GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste (self,
+ self->priv->copied_from, paste_position);
+
+ g_clear_object (&self->priv->copied_from);
+
+ return res ? g_object_ref (res) : res;
+}
+
+/**
+ * ges_timeline_element_get_layer_priority:
+ * @self: A #GESTimelineElement
+ *
+ * Returns: The priority of the first layer the element is in (note that only
+ * groups can span over several layers). %GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY
+ * means that the element is not in a layer.
+ *
+ * Since: 1.16
+ */
+guint32
+ges_timeline_element_get_layer_priority (GESTimelineElement * self)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self),
+ GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY);
+
+ if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_layer_priority)
+ return self->priority;
+
+ return GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_layer_priority (self);
+}
+
+/* Internal */
+gdouble
+ges_timeline_element_get_media_duration_factor (GESTimelineElement * self)
+{
+ gdouble media_duration_factor;
+ GESEffectClass *class;
+ GList *props;
+
+ media_duration_factor = 1.0;
+
+ class = GES_EFFECT_CLASS (g_type_class_ref (GES_TYPE_EFFECT));
+
+ for (props = class->rate_properties; props != NULL; props = props->next) {
+ GObject *child;
+ GParamSpec *pspec;
+ if (ges_timeline_element_lookup_child (self, props->data, &child, &pspec)) {
+ if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_FLOAT) {
+ gfloat rate_change;
+ g_object_get (child, pspec->name, &rate_change, NULL);
+ media_duration_factor *= rate_change;
+ } else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_DOUBLE) {
+ gdouble rate_change;
+ g_object_get (child, pspec->name, &rate_change, NULL);
+ media_duration_factor *= rate_change;
+ } else {
+ GST_WARNING_OBJECT (self,
+ "Rate property %s in child %" GST_PTR_FORMAT
+ " is of unsupported type %s", pspec->name, child,
+ G_VALUE_TYPE_NAME (pspec->value_type));
+ }
+
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+
+ GST_DEBUG_OBJECT (self,
+ "Added rate changing property %s, set to value %lf",
+ (const char *) props->data, media_duration_factor);
+ }
+ }
+
+ g_type_class_unref (class);
+ return media_duration_factor;
+}
+
+/* Internal */
+GESTimelineElement *
+ges_timeline_element_get_copied_from (GESTimelineElement * self)
+{
+ GESTimelineElement *copied_from = self->priv->copied_from;
+ self->priv->copied_from = NULL;
+ return copied_from;
+}
+
+GESTimelineElementFlags
+ges_timeline_element_flags (GESTimelineElement * self)
+{
+ return self->priv->flags;
+}
+
+void
+ges_timeline_element_set_flags (GESTimelineElement * self,
+ GESTimelineElementFlags flags)
+{
+ self->priv->flags = flags;
+
+}
--- /dev/null
+/*
+ * gst-editing-services
+ * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
+ * <2013> Collabora Ltd.
+ *
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GES_TIMELINE_ELEMENT_H_
+#define _GES_TIMELINE_ELEMENT_H_
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include "ges-enums.h"
+#include "ges-types.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TIMELINE_ELEMENT (ges_timeline_element_get_type ())
+#define GES_TIMELINE_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TIMELINE_ELEMENT, GESTimelineElement))
+#define GES_TIMELINE_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TIMELINE_ELEMENT, GESTimelineElementClass))
+#define GES_IS_TIMELINE_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TIMELINE_ELEMENT))
+#define GES_IS_TIMELINE_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TIMELINE_ELEMENT))
+#define GES_TIMELINE_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TIMELINE_ELEMENT, GESTimelineElementClass))
+
+typedef struct _GESTimelineElementPrivate GESTimelineElementPrivate;
+
+/**
+ * GES_TIMELINE_ELEMENT_START:
+ * @obj: a #GESTimelineElement
+ *
+ * The start position of the object (in nanoseconds).
+ */
+#define GES_TIMELINE_ELEMENT_START(obj) (((GESTimelineElement*)obj)->start)
+
+/**
+ * GES_TIMELINE_ELEMENT_END:
+ * @obj: a #GESTimelineElement
+ *
+ * The end position of the object (in nanoseconds).
+ */
+#define GES_TIMELINE_ELEMENT_END(obj) ((((GESTimelineElement*)obj)->start) + (((GESTimelineElement*)obj)->duration))
+
+/**
+ * GES_TIMELINE_ELEMENT_INPOINT:
+ * @obj: a #GESTimelineElement
+ *
+ * The in-point of the object (in nanoseconds).
+ */
+#define GES_TIMELINE_ELEMENT_INPOINT(obj) (((GESTimelineElement*)obj)->inpoint)
+
+/**
+ * GES_TIMELINE_ELEMENT_DURATION:
+ * @obj: a #GESTimelineElement
+ *
+ * The duration of the object (in nanoseconds).
+ */
+#define GES_TIMELINE_ELEMENT_DURATION(obj) (((GESTimelineElement*)obj)->duration)
+
+/**
+ * GES_TIMELINE_ELEMENT_MAX_DURATION:
+ * @obj: a #GESTimelineElement
+ *
+ * The maximun duration of the object (in nanoseconds).
+ */
+#define GES_TIMELINE_ELEMENT_MAX_DURATION(obj) (((GESTimelineElement*)obj)->maxduration)
+
+/**
+ * GES_TIMELINE_ELEMENT_PRIORITY:
+ * @obj: a #GESTimelineElement
+ *
+ * The priority of the object.
+ */
+#define GES_TIMELINE_ELEMENT_PRIORITY(obj) (((GESTimelineElement*)obj)->priority)
+
+/**
+ * GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY:
+ *
+ * Layer priority when the element is not in a layer
+ */
+#define GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY ((guint32) -1)
+
+/**
+ * GES_TIMELINE_ELEMENT_LAYER_PRIORITY:
+ * @obj: The object to retrieve the layer priority from
+ *
+ * See #ges_timeline_element_get_layer_priority
+ */
+#define GES_TIMELINE_ELEMENT_LAYER_PRIORITY(obj) (ges_timeline_element_get_layer_priority(((GESTimelineElement*)obj)))
+
+/**
+ * GES_TIMELINE_ELEMENT_PARENT:
+ * @obj: a #GESTimelineElement
+ *
+ * The parent of the object.
+ */
+#define GES_TIMELINE_ELEMENT_PARENT(obj) (((GESTimelineElement*)obj)->parent)
+
+/**
+ * GES_TIMELINE_ELEMENT_TIMELINE:
+ * @obj: a #GESTimelineElement
+ *
+ * The timeline in which the object is.
+ */
+#define GES_TIMELINE_ELEMENT_TIMELINE(obj) (((GESTimelineElement*)obj)->timeline)
+
+/**
+ * GES_TIMELINE_ELEMENT_NAME:
+ * @obj: a #GESTimelineElement
+ *
+ * The name of the object.
+ */
+#define GES_TIMELINE_ELEMENT_NAME(obj) (((GESTimelineElement*)obj)->name)
+
+/**
+ * GESTimelineElement:
+ * @parent: The #GESTimelineElement that controls the object
+ * @asset: The #GESAsset from which the object has been extracted
+ * @start: position (in time) of the object
+ * @inpoint: Position in the media from which the object should be used
+ * @duration: duration of the object to be used
+ * @maxduration: The maximum duration the object can have
+ * @priority: priority of the object in the layer (0:top priority)
+ *
+ * Those filed can be accessed from outside but in no case should
+ * be changed from there. Subclasses can write them but should make
+ * sure to properly call g_object_notify.
+ */
+struct _GESTimelineElement
+{
+ GInitiallyUnowned parent_instance;
+
+ /*< public > */
+ /*< read only >*/
+ GESTimelineElement *parent;
+ GESAsset *asset;
+ GstClockTime start;
+ GstClockTime inpoint;
+ GstClockTime duration;
+ GstClockTime maxduration;
+ guint32 priority;
+ GESTimeline *timeline;
+ gchar *name;
+
+ /*< private >*/
+ GESTimelineElementPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+/**
+ * GESTimelineElementClass:
+ * @set_parent: method to set the parent of a #GESTimelineElement.
+ * @set_start: method to set the start of a #GESTimelineElement
+ * @set_duration: method to set the duration of a #GESTimelineElement
+ * @set_inpoint: method to set the inpoint of a #GESTimelineElement
+ * @set_max_duration: method to set the maximun duration of a #GESTimelineElement
+ * @set_priority: method to set the priority of a #GESTimelineElement
+ * @ripple: method to ripple an object
+ * @ripple_end: method to ripple an object on its #GES_EDGE_END edge
+ * @roll_start: method to roll an object on its #GES_EDGE_START edge
+ * @roll_end: method to roll an object on its #GES_EDGE_END edge
+ * @trim: method to trim an object
+ * @deep_copy: Copy the children properties of @self into @copy
+ *
+ * The GESTimelineElement base class. Subclasses should override at least
+ * @set_start @set_inpoint @set_duration @ripple @ripple_end @roll_start
+ * @roll_end and @trim.
+ *
+ * Vmethods in subclasses should apply all the operation they need to but
+ * the real method implementation is in charge of setting the proper field,
+ * and emit the notify signal.
+ */
+struct _GESTimelineElementClass
+{
+ GInitiallyUnownedClass parent_class;
+
+ /*< public > */
+ gboolean (*set_parent) (GESTimelineElement * self, GESTimelineElement *parent);
+ gboolean (*set_start) (GESTimelineElement * self, GstClockTime start);
+ gboolean (*set_inpoint) (GESTimelineElement * self, GstClockTime inpoint);
+ gboolean (*set_duration) (GESTimelineElement * self, GstClockTime duration);
+ gboolean (*set_max_duration) (GESTimelineElement * self, GstClockTime maxduration);
+ gboolean (*set_priority) (GESTimelineElement * self, guint32 priority); /* set_priority is now protected */
+
+ gboolean (*ripple) (GESTimelineElement *self, guint64 start);
+ gboolean (*ripple_end) (GESTimelineElement *self, guint64 end);
+ gboolean (*roll_start) (GESTimelineElement *self, guint64 start);
+ gboolean (*roll_end) (GESTimelineElement *self, guint64 end);
+ gboolean (*trim) (GESTimelineElement *self, guint64 start);
+ void (*deep_copy) (GESTimelineElement *self, GESTimelineElement *copy);
+
+ GESTimelineElement * (*paste) (GESTimelineElement *self,
+ GESTimelineElement *ref_element,
+ GstClockTime paste_position);
+
+ GParamSpec** (*list_children_properties) (GESTimelineElement * self, guint *n_properties);
+ gboolean (*lookup_child) (GESTimelineElement *self, const gchar *prop_name,
+ GObject **child, GParamSpec **pspec);
+ GESTrackType (*get_track_types) (GESTimelineElement * self);
+ void (*set_child_property) (GESTimelineElement * self, GObject *child,
+ GParamSpec *pspec, GValue *value);
+
+ guint32 (*get_layer_priority) (GESTimelineElement *self);
+
+ /*< private > */
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE - 4];
+};
+
+GES_API
+GType ges_timeline_element_get_type (void) G_GNUC_CONST;
+
+GES_API GESTimelineElement *
+ges_timeline_element_get_toplevel_parent (GESTimelineElement *self);
+GES_API
+GESTimelineElement * ges_timeline_element_get_parent (GESTimelineElement * self);
+GES_API
+gboolean ges_timeline_element_set_parent (GESTimelineElement *self, GESTimelineElement *parent);
+GES_API
+gboolean ges_timeline_element_set_timeline (GESTimelineElement *self, GESTimeline *timeline);
+GES_API
+gboolean ges_timeline_element_set_start (GESTimelineElement *self, GstClockTime start);
+GES_API
+gboolean ges_timeline_element_set_inpoint (GESTimelineElement *self, GstClockTime inpoint);
+GES_API
+gboolean ges_timeline_element_set_duration (GESTimelineElement *self, GstClockTime duration);
+GES_API
+gboolean ges_timeline_element_set_max_duration (GESTimelineElement *self, GstClockTime maxduration);
+GES_API
+gboolean ges_timeline_element_set_priority (GESTimelineElement *self, guint32 priority);
+
+GES_API
+GstClockTime ges_timeline_element_get_start (GESTimelineElement *self);
+GES_API
+GstClockTime ges_timeline_element_get_inpoint (GESTimelineElement *self);
+GES_API
+GstClockTime ges_timeline_element_get_duration (GESTimelineElement *self);
+GES_API
+GstClockTime ges_timeline_element_get_max_duration (GESTimelineElement *self);
+GES_API
+GESTimeline * ges_timeline_element_get_timeline (GESTimelineElement *self);
+GES_API
+guint32 ges_timeline_element_get_priority (GESTimelineElement *self);
+
+GES_API
+gboolean ges_timeline_element_ripple (GESTimelineElement *self, GstClockTime start);
+GES_API
+gboolean ges_timeline_element_ripple_end (GESTimelineElement *self, GstClockTime end);
+GES_API
+gboolean ges_timeline_element_roll_start (GESTimelineElement *self, GstClockTime start);
+GES_API
+gboolean ges_timeline_element_roll_end (GESTimelineElement *self, GstClockTime end);
+GES_API
+gboolean ges_timeline_element_trim (GESTimelineElement *self, GstClockTime start);
+GES_API
+GESTimelineElement * ges_timeline_element_copy (GESTimelineElement *self, gboolean deep);
+GES_API
+gchar * ges_timeline_element_get_name (GESTimelineElement *self);
+GES_API
+gboolean ges_timeline_element_set_name (GESTimelineElement *self, const gchar *name);
+GES_API GParamSpec **
+ges_timeline_element_list_children_properties (GESTimelineElement *self,
+ guint *n_properties);
+
+GES_API
+gboolean ges_timeline_element_lookup_child (GESTimelineElement *self,
+ const gchar *prop_name,
+ GObject **child,
+ GParamSpec **pspec);
+
+GES_API void
+ges_timeline_element_get_child_property_by_pspec (GESTimelineElement * self,
+ GParamSpec * pspec,
+ GValue * value);
+
+GES_API void
+ges_timeline_element_get_child_property_valist (GESTimelineElement * self,
+ const gchar * first_property_name,
+ va_list var_args);
+
+GES_API void
+ges_timeline_element_get_child_properties (GESTimelineElement *self,
+ const gchar * first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GES_API void
+ges_timeline_element_set_child_property_valist (GESTimelineElement * self,
+ const gchar * first_property_name,
+ va_list var_args);
+
+GES_API void
+ges_timeline_element_set_child_property_by_pspec (GESTimelineElement * self,
+ GParamSpec * pspec,
+ const GValue * value);
+GES_API
+void ges_timeline_element_set_child_properties (GESTimelineElement * self,
+ const gchar * first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GES_API
+gboolean ges_timeline_element_set_child_property (GESTimelineElement *self,
+ const gchar *property_name,
+ const GValue * value);
+
+GES_API
+gboolean ges_timeline_element_get_child_property (GESTimelineElement *self,
+ const gchar *property_name,
+ GValue * value);
+
+GES_API
+gboolean ges_timeline_element_add_child_property (GESTimelineElement * self,
+ GParamSpec *pspec,
+ GObject *child);
+
+GES_API
+gboolean ges_timeline_element_remove_child_property(GESTimelineElement * self,
+ GParamSpec *pspec);
+
+GES_API
+GESTimelineElement * ges_timeline_element_paste (GESTimelineElement * self,
+ GstClockTime paste_position);
+
+GES_API
+GESTrackType ges_timeline_element_get_track_types (GESTimelineElement * self);
+
+GES_API
+guint32 ges_timeline_element_get_layer_priority (GESTimelineElement * self);
+
+G_END_DECLS
+
+#endif /* _GES_TIMELINE_ELEMENT_H_ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2019 Igalia S.L
+ * Author: 2019 Thibault Saunier <tsaunier@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 "ges-timeline-tree.h"
+#include "ges-internal.h"
+
+GST_DEBUG_CATEGORY_STATIC (tree_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT tree_debug
+
+#define ELEMENT_EDGE_VALUE(e, edge) ((edge == GES_EDGE_END) ? ((GstClockTimeDiff) _END (e)) : ((GstClockTimeDiff) _START (e)))
+typedef struct
+{
+ GstClockTime distance;
+ gboolean on_end_only;
+ gboolean on_start_only;
+
+ GESEdge edge;
+ GESTimelineElement *element;
+
+ GESTimelineElement *moving_element;
+ GESEdge moving_edge;
+ GstClockTimeDiff diff;
+} SnappingData;
+
+/* *INDENT-OFF* */
+struct _TreeIterationData
+{
+ GNode *root;
+ gboolean res;
+
+ GstClockTimeDiff start_diff;
+ GstClockTimeDiff inpoint_diff;
+ GstClockTimeDiff duration_diff;
+ gint64 priority_diff;
+
+ /* The element we are visiting */
+ GESTimelineElement *element;
+
+ /* All the TrackElement currently moving */
+ GList *movings;
+
+ /* Elements overlaping on the start/end of @element */
+ GESTimelineElement *overlaping_on_start;
+ GESTimelineElement *overlaping_on_end;
+
+ /* Timestamp after which elements will be rippled */
+ GstClockTime ripple_time;
+
+ SnappingData *snapping;
+
+ /* The edge being trimmed or rippled */
+ GESEdge edge;
+ GHashTable *moved_clips;
+
+ GList *neighbours;
+} tree_iteration_data_init = {
+ .root = NULL,
+ .res = TRUE,
+ .start_diff = 0,
+ .inpoint_diff = 0,
+ .duration_diff = 0,
+ .priority_diff = 0,
+ .element = NULL,
+ .movings = NULL,
+ .overlaping_on_start = NULL,
+ .overlaping_on_end = NULL,
+ .ripple_time = GST_CLOCK_TIME_NONE,
+ .snapping = NULL,
+ .edge = GES_EDGE_NONE,
+ .moved_clips = NULL,
+ .neighbours = NULL,
+};
+/* *INDENT-ON* */
+
+typedef struct _TreeIterationData TreeIterationData;
+
+static void
+clean_iteration_data (TreeIterationData * data)
+{
+ g_list_free (data->neighbours);
+ g_list_free (data->movings);
+ if (data->moved_clips)
+ g_hash_table_unref (data->moved_clips);
+}
+
+void
+timeline_tree_init_debug (void)
+{
+ GST_DEBUG_CATEGORY_INIT (tree_debug, "gestree",
+ GST_DEBUG_FG_YELLOW, "timeline tree");
+}
+
+
+static gboolean
+print_node (GNode * node, gpointer unused_data)
+{
+ if (G_NODE_IS_ROOT (node)) {
+ g_print ("Timeline: %p\n", node->data);
+ return FALSE;
+ }
+
+ g_print ("%*c- %" GES_FORMAT " - layer %" G_GINT32_FORMAT "\n",
+ 2 * g_node_depth (node), ' ', GES_ARGS (node->data),
+ ges_timeline_element_get_layer_priority (node->data));
+
+ return FALSE;
+}
+
+void
+timeline_tree_debug (GNode * root)
+{
+ g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ (GNodeTraverseFunc) print_node, NULL);
+}
+
+static inline GESTimelineElement *
+get_toplevel_container (gpointer element)
+{
+ GESTimelineElement *ret =
+ ges_timeline_element_get_toplevel_parent ((GESTimelineElement
+ *) (element));
+
+ /* We own a ref to the elements ourself */
+ gst_object_unref (ret);
+ return ret;
+}
+
+static gboolean
+timeline_tree_can_move_element_internal (GNode * root,
+ GESTimelineElement * element,
+ gint64 priority,
+ GstClockTimeDiff start,
+ GstClockTimeDiff inpoint,
+ GstClockTimeDiff duration,
+ GList * moving_track_elements,
+ GstClockTime ripple_time, SnappingData * snapping, GESEdge edge);
+
+static GNode *
+find_node (GNode * root, gpointer element)
+{
+ return g_node_find (root, G_IN_ORDER, G_TRAVERSE_ALL, element);
+}
+
+static void
+timeline_element_parent_cb (GESTimelineElement * child, GParamSpec * arg
+ G_GNUC_UNUSED, GNode * root)
+{
+ GNode *new_parent_node = NULL, *node = find_node (root, child);
+
+ if (child->parent)
+ new_parent_node = find_node (root, child->parent);
+
+ if (!new_parent_node)
+ new_parent_node = root;
+
+ g_node_unlink (node);
+ g_node_prepend (new_parent_node, node);
+}
+
+void
+timeline_tree_track_element (GNode * root, GESTimelineElement * element)
+{
+ GNode *node;
+ GNode *parent;
+ GESTimelineElement *toplevel;
+
+ if (find_node (root, element)) {
+ return;
+ }
+
+ g_signal_connect (element, "notify::parent",
+ G_CALLBACK (timeline_element_parent_cb), root);
+
+ toplevel = get_toplevel_container (element);
+ if (toplevel == element) {
+ GST_DEBUG ("Tracking toplevel element %" GES_FORMAT, GES_ARGS (element));
+
+ node = g_node_prepend_data (root, element);
+ } else {
+ parent = find_node (root, element->parent);
+ GST_LOG ("%" GES_FORMAT "parent is %" GES_FORMAT, GES_ARGS (element),
+ GES_ARGS (element->parent));
+ g_assert (parent);
+ node = g_node_prepend_data (parent, element);
+ }
+
+ if (GES_IS_CONTAINER (element)) {
+ GList *tmp;
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ GNode *child_node = find_node (root, tmp->data);
+
+ if (child_node) {
+ g_node_unlink (child_node);
+ g_node_prepend (node, child_node);
+ } else {
+ timeline_tree_track_element (root, tmp->data);
+ }
+ }
+ }
+
+ timeline_update_duration (root->data);
+}
+
+void
+timeline_tree_stop_tracking_element (GNode * root, GESTimelineElement * element)
+{
+ GNode *node = find_node (root, element);
+
+ node = find_node (root, element);
+
+ /* Move children to the parent */
+ while (node->children) {
+ GNode *tmp = node->children;
+ g_node_unlink (tmp);
+ g_node_prepend (node->parent, tmp);
+ }
+
+ g_assert (node);
+ GST_DEBUG ("Stop tracking %" GES_FORMAT, GES_ARGS (element));
+ g_signal_handlers_disconnect_by_func (element, timeline_element_parent_cb,
+ root);
+
+ g_node_destroy (node);
+ timeline_update_duration (root->data);
+}
+
+static inline gboolean
+check_can_move_to_layer (GESTimelineElement * element,
+ gint layer_priority_offset)
+{
+ return (((gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element) -
+ layer_priority_offset) >= 0);
+}
+
+/* *INDENT-OFF* */
+#define CHECK_AND_SNAP(diff_val,moving_edge_,edge_) \
+if (snapping->distance >= ABS(diff_val) && ABS(diff_val) <= ABS(snapping->diff)) { \
+ snapping->element = element; \
+ snapping->edge = edge_; \
+ snapping->moving_element = moving_elem; \
+ snapping->moving_edge = moving_edge_; \
+ snapping->diff = (diff_val); \
+ GST_LOG("Snapping %" GES_FORMAT "with %" GES_FORMAT " - diff: %" G_GINT64_FORMAT "", GES_ARGS (moving_elem), GES_ARGS(element), (diff_val)); \
+}
+
+static void
+check_snapping (GESTimelineElement * element, GESTimelineElement * moving_elem,
+ SnappingData * snapping, GstClockTime start, GstClockTime end,
+ GstClockTime moving_start, GstClockTime moving_end)
+{
+ GstClockTimeDiff snap_end_end_diff;
+ GstClockTimeDiff snap_end_start_diff;
+
+ if (element == moving_elem)
+ return;
+
+ if (!snapping || (
+ GES_IS_CLIP (element->parent) && element->parent == moving_elem->parent))
+ return;
+
+ snap_end_end_diff = (GstClockTimeDiff) moving_end - (GstClockTimeDiff) end;
+ snap_end_start_diff = (GstClockTimeDiff) moving_end - (GstClockTimeDiff) start;
+
+ GST_DEBUG("Moving [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT "] element [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT "]", moving_start, moving_end, start, end);
+ /* Prefer snapping end */
+ if (!snapping->on_start_only) {
+ CHECK_AND_SNAP(snap_end_end_diff, GES_EDGE_END, GES_EDGE_END)
+ else CHECK_AND_SNAP(snap_end_start_diff, GES_EDGE_END, GES_EDGE_START)
+ }
+
+ if (!snapping->on_end_only) {
+ GstClockTimeDiff snap_start_end_diff = GST_CLOCK_DIFF(end, moving_start);
+ GstClockTimeDiff snap_start_start_diff = GST_CLOCK_DIFF(start, moving_start);
+
+ CHECK_AND_SNAP(snap_start_end_diff, GES_EDGE_START, GES_EDGE_END)
+ else CHECK_AND_SNAP(snap_start_start_diff, GES_EDGE_START, GES_EDGE_START)
+ }
+}
+#undef CHECK_AND_SNAP
+/* *INDENT-ON* */
+
+static gboolean
+check_track_elements_overlaps_and_values (GNode * node,
+ TreeIterationData * data)
+{
+ GESTimelineElement *e = (GESTimelineElement *) node->data;
+ GstClockTimeDiff moving_start, moving_end, start, inpoint, end, duration;
+ gint64 priority = ((gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (e));
+ gint64 moving_priority =
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (data->element) -
+ data->priority_diff;
+
+ gboolean can_overlap = e != data->element, in_movings, rippling, moving;
+
+ if (!GES_IS_SOURCE (e))
+ return FALSE;
+
+ in_movings = ! !g_list_find (data->movings, e);
+ rippling = e != data->element && !in_movings
+ && (GST_CLOCK_TIME_IS_VALID (data->ripple_time)
+ && e->start >= data->ripple_time);
+ moving = in_movings || rippling || e == data->element;
+
+ start = (GstClockTimeDiff) e->start;
+ inpoint = (GstClockTimeDiff) e->inpoint;
+ end = (GstClockTimeDiff) e->start + e->duration;
+ duration = e->duration;
+ moving_start = GST_CLOCK_DIFF (data->start_diff, data->element->start);
+ moving_end =
+ GST_CLOCK_DIFF (data->duration_diff,
+ moving_start + data->element->duration);
+
+ if (moving) {
+ if (rippling) {
+ if (data->edge == GES_EDGE_END) {
+ /* Moving as rippled from the end of a previous element */
+ start -= data->duration_diff;
+ } else
+ start -= data->start_diff;
+ } else {
+ start -= data->start_diff;
+ if (GES_TIMELINE_ELEMENT_GET_CLASS (e)->set_inpoint)
+ inpoint -= data->inpoint_diff;
+ duration -= data->duration_diff;
+ }
+ end = start + duration;
+ priority -= data->priority_diff;
+
+ GST_DEBUG ("%s %" GES_FORMAT "to [%" G_GINT64_FORMAT "(%"
+ G_GINT64_FORMAT ") - %" G_GINT64_FORMAT "] - layer: %" G_GINT64_FORMAT,
+ rippling ? "Rippling" : "Moving", GES_ARGS (e), start, inpoint,
+ duration, priority);
+ }
+
+ /* Not in the same track */
+ if (ges_track_element_get_track ((GESTrackElement *) node->data) !=
+ ges_track_element_get_track ((GESTrackElement *) data->element)) {
+ GST_LOG ("%" GES_FORMAT " and %" GES_FORMAT
+ " are not in the same track", GES_ARGS (node->data),
+ GES_ARGS (data->element));
+ can_overlap = FALSE;
+ }
+
+ /* Not in the same layer */
+ if (priority != moving_priority) {
+ GST_LOG ("%" GST_PTR_FORMAT " and %" GST_PTR_FORMAT
+ " are not on the same layer (%d != %" G_GINT64_FORMAT ")", node->data,
+ data->element, GES_TIMELINE_ELEMENT_LAYER_PRIORITY (e),
+ moving_priority);
+ can_overlap = FALSE;
+ }
+
+ if (start < 0) {
+ GST_INFO ("%" GES_FORMAT "start would be %" G_GINT64_FORMAT " < 0",
+ GES_ARGS (e), start);
+ goto error;
+ }
+
+ if (duration < 0) {
+ GST_INFO ("%" GES_FORMAT "duration would be %" G_GINT64_FORMAT " < 0",
+ GES_ARGS (e), duration);
+ goto error;
+ }
+
+ if (priority < 0) {
+ GST_INFO ("%" GES_FORMAT "priority would be %" G_GINT64_FORMAT " < 0",
+ GES_ARGS (e), priority);
+ goto error;
+ }
+
+ if (inpoint < 0) {
+ GST_INFO ("%" GES_FORMAT " can't set inpoint %" G_GINT64_FORMAT,
+ GES_ARGS (e), inpoint);
+ goto error;
+ }
+
+ if (inpoint + duration > e->maxduration) {
+ GST_INFO ("%" GES_FORMAT " inpoint + duration %" G_GINT64_FORMAT
+ " > max_duration %" G_GINT64_FORMAT,
+ GES_ARGS (e), inpoint + duration, e->maxduration);
+ goto error;
+ }
+
+ if (!moving)
+ check_snapping (e, data->element, data->snapping, start, end, moving_start,
+ moving_end);
+
+ if (!can_overlap)
+ return FALSE;
+
+ if (start > moving_end || moving_start > end) {
+ /* They do not overlap at all */
+ GST_LOG ("%" GES_FORMAT " and %" GES_FORMAT
+ " do not overlap at all.",
+ GES_ARGS (node->data), GES_ARGS (data->element));
+ return FALSE;
+ }
+
+ if ((moving_start <= start && moving_end >= end) ||
+ (moving_start >= start && moving_end <= end)) {
+ GST_INFO ("Fully overlaped: %s<%p> [%" G_GINT64_FORMAT " - %"
+ G_GINT64_FORMAT "] and %s<%p> [%" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
+ " (%" G_GINT64_FORMAT ")]", e->name, e, start, end, data->element->name,
+ data->element, moving_start, moving_end, data->duration_diff);
+
+ goto error;
+ }
+
+ if (moving_start < end && moving_start > start) {
+ GST_LOG ("Overlap start: %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT
+ "] and %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT " (%"
+ G_GINT64_FORMAT ")]", e->name, e, start, end, data->element->name,
+ data->element, moving_start, moving_end, data->duration_diff);
+ if (data->overlaping_on_start) {
+ GST_INFO ("Clip is overlapped by %s and %s at its start",
+ data->overlaping_on_start->name, e->name);
+ goto error;
+ }
+
+ data->overlaping_on_start = node->data;
+ } else if (moving_end > end && end > moving_start) {
+ GST_LOG ("Overlap end: %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT
+ "] and %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT " (%"
+ G_GINT64_FORMAT ")]", e->name, e, start, end, data->element->name,
+ data->element, moving_start, moving_end, data->duration_diff);
+
+ if (data->overlaping_on_end) {
+ GST_INFO ("Clip is overlapped by %s and %s at its end",
+ data->overlaping_on_end->name, e->name);
+ goto error;
+ }
+ data->overlaping_on_end = node->data;
+ }
+
+ return FALSE;
+
+error:
+ data->res = FALSE;
+ return TRUE;
+}
+
+static gboolean
+check_can_move_children (GNode * node, TreeIterationData * data)
+{
+ GESTimelineElement *element = node->data;
+ GstClockTimeDiff start = GST_CLOCK_DIFF (data->start_diff, element->start);
+ GstClockTime inpoint = GST_CLOCK_DIFF (data->inpoint_diff, element->inpoint);
+ GstClockTime duration =
+ GST_CLOCK_DIFF (data->duration_diff, element->duration);
+ gint64 priority =
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element) -
+ data->priority_diff;
+ if (element == data->element)
+ return FALSE;
+
+ data->res =
+ timeline_tree_can_move_element_internal (data->root, node->data, priority,
+ start, inpoint, duration, data->movings, data->ripple_time,
+ data->snapping, data->edge);
+
+ return !data->res;
+}
+
+static gboolean
+timeline_tree_can_move_element_from_data (GNode * root,
+ TreeIterationData * data)
+{
+ GNode *node = find_node (root, data->element);
+
+ g_assert (node);
+ if (G_NODE_IS_LEAF (node)) {
+ if (GES_IS_SOURCE (node->data)) {
+ g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_LEAVES, -1,
+ (GNodeTraverseFunc) check_track_elements_overlaps_and_values, data);
+
+ return data->res;
+ }
+
+ return TRUE;
+ }
+
+ g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_LEAFS, -1,
+ (GNodeTraverseFunc) check_can_move_children, data);
+
+ return data->res;
+}
+
+static gboolean
+add_element_to_list (GNode * node, GList ** elements)
+{
+ *elements = g_list_prepend (*elements, node->data);
+
+ return FALSE;
+}
+
+static gboolean
+timeline_tree_can_move_element_internal (GNode * root,
+ GESTimelineElement * element, gint64 priority, GstClockTimeDiff start,
+ GstClockTimeDiff inpoint, GstClockTimeDiff duration,
+ GList * moving_track_elements, GstClockTime ripple_time,
+ SnappingData * snapping, GESEdge edge)
+{
+ gboolean res;
+ TreeIterationData data = tree_iteration_data_init;
+
+ data.root = root;
+ data.start_diff = GST_CLOCK_DIFF (start, element->start);
+ data.inpoint_diff = GST_CLOCK_DIFF (inpoint, element->inpoint);
+ data.duration_diff = GST_CLOCK_DIFF (duration, element->duration);
+ data.priority_diff =
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element) - priority;
+ data.element = element;
+ data.movings = g_list_copy (moving_track_elements);
+ data.ripple_time = ripple_time;
+ data.snapping = snapping;
+ data.edge = edge;
+
+ if (GES_IS_SOURCE (element))
+ data.priority_diff =
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element) - priority;
+
+ res = timeline_tree_can_move_element_from_data (root, &data);
+ clean_iteration_data (&data);
+
+ return res;
+}
+
+gboolean
+timeline_tree_can_move_element (GNode * root,
+ GESTimelineElement * element, guint32 priority, GstClockTime start,
+ GstClockTime duration, GList * moving_track_elements)
+{
+ GESTimelineElement *toplevel;
+ GstClockTimeDiff start_offset, duration_offset;
+ gint64 priority_diff;
+ gboolean res;
+ GList *local_moving_track_elements = g_list_copy (moving_track_elements);
+
+ toplevel = get_toplevel_container (element);
+ if (ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE) ||
+ ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE))
+ return TRUE;
+
+ start_offset = GST_CLOCK_DIFF (start, element->start);
+ duration_offset = GST_CLOCK_DIFF (duration, element->duration);
+ priority_diff =
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (toplevel) -
+ (gint64) priority;
+
+ g_node_traverse (find_node (root, toplevel), G_IN_ORDER,
+ G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list,
+ &local_moving_track_elements);
+
+ res = timeline_tree_can_move_element_internal (root, toplevel,
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (toplevel) - priority_diff,
+ GST_CLOCK_DIFF (start_offset, toplevel->start),
+ toplevel->inpoint,
+ GST_CLOCK_DIFF (duration_offset, toplevel->duration),
+ local_moving_track_elements, GST_CLOCK_TIME_NONE, NULL, GES_EDGE_NONE);
+
+ g_list_free (local_moving_track_elements);
+ return res;
+}
+
+static void
+move_to_new_layer (GESTimelineElement * elem, gint layer_priority_offset)
+{
+ guint32 nprio =
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (elem) - layer_priority_offset;
+ GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (elem);
+
+ if (!layer_priority_offset)
+ return;
+
+ GST_DEBUG ("%s moving from %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT " (%"
+ G_GUINT32_FORMAT ")", elem->name, elem->priority, nprio,
+ layer_priority_offset);
+ if (GES_IS_CLIP (elem)) {
+ GESLayer *layer = ges_timeline_get_layer (timeline, nprio);
+
+ if (layer == NULL) {
+ do {
+ layer = ges_timeline_append_layer (timeline);
+ } while (ges_layer_get_priority (layer) < nprio);
+ } else {
+ gst_object_unref (layer);
+ }
+
+ ges_clip_move_to_layer (GES_CLIP (elem), layer);
+ } else if (GES_IS_GROUP (elem)) {
+ ges_timeline_element_set_priority (elem, nprio);
+ } else {
+ g_assert_not_reached ();
+ }
+}
+
+gboolean
+timeline_tree_ripple (GNode * root, gint64 layer_priority_offset,
+ GstClockTimeDiff offset, GESTimelineElement * rippled_element,
+ GESEdge edge, GstClockTime snapping_distance)
+{
+ GNode *node;
+ GHashTableIter iter;
+ GESTimelineElement *elem;
+ GstClockTimeDiff start, duration;
+ gboolean res = TRUE;
+ GHashTable *to_move = g_hash_table_new (g_direct_hash, g_direct_equal);
+ GList *moving_track_elements = NULL;
+ SnappingData snapping = {
+ .distance = snapping_distance,
+ .on_end_only = edge == GES_EDGE_END,
+ .on_start_only = FALSE,
+ .element = NULL,
+ .edge = GES_EDGE_NONE,
+ .diff = (GstClockTimeDiff) snapping_distance,
+ };
+ gint64 new_layer_priority =
+ ((gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (rippled_element)) -
+ layer_priority_offset;
+ GESTimelineElement *ripple_toplevel =
+ get_toplevel_container (rippled_element);
+ GstClockTimeDiff ripple_time = ELEMENT_EDGE_VALUE (rippled_element, edge);
+
+ if (edge == GES_EDGE_END) {
+ if (ripple_toplevel != rippled_element) {
+ GST_FIXME ("Trying to ripple end %" GES_FORMAT " but in %" GES_FORMAT
+ " we do not know how to do that yet!",
+ GES_ARGS (rippled_element), GES_ARGS (ripple_toplevel));
+ goto error;
+ }
+ } else {
+ g_node_traverse (find_node (root, ripple_toplevel), G_IN_ORDER,
+ G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list,
+ &moving_track_elements);
+ }
+
+ GST_INFO ("Moving %" GES_FORMAT " with offset %" G_GINT64_FORMAT "",
+ GES_ARGS (ripple_toplevel), offset);
+
+ if (edge == GES_EDGE_END) {
+ start = _START (rippled_element);
+ duration = GST_CLOCK_DIFF (offset, _DURATION (rippled_element));
+ } else {
+ start = GST_CLOCK_DIFF (offset, _START (rippled_element));
+ duration = _DURATION (rippled_element);
+ }
+
+ if (!timeline_tree_can_move_element_internal (root, rippled_element,
+ new_layer_priority, start, rippled_element->inpoint, duration, NULL,
+ ripple_time, snapping_distance ? &snapping : NULL, edge)) {
+ goto error;
+ }
+
+ if (snapping_distance) {
+ if (snapping.element) {
+ offset =
+ GST_CLOCK_DIFF (ELEMENT_EDGE_VALUE (snapping.element, snapping.edge),
+ ELEMENT_EDGE_VALUE (snapping.moving_element, snapping.moving_edge));
+
+ if (edge == GES_EDGE_END) {
+ start = _START (rippled_element);
+ duration = GST_CLOCK_DIFF (offset, _DURATION (rippled_element));
+ } else {
+ start = GST_CLOCK_DIFF (offset, _START (rippled_element));
+ duration = _DURATION (rippled_element);
+ }
+
+ GST_INFO ("Snapping on %" GES_FORMAT "%s %" G_GINT64_FORMAT "",
+ GES_ARGS (snapping.element),
+ snapping.edge == GES_EDGE_END ? "end" : "start",
+ ELEMENT_EDGE_VALUE (snapping.element, snapping.edge));
+ if (!timeline_tree_can_move_element_internal (root, rippled_element,
+ new_layer_priority, start, rippled_element->inpoint, duration,
+ NULL, ripple_time, NULL, edge)) {
+ goto error;
+ }
+ }
+
+ ges_timeline_emit_snapping (root->data, rippled_element, snapping.element,
+ snapping.element ? ELEMENT_EDGE_VALUE (snapping.element,
+ snapping.edge) : GST_CLOCK_TIME_NONE);
+ }
+
+ /* Make sure we can ripple all toplevels after the rippled element */
+ for (node = root->children; node; node = node->next) {
+ GESTimelineElement *toplevel = get_toplevel_container (node->data);
+
+ if (GES_TIMELINE_ELEMENT_START (toplevel) < ripple_time
+ && (edge == GES_EDGE_END || toplevel != ripple_toplevel))
+ continue;
+
+ if (!timeline_tree_can_move_element_internal (root, node->data,
+ ((gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (node->data)) -
+ layer_priority_offset,
+ GST_CLOCK_DIFF (offset, _START (node->data)),
+ _INPOINT (node->data),
+ _DURATION (node->data), moving_track_elements, ripple_time, NULL,
+ GES_EDGE_NONE)) {
+ goto error;
+ }
+
+ if (!check_can_move_to_layer (toplevel, layer_priority_offset)) {
+ GST_INFO ("%" GES_FORMAT " would land in a layer with negative priority",
+ GES_ARGS (toplevel));
+ goto error;
+ }
+
+ g_hash_table_add (to_move, toplevel);
+ }
+
+ if (edge == GES_EDGE_END) {
+ if (!check_can_move_to_layer (rippled_element, layer_priority_offset)) {
+ GST_INFO ("%" GES_FORMAT " would land in a layer with negative priority",
+ GES_ARGS (rippled_element));
+
+ goto error;
+ }
+
+ if (duration < 0) {
+ GST_INFO ("Would set duration to %" G_GINT64_FORMAT " <= 0", duration);
+ goto error;
+ }
+
+ ELEMENT_SET_FLAG (rippled_element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ ges_timeline_element_set_duration (rippled_element, duration);
+ ELEMENT_UNSET_FLAG (rippled_element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ }
+
+ g_hash_table_iter_init (&iter, to_move);
+ while (g_hash_table_iter_next (&iter, (gpointer *) & elem, NULL)) {
+ GST_LOG ("Moving %" GES_FORMAT " to %" G_GINT64_FORMAT " - layer %"
+ G_GINT64_FORMAT "", GES_ARGS (elem),
+ GES_TIMELINE_ELEMENT_START (elem) - offset,
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (elem) -
+ layer_priority_offset);
+
+ ELEMENT_SET_FLAG (elem, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ ges_timeline_element_set_start (elem,
+ GST_CLOCK_DIFF (offset, GES_TIMELINE_ELEMENT_START (elem)));
+ move_to_new_layer (elem, layer_priority_offset);
+ ELEMENT_UNSET_FLAG (elem, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ }
+
+ ELEMENT_SET_FLAG (rippled_element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ if (edge == GES_EDGE_END)
+ move_to_new_layer (rippled_element, layer_priority_offset);
+ ELEMENT_UNSET_FLAG (rippled_element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+
+ timeline_tree_create_transitions (root, ges_timeline_find_auto_transition);
+ timeline_update_transition (root->data);
+ timeline_update_duration (root->data);
+
+done:
+ g_hash_table_unref (to_move);
+ g_list_free (moving_track_elements);
+ return res;
+
+error:
+ res = FALSE;
+ goto done;
+}
+
+static gboolean
+check_trim_child (GNode * node, TreeIterationData * data)
+{
+ GESTimelineElement *e = node->data;
+ GstClockTimeDiff n_start = GST_CLOCK_DIFF (data->start_diff, e->start);
+ GstClockTimeDiff n_inpoint = GST_CLOCK_DIFF (data->inpoint_diff, e->inpoint);
+ GstClockTimeDiff n_duration = data->edge == GES_EDGE_END ?
+ GST_CLOCK_DIFF (data->duration_diff, e->duration) :
+ GST_CLOCK_DIFF (n_start, (GstClockTimeDiff) e->start + e->duration);
+
+ if (!timeline_tree_can_move_element_internal (data->root, e,
+ (gint64) ges_timeline_element_get_layer_priority (e) -
+ data->priority_diff, n_start, n_inpoint, n_duration, NULL,
+ GST_CLOCK_TIME_NONE, data->snapping, GES_EDGE_NONE))
+ goto error;
+
+ if (GES_IS_CLIP (e->parent))
+ g_hash_table_add (data->moved_clips, e->parent);
+ else if (GES_IS_CLIP (e))
+ g_hash_table_add (data->moved_clips, e);
+
+ return FALSE;
+
+error:
+ data->res = FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+timeline_tree_can_trim_element_internal (GNode * root, TreeIterationData * data)
+{
+ g_node_traverse (find_node (root, data->element), G_IN_ORDER,
+ G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) check_trim_child, data);
+
+ return data->res;
+}
+
+static void
+trim_simple (GESTimelineElement * element, GstClockTimeDiff offset,
+ GESEdge edge)
+{
+ ELEMENT_SET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ if (edge == GES_EDGE_END) {
+ ges_timeline_element_set_duration (element, GST_CLOCK_DIFF (offset,
+ element->duration));
+ } else {
+ ges_timeline_element_set_start (element, GST_CLOCK_DIFF (offset,
+ element->start));
+ ges_timeline_element_set_inpoint (element, GST_CLOCK_DIFF (offset,
+ element->inpoint));
+ ges_timeline_element_set_duration (element, element->duration + offset);
+ }
+ GST_LOG ("Trimmed %" GES_FORMAT, GES_ARGS (element));
+ ELEMENT_UNSET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+}
+
+#define SET_TRIMMING_DATA(data, _edge, offset) G_STMT_START { \
+ data.edge = (_edge); \
+ data.start_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
+ data.inpoint_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
+ data.duration_diff = (_edge) == GES_EDGE_END ? (offset) : -(offset); \
+} G_STMT_END
+
+
+gboolean
+timeline_tree_trim (GNode * root, GESTimelineElement * element,
+ gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
+ GstClockTime snapping_distance)
+{
+ GHashTableIter iter;
+ gboolean res = TRUE;
+ GESTimelineElement *elem;
+ gint64 new_layer_priority =
+ ((gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element)) -
+ layer_priority_offset;
+ SnappingData snapping = {
+ .distance = snapping_distance,
+ .on_end_only = edge == GES_EDGE_END,
+ .on_start_only = edge != GES_EDGE_END,
+ .element = NULL,
+ .edge = GES_EDGE_NONE,
+ .diff = (GstClockTimeDiff) snapping_distance,
+ };
+ TreeIterationData data = tree_iteration_data_init;
+
+ data.root = root;
+ data.element = element;
+ data.priority_diff =
+ (gint64) ges_timeline_element_get_layer_priority (element) -
+ new_layer_priority;
+ data.snapping = snapping_distance ? &snapping : NULL;
+ data.moved_clips = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ SET_TRIMMING_DATA (data, edge, offset);
+ GST_INFO ("%" GES_FORMAT " trimming %s with offset %" G_GINT64_FORMAT "",
+ GES_ARGS (element), edge == GES_EDGE_END ? "end" : "start", offset);
+ g_node_traverse (find_node (root, element), G_IN_ORDER,
+ G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list,
+ &data.movings);
+
+ if (!timeline_tree_can_trim_element_internal (root, &data)) {
+ GST_INFO ("Can not trim object.");
+ goto error;
+ }
+
+ if (snapping_distance) {
+ if (snapping.element) {
+ offset =
+ GST_CLOCK_DIFF (ELEMENT_EDGE_VALUE (snapping.element, snapping.edge),
+ ELEMENT_EDGE_VALUE (snapping.moving_element, snapping.moving_edge));
+
+ GST_INFO ("Snapping on %" GES_FORMAT "%s %" G_GINT64_FORMAT
+ " -- offset: %" G_GINT64_FORMAT "", GES_ARGS (snapping.element),
+ snapping.edge == GES_EDGE_END ? "end" : "start",
+ ELEMENT_EDGE_VALUE (snapping.element, snapping.edge), offset);
+ }
+
+ ges_timeline_emit_snapping (root->data, element,
+ snapping.element,
+ snapping.element ? ELEMENT_EDGE_VALUE (snapping.element,
+ snapping.edge) : GST_CLOCK_TIME_NONE);
+ }
+
+ g_hash_table_iter_init (&iter, data.moved_clips);
+ while (g_hash_table_iter_next (&iter, (gpointer *) & elem, NULL))
+ trim_simple (elem, offset, edge);
+
+ timeline_tree_create_transitions (root, ges_timeline_find_auto_transition);
+ timeline_update_transition (root->data);
+ timeline_update_duration (root->data);
+
+done:
+ clean_iteration_data (&data);
+ return res;
+
+error:
+ res = FALSE;
+ goto done;
+}
+
+gboolean
+timeline_tree_move (GNode * root, GESTimelineElement * element,
+ gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
+ GstClockTime snapping_distance)
+{
+ gboolean res = TRUE;
+ GESTimelineElement *toplevel = get_toplevel_container (element);
+ TreeIterationData data = tree_iteration_data_init;
+ SnappingData snapping = {
+ .distance = snapping_distance,
+ .on_end_only = edge == GES_EDGE_END,
+ .on_start_only = edge == GES_EDGE_END,
+ .element = NULL,
+ .edge = GES_EDGE_NONE,
+ .diff = (GstClockTimeDiff) snapping_distance,
+ };
+
+ data.root = root;
+ data.element = edge == GES_EDGE_END ? element : toplevel;
+ data.edge = edge;
+ data.priority_diff = layer_priority_offset;
+ data.snapping = snapping_distance ? &snapping : NULL;
+ data.start_diff = edge == GES_EDGE_END ? 0 : offset;
+ data.duration_diff = edge == GES_EDGE_END ? offset : 0;
+
+ GST_INFO ("%" GES_FORMAT
+ " moving %s with offset %" G_GINT64_FORMAT ", (snaping distance: %"
+ G_GINT64_FORMAT ")", GES_ARGS (element),
+ edge == GES_EDGE_END ? "end" : "start", offset, snapping_distance);
+ g_node_traverse (find_node (root, data.element), G_IN_ORDER,
+ G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list,
+ &data.movings);
+
+ if (!timeline_tree_can_move_element_from_data (root, &data)) {
+ GST_INFO ("Can not move object.");
+ goto error;
+ }
+
+ if (snapping_distance) {
+ if (snapping.element) {
+ gint64 noffset =
+ GST_CLOCK_DIFF (ELEMENT_EDGE_VALUE (snapping.element, snapping.edge),
+ ELEMENT_EDGE_VALUE (snapping.moving_element, snapping.moving_edge));
+
+ GST_INFO ("Snapping %" GES_FORMAT " (%s) with %" GES_FORMAT
+ "%s %" G_GINT64_FORMAT " -- offset: %" G_GINT64_FORMAT
+ " (previous offset: %" G_GINT64_FORMAT ")",
+ GES_ARGS (snapping.moving_element),
+ snapping.moving_edge == GES_EDGE_END ? "end" : "start",
+ GES_ARGS (snapping.element),
+ snapping.edge == GES_EDGE_END ? "end" : "start",
+ ELEMENT_EDGE_VALUE (snapping.element, snapping.edge), noffset,
+ offset);
+ offset = noffset;
+ data.start_diff = edge == GES_EDGE_END ? 0 : offset;
+ data.duration_diff = edge == GES_EDGE_END ? offset : 0;
+ data.snapping = NULL;
+ if (!timeline_tree_can_move_element_from_data (root, &data)) {
+ GST_INFO ("Can not move object.");
+ goto error;
+ }
+ }
+
+ ges_timeline_emit_snapping (root->data, element,
+ snapping.element,
+ snapping.element ? ELEMENT_EDGE_VALUE (snapping.element,
+ snapping.edge) : GST_CLOCK_TIME_NONE);
+ }
+
+ if (!check_can_move_to_layer (toplevel, layer_priority_offset)) {
+ GST_INFO ("%" GES_FORMAT " would land in a layer with negative priority",
+ GES_ARGS (toplevel));
+ goto error;
+ }
+
+ ELEMENT_SET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+ if (edge == GES_EDGE_END)
+ ges_timeline_element_set_duration (element, GST_CLOCK_DIFF (offset,
+ element->duration));
+ else
+ ges_timeline_element_set_start (toplevel, GST_CLOCK_DIFF (offset,
+ toplevel->start));
+ move_to_new_layer (toplevel, layer_priority_offset);
+ ELEMENT_UNSET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+
+ timeline_tree_create_transitions (root, ges_timeline_find_auto_transition);
+ timeline_update_transition (root->data);
+ timeline_update_duration (root->data);
+
+ GST_LOG ("Moved %" GES_FORMAT, GES_ARGS (element));
+
+done:
+ clean_iteration_data (&data);
+ return res;
+
+error:
+ res = FALSE;
+ goto done;
+}
+
+static gboolean
+find_neighbour (GNode * node, TreeIterationData * data)
+{
+ gboolean in_same_track = FALSE;
+ GList *tmp;
+
+ if (!GES_IS_SOURCE (node->data)) {
+ return FALSE;
+ }
+
+
+ for (tmp = GES_CONTAINER_CHILDREN (data->element); tmp; tmp = tmp->next) {
+ if (tmp->data == node->data)
+ return FALSE;
+
+ if (ges_track_element_get_track (node->data) ==
+ ges_track_element_get_track (tmp->data)) {
+ in_same_track = TRUE;
+ }
+ }
+
+ if (!in_same_track) {
+ return FALSE;
+ }
+
+ if (ELEMENT_EDGE_VALUE (node->data,
+ data->edge == GES_EDGE_START ? GES_EDGE_END : GES_EDGE_START) ==
+ ELEMENT_EDGE_VALUE (data->element, data->edge)) {
+ if (!g_list_find (data->neighbours,
+ GES_TIMELINE_ELEMENT_PARENT (node->data)))
+ data->neighbours =
+ g_list_prepend (data->neighbours,
+ GES_TIMELINE_ELEMENT_PARENT (node->data));
+ }
+
+ return FALSE;
+}
+
+gboolean
+timeline_tree_roll (GNode * root, GESTimelineElement * element,
+ GstClockTimeDiff offset, GESEdge edge, GstClockTime snapping_distance)
+{
+ gboolean res = TRUE;
+ GList *tmp;
+ GESEdge neighbour_edge;
+ TreeIterationData data = tree_iteration_data_init;
+ SnappingData snapping = {
+ .distance = snapping_distance,
+ .on_end_only = edge == GES_EDGE_END,
+ .on_start_only = edge == GES_EDGE_END,
+ .element = NULL,
+ .edge = GES_EDGE_NONE,
+ .moving_edge = GES_EDGE_NONE,
+ .diff = (GstClockTimeDiff) snapping_distance,
+ };
+
+ data.root = root;
+ data.element = element;
+ data.edge = edge;
+ data.snapping = snapping_distance ? &snapping : NULL;
+ data.start_diff = edge == GES_EDGE_END ? 0 : offset;
+ data.inpoint_diff = edge == GES_EDGE_END ? 0 : offset;
+ data.duration_diff = edge == GES_EDGE_END ? offset : -offset;
+ data.ripple_time = GST_CLOCK_TIME_NONE;
+ neighbour_edge = data.edge == GES_EDGE_END ? GES_EDGE_START : GES_EDGE_END;
+
+ SET_TRIMMING_DATA (data, edge, offset);
+ g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_LEAFS, -1,
+ (GNodeTraverseFunc) find_neighbour, &data);
+
+ if (data.neighbours == NULL) {
+ GST_INFO ("%s doesn't have any direct neighbour on edge %s",
+ element->name, ges_edge_name (edge));
+
+ return timeline_tree_trim (root, element, 0, offset, edge,
+ snapping_distance);
+ }
+
+ GST_INFO ("Trimming %" GES_FORMAT " %s to %" G_GINT64_FORMAT "",
+ GES_ARGS (data.element), ges_edge_name (edge), offset);
+
+ if (!timeline_tree_can_move_element_from_data (root, &data))
+ goto error;
+
+
+ if (snapping_distance) {
+ if (snapping.element) {
+ gint64 noffset =
+ GST_CLOCK_DIFF (ELEMENT_EDGE_VALUE (snapping.element, snapping.edge),
+ ELEMENT_EDGE_VALUE (snapping.moving_element, snapping.moving_edge));
+
+ GST_INFO ("Snapping %" GES_FORMAT " (%s) with %" GES_FORMAT
+ "%s %" G_GINT64_FORMAT " -- offset: %" G_GINT64_FORMAT
+ " (previous offset: %" G_GINT64_FORMAT ")",
+ GES_ARGS (snapping.moving_element),
+ snapping.moving_edge == GES_EDGE_END ? "end" : "start",
+ GES_ARGS (snapping.element),
+ snapping.edge == GES_EDGE_END ? "end" : "start",
+ ELEMENT_EDGE_VALUE (snapping.element, snapping.edge), noffset,
+ offset);
+ offset = noffset;
+
+ SET_TRIMMING_DATA (data, edge, offset);
+
+ if (!timeline_tree_can_move_element_from_data (root, &data)) {
+ GST_INFO ("Can not move object.");
+ goto error;
+ }
+ }
+ }
+
+ if (snapping_distance && snapping.element) {
+ ges_timeline_emit_snapping (root->data, element,
+ snapping.element,
+ snapping.element ? ELEMENT_EDGE_VALUE (snapping.element,
+ snapping.edge) : GST_CLOCK_TIME_NONE);
+ }
+
+ data.snapping = NULL;
+ SET_TRIMMING_DATA (data, neighbour_edge, offset);
+ for (tmp = data.neighbours; tmp; tmp = tmp->next) {
+ data.element = tmp->data;
+
+ GST_INFO ("Trimming %" GES_FORMAT " %s to %" G_GINT64_FORMAT "",
+ GES_ARGS (data.element), ges_edge_name (data.edge), offset);
+ if (!timeline_tree_can_move_element_from_data (root, &data)) {
+ GST_INFO ("Can not move object.");
+ goto error;
+ }
+ }
+
+ trim_simple (element, offset, edge);
+ for (tmp = data.neighbours; tmp; tmp = tmp->next)
+ trim_simple (tmp->data, offset, data.edge);
+
+done:
+ timeline_update_duration (root->data);
+ g_list_free (data.neighbours);
+ return res;
+
+error:
+ res = FALSE;
+ goto done;
+}
+
+static void
+create_transition_if_needed (GESTimeline * timeline, GESTrackElement * prev,
+ GESTrackElement * next, GESTreeGetAutoTransitionFunc get_auto_transition)
+{
+ GstClockTime duration = _END (prev) - _START (next);
+ GESAutoTransition *trans =
+ get_auto_transition (timeline, prev, next, duration);
+
+ if (!trans) {
+ GESLayer *layer = ges_timeline_get_layer (timeline,
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (prev));
+ gst_object_unref (layer);
+
+ GST_INFO ("Creating transition [%" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
+ "]", _START (next), duration);
+ ges_timeline_create_transition (timeline, prev, next, NULL, layer,
+ _START (next), duration);
+ }
+}
+
+static gboolean
+create_transitions (GNode * node,
+ GESTreeGetAutoTransitionFunc get_auto_transition)
+{
+ TreeIterationData data = tree_iteration_data_init;
+ GESTimeline *timeline;
+ GESLayer *layer;
+
+ if (G_NODE_IS_ROOT (node))
+ return FALSE;
+
+ timeline = GES_TIMELINE_ELEMENT_TIMELINE (node->data);
+ data.element = node->data;
+ if (!GES_IS_SOURCE (node->data))
+ return FALSE;
+
+ if (!timeline) {
+ GST_INFO ("%" GES_FORMAT " not in timeline yet", GES_ARGS (node->data));
+
+ return FALSE;
+ }
+
+ layer =
+ ges_timeline_get_layer (timeline,
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (node->data));
+ gst_object_unref (layer);
+
+ if (!ges_layer_get_auto_transition (layer))
+ return FALSE;
+
+ g_node_traverse (g_node_get_root (node), G_IN_ORDER, G_TRAVERSE_LEAVES, -1,
+ (GNodeTraverseFunc) check_track_elements_overlaps_and_values, &data);
+
+ if (data.overlaping_on_start)
+ create_transition_if_needed (timeline,
+ GES_TRACK_ELEMENT (data.overlaping_on_start), node->data,
+ get_auto_transition);
+
+ if (data.overlaping_on_end)
+ create_transition_if_needed (timeline, node->data,
+ GES_TRACK_ELEMENT (data.overlaping_on_end), get_auto_transition);
+
+ return FALSE;
+}
+
+void
+timeline_tree_create_transitions (GNode * root,
+ GESTreeGetAutoTransitionFunc get_auto_transition)
+{
+ g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_LEAFS, -1,
+ (GNodeTraverseFunc) create_transitions, get_auto_transition);
+}
+
+static gboolean
+compute_duration (GNode * node, GstClockTime * duration)
+{
+ *duration = MAX (_END (node->data), *duration);
+
+ return FALSE;
+}
+
+GstClockTime
+timeline_tree_get_duration (GNode * root)
+{
+ GstClockTime duration = 0;
+
+ if (root->children)
+ g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_LEAFS, -1,
+ (GNodeTraverseFunc) compute_duration, &duration);
+
+ return duration;
+}
--- /dev/null
+#ifndef __GES_TIMELINE_TREE__
+#define __GES_TIMELINE_TREE__
+
+#include <ges/ges.h>
+#include "ges-auto-transition.h"
+
+void timeline_tree_track_element (GNode *root,
+ GESTimelineElement *element);
+
+void timeline_tree_stop_tracking_element (GNode *root,
+ GESTimelineElement *element);
+
+gboolean timeline_tree_can_move_element (GNode *root,
+ GESTimelineElement *element,
+ guint32 priority,
+ GstClockTime start,
+ GstClockTime duration,
+ GList *moving_track_elements);
+
+gboolean timeline_tree_ripple (GNode *root,
+ gint64 layer_priority_offset,
+ GstClockTimeDiff offset,
+ GESTimelineElement *rippled_element,
+ GESEdge moving_edge,
+ GstClockTime snapping_distance);
+
+void ges_timeline_emit_snapping (GESTimeline * timeline,
+ GESTimelineElement * elem1,
+ GESTimelineElement * elem2,
+ GstClockTime snap_time);
+
+gboolean timeline_tree_trim (GNode *root,
+ GESTimelineElement *element,
+ gint64 layer_priority_offset,
+ GstClockTimeDiff offset,
+ GESEdge edge,
+ GstClockTime snapping_distance);
+
+
+gboolean timeline_tree_move (GNode *root,
+ GESTimelineElement *element,
+ gint64 layer_priority_offset,
+ GstClockTimeDiff offset,
+ GESEdge edge,
+ GstClockTime snapping_distance);
+
+gboolean timeline_tree_roll (GNode * root,
+ GESTimelineElement * element,
+ GstClockTimeDiff offset,
+ GESEdge edge,
+ GstClockTime snapping_distance);
+
+typedef GESAutoTransition *
+(*GESTreeGetAutoTransitionFunc) (GESTimeline * timeline,
+ GESTrackElement * previous,
+ GESTrackElement * next,
+ GstClockTime transition_duration);
+
+void timeline_tree_create_transitions (GNode *root,
+ GESTreeGetAutoTransitionFunc get_auto_transition);
+
+GstClockTime timeline_tree_get_duration (GNode *root);
+
+void timeline_tree_debug (GNode * root);
+
+GESAutoTransition *
+ges_timeline_create_transition (GESTimeline * timeline, GESTrackElement * previous,
+ GESTrackElement * next, GESClip * transition,
+ GESLayer * layer, guint64 start, guint64 duration);
+GESAutoTransition *
+ges_timeline_find_auto_transition (GESTimeline * timeline, GESTrackElement * prev,
+ GESTrackElement * next, GstClockTime transition_duration);
+
+void
+timeline_update_duration (GESTimeline * timeline);
+
+void timeline_tree_init_debug (void);
+
+#endif // __GES_TIMELINE_TREE__
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ * 2012 Thibault Saunier <tsaunier@gnome.org>
+ * 2012 Collabora Ltd.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ * 2019 Igalia S.L
+ * Author: Thibault Saunier <tsaunier@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.
+ */
+
+/**
+ * SECTION:gestimeline
+ * @title: GESTimeline
+ * @short_description: Multimedia timeline
+ *
+ * #GESTimeline is the central object for any multimedia timeline.
+ *
+ * Contains a list of #GESLayer which users should use to arrange the
+ * various clips through time.
+ *
+ * The output type is determined by the #GESTrack that are set on
+ * the #GESTimeline.
+ *
+ * To save/load a timeline, you can use the ges_timeline_load_from_uri() and
+ * ges_timeline_save_to_uri() methods to use the default format. If you wish
+ *
+ * Note that any change you make in the timeline will not actually be taken
+ * into account until you call the #ges_timeline_commit method.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-project.h"
+#include "ges-container.h"
+#include "ges-timeline.h"
+#include "ges-timeline-tree.h"
+#include "ges-track.h"
+#include "ges-layer.h"
+#include "ges-auto-transition.h"
+#include "ges.h"
+
+
+static GPtrArray *select_tracks_for_object_default (GESTimeline * timeline,
+ GESClip * clip, GESTrackElement * tr_obj, gpointer user_data);
+static void ges_extractable_interface_init (GESExtractableInterface * iface);
+static void ges_meta_container_interface_init
+ (GESMetaContainerInterface * iface);
+
+GST_DEBUG_CATEGORY_STATIC (ges_timeline_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT ges_timeline_debug
+
+/* lock to protect dynamic callbacks, like pad-added */
+#define DYN_LOCK(timeline) (&GES_TIMELINE (timeline)->priv->dyn_mutex)
+#define LOCK_DYN(timeline) G_STMT_START { \
+ GST_LOG_OBJECT (timeline, "Getting dynamic lock from %p", \
+ g_thread_self()); \
+ g_rec_mutex_lock (DYN_LOCK (timeline)); \
+ GST_LOG_OBJECT (timeline, "Got Dynamic lock from %p", \
+ g_thread_self()); \
+ } G_STMT_END
+
+#define UNLOCK_DYN(timeline) G_STMT_START { \
+ GST_LOG_OBJECT (timeline, "Unlocking dynamic lock from %p", \
+ g_thread_self()); \
+ g_rec_mutex_unlock (DYN_LOCK (timeline)); \
+ GST_LOG_OBJECT (timeline, "Unlocked Dynamic lock from %p", \
+ g_thread_self()); \
+ } G_STMT_END
+
+#define CHECK_THREAD(timeline) g_assert(timeline->priv->valid_thread == g_thread_self())
+
+struct _GESTimelinePrivate
+{
+ GNode *tree;
+
+ /* The duration of the timeline */
+ gint64 duration;
+
+ /* The auto-transition of the timeline */
+ gboolean auto_transition;
+
+ /* Timeline edition modes and snapping management */
+ guint64 snapping_distance;
+
+ GRecMutex dyn_mutex;
+ GList *priv_tracks;
+
+ /* Avoid sorting layers when we are actually resyncing them ourself */
+ gboolean resyncing_layers;
+ GList *auto_transitions;
+
+ /* Last snapping properties */
+ GstClockTime last_snap_ts;
+ GESTrackElement *last_snaped1;
+ GESTrackElement *last_snaped2;
+
+ /* This variable is set to %TRUE when it makes sense to update the transitions,
+ * and %FALSE otherwize */
+ gboolean needs_transitions_update;
+
+ /* While we are creating and adding the TrackElements for a clip, we need to
+ * ignore the child-added signal */
+ GESClip *ignore_track_element_added;
+ GList *groups;
+
+ guint stream_start_group_id;
+
+ GHashTable *all_elements;
+
+ /* With GST_OBJECT_LOCK */
+ guint expected_async_done;
+ /* With GST_OBJECT_LOCK */
+ guint expected_commited;
+
+ /* For ges_timeline_commit_sync */
+ GMutex commited_lock;
+ GCond commited_cond;
+
+ GThread *valid_thread;
+};
+
+/* private structure to contain our track-related information */
+
+typedef struct
+{
+ GESTimeline *timeline;
+ GESTrack *track;
+ GstPad *pad; /* Pad from the track */
+ GstPad *ghostpad;
+
+ gulong probe_id;
+} TrackPrivate;
+
+enum
+{
+ PROP_0,
+ PROP_DURATION,
+ PROP_AUTO_TRANSITION,
+ PROP_SNAPPING_DISTANCE,
+ PROP_UPDATE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+enum
+{
+ TRACK_ADDED,
+ TRACK_REMOVED,
+ LAYER_ADDED,
+ LAYER_REMOVED,
+ GROUP_ADDED,
+ GROUP_REMOVED,
+ SNAPING_STARTED,
+ SNAPING_ENDED,
+ SELECT_TRACKS_FOR_OBJECT,
+ COMMITED,
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE_WITH_CODE (GESTimeline, ges_timeline, GST_TYPE_BIN,
+ G_ADD_PRIVATE (GESTimeline)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, ges_extractable_interface_init)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
+ ges_meta_container_interface_init));
+
+static GstBinClass *parent_class;
+
+static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
+
+static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track);
+
+static guint nb_assets = 0;
+
+/* GESExtractable implementation */
+static gchar *
+extractable_check_id (GType type, const gchar * id)
+{
+ gchar *res;
+
+ if (id == NULL)
+ res = g_strdup_printf ("%s-%i", "project", nb_assets);
+ else
+ res = g_strdup (id);
+
+ nb_assets++;
+
+ return res;
+}
+
+static gchar *
+extractable_get_id (GESExtractable * self)
+{
+ GESAsset *asset;
+
+ if (!(asset = ges_extractable_get_asset (self)))
+ return NULL;
+
+ return g_strdup (ges_asset_get_id (asset));
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_PROJECT;
+ iface->check_id = (GESExtractableCheckId) extractable_check_id;
+ iface->get_id = extractable_get_id;
+}
+
+static void
+ges_meta_container_interface_init (GESMetaContainerInterface * iface)
+{
+}
+
+/* GObject Standard vmethods*/
+static void
+ges_timeline_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTimeline *timeline = GES_TIMELINE (object);
+
+ switch (property_id) {
+ case PROP_DURATION:
+ g_value_set_uint64 (value, timeline->priv->duration);
+ break;
+ case PROP_AUTO_TRANSITION:
+ g_value_set_boolean (value, timeline->priv->auto_transition);
+ break;
+ case PROP_SNAPPING_DISTANCE:
+ g_value_set_uint64 (value, timeline->priv->snapping_distance);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_timeline_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTimeline *timeline = GES_TIMELINE (object);
+
+ switch (property_id) {
+ case PROP_AUTO_TRANSITION:
+ ges_timeline_set_auto_transition (timeline, g_value_get_boolean (value));
+ break;
+ case PROP_SNAPPING_DISTANCE:
+ timeline->priv->snapping_distance = g_value_get_uint64 (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_timeline_dispose (GObject * object)
+{
+ GESTimeline *tl = GES_TIMELINE (object);
+ GESTimelinePrivate *priv = tl->priv;
+ GList *tmp, *groups;
+
+ while (tl->layers) {
+ GESLayer *layer = (GESLayer *) tl->layers->data;
+ ges_timeline_remove_layer (GES_TIMELINE (object), layer);
+ }
+
+ /* FIXME: it should be possible to remove tracks before removing
+ * layers, but at the moment this creates a problem because the track
+ * objects aren't notified that their nleobjects have been destroyed.
+ */
+
+ while (tl->tracks)
+ ges_timeline_remove_track (GES_TIMELINE (object), tl->tracks->data);
+
+ groups = g_list_copy (priv->groups);
+ for (tmp = groups; tmp; tmp = tmp->next) {
+ GList *elems = ges_container_ungroup (tmp->data, FALSE);
+
+ g_list_free_full (elems, gst_object_unref);
+ }
+ g_list_free (priv->groups);
+ g_list_free (groups);
+
+ g_list_free_full (priv->auto_transitions, gst_object_unref);
+
+ g_hash_table_unref (priv->all_elements);
+
+ G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
+}
+
+static void
+ges_timeline_finalize (GObject * object)
+{
+ GESTimeline *tl = GES_TIMELINE (object);
+
+ g_rec_mutex_clear (&tl->priv->dyn_mutex);
+ g_node_destroy (tl->priv->tree);
+
+ G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
+}
+
+
+
+static void
+ges_timeline_handle_message (GstBin * bin, GstMessage * message)
+{
+ GESTimeline *timeline = GES_TIMELINE (bin);
+
+ if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) {
+ GstMessage *amessage = NULL;
+ const GstStructure *mstructure = gst_message_get_structure (message);
+
+ if (gst_structure_has_name (mstructure, "NleCompositionStartUpdate")) {
+ if (g_strcmp0 (gst_structure_get_string (mstructure, "reason"), "Seek")) {
+ GST_INFO_OBJECT (timeline,
+ "A composition is starting an update because of %s"
+ " not considering async", gst_structure_get_string (mstructure,
+ "reason"));
+
+ goto forward;
+ }
+
+ GST_OBJECT_LOCK (timeline);
+ if (timeline->priv->expected_async_done == 0) {
+ amessage = gst_message_new_async_start (GST_OBJECT_CAST (bin));
+ timeline->priv->expected_async_done = g_list_length (timeline->tracks);
+ GST_INFO_OBJECT (timeline, "Posting ASYNC_START %s",
+ gst_structure_get_string (mstructure, "reason"));
+ }
+ GST_OBJECT_UNLOCK (timeline);
+
+ } else if (gst_structure_has_name (mstructure, "NleCompositionUpdateDone")) {
+ if (g_strcmp0 (gst_structure_get_string (mstructure, "reason"), "Seek")) {
+ GST_INFO_OBJECT (timeline,
+ "A composition is done updating because of %s"
+ " not considering async", gst_structure_get_string (mstructure,
+ "reason"));
+
+ goto forward;
+ }
+
+ GST_OBJECT_LOCK (timeline);
+ timeline->priv->expected_async_done -= 1;
+ if (timeline->priv->expected_async_done == 0) {
+ amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin),
+ GST_CLOCK_TIME_NONE);
+ GST_INFO_OBJECT (timeline, "Posting ASYNC_DONE %s",
+ gst_structure_get_string (mstructure, "reason"));
+ }
+ GST_OBJECT_UNLOCK (timeline);
+ }
+
+ if (amessage)
+ gst_element_post_message (GST_ELEMENT_CAST (bin), amessage);
+ }
+
+forward:
+ gst_element_post_message (GST_ELEMENT_CAST (bin), message);
+}
+
+/* we collect the first result */
+static gboolean
+_gst_array_accumulator (GSignalInvocationHint * ihint,
+ GValue * return_accu, const GValue * handler_return, gpointer dummy)
+{
+ gpointer array;
+
+ array = g_value_get_boxed (handler_return);
+ if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
+ g_value_set_boxed (return_accu, array);
+
+ return FALSE;
+}
+
+static void
+ges_timeline_class_init (GESTimelineClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstBinClass *bin_class = GST_BIN_CLASS (klass);
+
+ GST_DEBUG_CATEGORY_INIT (ges_timeline_debug, "gestimeline",
+ GST_DEBUG_FG_YELLOW, "ges timeline");
+ timeline_tree_init_debug ();
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->get_property = ges_timeline_get_property;
+ object_class->set_property = ges_timeline_set_property;
+ object_class->dispose = ges_timeline_dispose;
+ object_class->finalize = ges_timeline_finalize;
+
+ bin_class->handle_message = GST_DEBUG_FUNCPTR (ges_timeline_handle_message);
+
+ /**
+ * GESTimeline:duration:
+ *
+ * Current duration (in nanoseconds) of the #GESTimeline
+ */
+ properties[PROP_DURATION] =
+ g_param_spec_uint64 ("duration", "Duration",
+ "The duration of the timeline", 0, G_MAXUINT64,
+ GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_DURATION,
+ properties[PROP_DURATION]);
+
+ /**
+ * GESTimeline:auto-transition:
+ *
+ * Sets whether transitions are added automagically when clips overlap.
+ */
+ g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
+ g_param_spec_boolean ("auto-transition", "Auto-Transition",
+ "whether the transitions are added", FALSE, G_PARAM_READWRITE));
+
+ /**
+ * GESTimeline:snapping-distance:
+ *
+ * Distance (in nanoseconds) from which a moving object will snap
+ * with it neighboors. 0 means no snapping.
+ */
+ properties[PROP_SNAPPING_DISTANCE] =
+ g_param_spec_uint64 ("snapping-distance", "Snapping distance",
+ "Distance from which moving an object will snap with neighboors", 0,
+ G_MAXUINT64, 0, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_SNAPPING_DISTANCE,
+ properties[PROP_SNAPPING_DISTANCE]);
+
+ /**
+ * GESTimeline::track-added:
+ * @timeline: the #GESTimeline
+ * @track: the #GESTrack that was added to the timeline
+ *
+ * Will be emitted after the track was added to the timeline.
+ */
+ ges_timeline_signals[TRACK_ADDED] =
+ g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
+ NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
+
+ /**
+ * GESTimeline::track-removed:
+ * @timeline: the #GESTimeline
+ * @track: the #GESTrack that was removed from the timeline
+ *
+ * Will be emitted after the track was removed from the timeline.
+ */
+ ges_timeline_signals[TRACK_REMOVED] =
+ g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
+
+ /**
+ * GESTimeline::layer-added:
+ * @timeline: the #GESTimeline
+ * @layer: the #GESLayer that was added to the timeline
+ *
+ * Will be emitted after a new layer is added to the timeline.
+ */
+ ges_timeline_signals[LAYER_ADDED] =
+ g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
+ NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_LAYER);
+
+ /**
+ * GESTimeline::layer-removed:
+ * @timeline: the #GESTimeline
+ * @layer: the #GESLayer that was removed from the timeline
+ *
+ * Will be emitted after the layer was removed from the timeline.
+ */
+ ges_timeline_signals[LAYER_REMOVED] =
+ g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_LAYER);
+
+ /**
+ * GESTimeline::group-added
+ * @timeline: the #GESTimeline
+ * @group: the #GESGroup
+ *
+ * Will be emitted after a new group is added to to the timeline.
+ */
+ ges_timeline_signals[GROUP_ADDED] =
+ g_signal_new ("group-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, group_added), NULL,
+ NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_GROUP);
+
+ /**
+ * GESTimeline::group-removed
+ * @timeline: the #GESTimeline
+ * @group: the #GESGroup
+ * @children: (element-type GES.Container) (transfer container): a list of #GESContainer
+ *
+ * Will be emitted after a group has been removed from the timeline.
+ */
+ ges_timeline_signals[GROUP_REMOVED] =
+ g_signal_new ("group-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, group_removed),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GES_TYPE_GROUP,
+ G_TYPE_PTR_ARRAY);
+
+ /**
+ * GESTimeline::snapping-started:
+ * @timeline: the #GESTimeline
+ * @obj1: the first #GESTrackElement that was snapping.
+ * @obj2: the second #GESTrackElement that was snapping.
+ * @position: the position where the two objects finally snapping.
+ *
+ * Will be emitted when the 2 #GESTrackElement first snapped
+ */
+ ges_timeline_signals[SNAPING_STARTED] =
+ g_signal_new ("snapping-started", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 3, GES_TYPE_TRACK_ELEMENT, GES_TYPE_TRACK_ELEMENT,
+ G_TYPE_UINT64);
+
+ /**
+ * GESTimeline::snapping-ended:
+ * @timeline: the #GESTimeline
+ * @obj1: the first #GESTrackElement that was snapping.
+ * @obj2: the second #GESTrackElement that was snapping.
+ * @position: the position where the two objects finally snapping.
+ *
+ * Will be emitted when the 2 #GESTrackElement ended to snap
+ */
+ ges_timeline_signals[SNAPING_ENDED] =
+ g_signal_new ("snapping-ended", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 3, GES_TYPE_TRACK_ELEMENT, GES_TYPE_TRACK_ELEMENT,
+ G_TYPE_UINT64);
+
+ /**
+ * GESTimeline::select-tracks-for-object:
+ * @timeline: the #GESTimeline
+ * @clip: The #GESClip on which @track_element will land
+ * @track_element: The #GESTrackElement for which to choose the tracks it should land into
+ *
+ * Returns: (transfer full) (element-type GESTrack): a #GPtrArray of #GESTrack-s where that object should be added
+ */
+ ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT] =
+ g_signal_new ("select-tracks-for-object", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, _gst_array_accumulator, NULL, NULL,
+ G_TYPE_PTR_ARRAY, 2, GES_TYPE_CLIP, GES_TYPE_TRACK_ELEMENT);
+
+ /**
+ * GESTimeline::commited:
+ * @timeline: the #GESTimeline
+ *
+ * This signal will be emitted once the changes initiated by #ges_timeline_commit
+ * have been executed in the backend. Use #ges_timeline_commit_sync if you
+ * don't need to do anything in the meantime.
+ */
+ ges_timeline_signals[COMMITED] =
+ g_signal_new ("commited", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+ges_timeline_init (GESTimeline * self)
+{
+ GESTimelinePrivate *priv = self->priv;
+
+ self->priv = ges_timeline_get_instance_private (self);
+ self->priv->tree = g_node_new (self);
+
+ priv = self->priv;
+ self->layers = NULL;
+ self->tracks = NULL;
+ self->priv->duration = 0;
+ self->priv->auto_transition = FALSE;
+ priv->snapping_distance = 0;
+ priv->expected_async_done = 0;
+ priv->expected_commited = 0;
+
+ self->priv->last_snap_ts = GST_CLOCK_TIME_NONE;
+
+ priv->priv_tracks = NULL;
+ priv->needs_transitions_update = TRUE;
+
+ priv->all_elements =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gst_object_unref);
+
+ priv->stream_start_group_id = -1;
+
+ g_signal_connect_after (self, "select-tracks-for-object",
+ G_CALLBACK (select_tracks_for_object_default), NULL);
+
+ g_rec_mutex_init (&priv->dyn_mutex);
+ g_mutex_init (&priv->commited_lock);
+ priv->valid_thread = g_thread_self ();
+}
+
+/* Private methods */
+
+static inline GESContainer *
+get_toplevel_container (gpointer element)
+{
+ GESTimelineElement *ret =
+ ges_timeline_element_get_toplevel_parent ((GESTimelineElement
+ *) (element));
+
+ /* We own a ref to the elements ourself */
+ gst_object_unref (ret);
+ return (GESContainer *) ret;
+}
+
+/* Sorting utils*/
+static gint
+sort_layers (gpointer a, gpointer b)
+{
+ GESLayer *layer_a, *layer_b;
+ guint prio_a, prio_b;
+
+ layer_a = GES_LAYER (a);
+ layer_b = GES_LAYER (b);
+
+ prio_a = ges_layer_get_priority (layer_a);
+ prio_b = ges_layer_get_priority (layer_b);
+
+ if ((gint) prio_a > (guint) prio_b)
+ return 1;
+ if ((guint) prio_a < (guint) prio_b)
+ return -1;
+
+ return 0;
+}
+
+static void
+_resync_layers (GESTimeline * timeline)
+{
+ GList *tmp;
+ gint i = 0;
+
+ timeline->priv->resyncing_layers = TRUE;
+ for (tmp = timeline->layers; tmp; tmp = tmp->next) {
+ layer_set_priority (tmp->data, i, TRUE);
+ i++;
+ }
+ timeline->priv->resyncing_layers = FALSE;
+}
+
+void
+timeline_update_duration (GESTimeline * timeline)
+{
+ GstClockTime duration = timeline_tree_get_duration (timeline->priv->tree);
+
+ if (timeline->priv->duration != duration) {
+ GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (duration),
+ GST_TIME_ARGS (timeline->priv->duration));
+
+ timeline->priv->duration = duration;
+
+ g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
+ }
+}
+
+static gint
+custom_find_track (TrackPrivate * tr_priv, GESTrack * track)
+{
+ if (tr_priv->track == track)
+ return 0;
+ return -1;
+}
+
+static void
+_destroy_auto_transition_cb (GESAutoTransition * auto_transition,
+ GESTimeline * timeline)
+{
+ GESTimelinePrivate *priv = timeline->priv;
+ GESClip *transition = auto_transition->transition_clip;
+ GESLayer *layer = ges_clip_get_layer (transition);
+
+ ges_layer_remove_clip (layer, transition);
+ g_signal_handlers_disconnect_by_func (auto_transition,
+ _destroy_auto_transition_cb, timeline);
+
+ priv->auto_transitions =
+ g_list_remove (priv->auto_transitions, auto_transition);
+ gst_object_unref (auto_transition);
+}
+
+GESAutoTransition *
+ges_timeline_create_transition (GESTimeline * timeline,
+ GESTrackElement * previous, GESTrackElement * next, GESClip * transition,
+ GESLayer * layer, guint64 start, guint64 duration)
+{
+ GESAsset *asset;
+ GESAutoTransition *auto_transition;
+
+ if (transition == NULL) {
+ /* TODO make it possible to specify a Transition asset in the API */
+ asset = ges_asset_request (GES_TYPE_TRANSITION_CLIP, "crossfade", NULL);
+ transition =
+ ges_layer_add_asset (layer, asset, start, 0, duration,
+ ges_track_element_get_track_type (next));
+ g_object_unref (asset);
+ } else {
+ GST_DEBUG_OBJECT (timeline,
+ "Reusing already existing transition: %" GST_PTR_FORMAT, transition);
+ }
+
+ /* We know there is only 1 TrackElement */
+ auto_transition =
+ ges_auto_transition_new (GES_CONTAINER_CHILDREN (transition)->data,
+ previous, next);
+
+ g_signal_connect (auto_transition, "destroy-me",
+ G_CALLBACK (_destroy_auto_transition_cb), timeline);
+
+ timeline->priv->auto_transitions =
+ g_list_prepend (timeline->priv->auto_transitions, auto_transition);
+
+ return auto_transition;
+}
+
+GESAutoTransition *
+ges_timeline_find_auto_transition (GESTimeline * timeline,
+ GESTrackElement * prev, GESTrackElement * next,
+ GstClockTime transition_duration)
+{
+ GList *tmp;
+
+ for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
+ GESAutoTransition *auto_trans = (GESAutoTransition *) tmp->data;
+
+ /* We already have a transition linked to one of the elements we want to
+ * find a transition for */
+ if (auto_trans->previous_source == prev || auto_trans->next_source == next) {
+ if (auto_trans->previous_source != prev
+ || auto_trans->next_source != next) {
+ GST_ERROR_OBJECT (timeline, "Failed creating auto transition, "
+ " trying to have 3 clips overlapping, rolling back");
+ }
+
+ return auto_trans;
+ }
+ }
+
+ return NULL;
+}
+
+static GESAutoTransition *
+_create_auto_transition_from_transitions (GESTimeline * timeline,
+ GESTrackElement * prev, GESTrackElement * next,
+ GstClockTime transition_duration)
+{
+ GList *tmp, *elements;
+ GESLayer *layer;
+ guint32 layer_prio = GES_TIMELINE_ELEMENT_LAYER_PRIORITY (prev);
+ GESTrack *track;
+ GESAutoTransition *auto_transition =
+ ges_timeline_find_auto_transition (timeline, prev, next,
+ transition_duration);
+
+ if (auto_transition)
+ return auto_transition;
+
+ layer = ges_timeline_get_layer (timeline, layer_prio);
+ track = ges_track_element_get_track (prev);
+ elements = ges_track_get_elements (track);
+ for (tmp = elements; tmp; tmp = tmp->next) {
+ GESTrackElement *maybe_transition = tmp->data;
+
+ if (ges_timeline_element_get_layer_priority (tmp->data) != layer_prio)
+ continue;
+
+ if (_START (maybe_transition) > _START (next))
+ break;
+ else if (_START (maybe_transition) != _START (next) ||
+ _DURATION (maybe_transition) != transition_duration)
+ continue;
+ else if (GES_IS_TRANSITION (maybe_transition)) {
+ /* Use that transition */
+ /* TODO We should make sure that the transition contains only
+ * TrackElement-s in @track and if it is not the case properly unlink the
+ * object to use it */
+ auto_transition = ges_timeline_create_transition (timeline, prev, next,
+ GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (maybe_transition)), layer,
+ _START (next), transition_duration);
+
+ break;
+ }
+ }
+ gst_object_unref (layer);
+ g_list_free_full (elements, gst_object_unref);
+
+ return auto_transition;
+}
+
+void
+ges_timeline_emit_snapping (GESTimeline * timeline, GESTimelineElement * elem1,
+ GESTimelineElement * elem2, GstClockTime snap_time)
+{
+ GESTimelinePrivate *priv = timeline->priv;
+ GstClockTime last_snap_ts = timeline->priv->last_snap_ts;
+
+ if (!GST_CLOCK_TIME_IS_VALID (snap_time)) {
+ if (priv->last_snaped1 != NULL && priv->last_snaped2 != NULL) {
+ g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
+ priv->last_snaped1, priv->last_snaped2, last_snap_ts);
+ priv->last_snaped1 = NULL;
+ priv->last_snaped2 = NULL;
+ priv->last_snap_ts = GST_CLOCK_TIME_NONE;
+ }
+
+ return;
+ }
+
+ g_assert (elem1 != elem2);
+ if (GES_IS_CLIP (elem1)) {
+ g_assert (GES_CONTAINER_CHILDREN (elem1));
+ elem1 = GES_CONTAINER_CHILDREN (elem1)->data;
+ }
+
+ if (GES_IS_CLIP (elem2)) {
+ g_assert (GES_CONTAINER_CHILDREN (elem2));
+ elem2 = GES_CONTAINER_CHILDREN (elem2)->data;
+ }
+
+ if (last_snap_ts != snap_time) {
+ g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
+ priv->last_snaped1, priv->last_snaped2, (last_snap_ts));
+
+ /* We want the snap start signal to be emited anyway */
+ timeline->priv->last_snap_ts = GST_CLOCK_TIME_NONE;
+ }
+
+ if (!GST_CLOCK_TIME_IS_VALID (timeline->priv->last_snap_ts)) {
+ priv->last_snaped1 = (GESTrackElement *) elem1;
+ priv->last_snaped2 = (GESTrackElement *) elem2;
+ timeline->priv->last_snap_ts = snap_time;
+
+ g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
+ elem1, elem2, snap_time);
+ }
+
+}
+
+gboolean
+ges_timeline_trim_object_simple (GESTimeline * timeline,
+ GESTimelineElement * element, guint32 new_layer_priority,
+ GList * layers, GESEdge edge, guint64 position, gboolean snapping)
+{
+
+ return timeline_trim_object (timeline, element, new_layer_priority, layers,
+ edge, position);
+}
+
+gboolean
+timeline_ripple_object (GESTimeline * timeline, GESTimelineElement * obj,
+ gint new_layer_priority, GList * layers, GESEdge edge, guint64 position)
+{
+ gboolean res = TRUE;
+ guint64 new_duration;
+ GstClockTimeDiff diff;
+
+ switch (edge) {
+ case GES_EDGE_NONE:
+ GST_DEBUG ("Simply rippling");
+ diff = GST_CLOCK_DIFF (position, _START (obj));
+
+ timeline->priv->needs_transitions_update = FALSE;
+ res = timeline_tree_ripple (timeline->priv->tree,
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (obj) -
+ (gint64) new_layer_priority, diff, obj,
+ GES_EDGE_NONE, timeline->priv->snapping_distance);
+ timeline->priv->needs_transitions_update = TRUE;
+
+ break;
+ case GES_EDGE_END:
+ GST_DEBUG ("Rippling end");
+
+ timeline->priv->needs_transitions_update = FALSE;
+ new_duration =
+ CLAMP (position - obj->start, 0, obj->maxduration - obj->inpoint);
+ res =
+ timeline_tree_ripple (timeline->priv->tree,
+ (gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (obj) -
+ (gint64) new_layer_priority,
+ _DURATION (obj) - new_duration, obj,
+ GES_EDGE_END, timeline->priv->snapping_distance);
+ timeline->priv->needs_transitions_update = TRUE;
+
+ GST_DEBUG ("Done Rippling end");
+ break;
+ case GES_EDGE_START:
+ GST_INFO ("Ripple start doesn't make sense, trimming instead");
+ if (!timeline_trim_object (timeline, obj, -1, layers, edge, position))
+ goto error;
+ break;
+ default:
+ GST_DEBUG ("Can not ripple edge: %i", edge);
+
+ break;
+ }
+
+ return res;
+
+error:
+
+ return FALSE;
+}
+
+gboolean
+timeline_slide_object (GESTimeline * timeline, GESTrackElement * obj,
+ GList * layers, GESEdge edge, guint64 position)
+{
+
+ /* FIXME implement me! */
+ GST_FIXME_OBJECT (timeline, "Slide mode editing not implemented yet");
+
+ return FALSE;
+}
+
+static gboolean
+_trim_transition (GESTimeline * timeline, GESTimelineElement * element,
+ GESEdge edge, GstClockTime position)
+{
+ GList *tmp;
+ GESLayer *layer = ges_timeline_get_layer (timeline,
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element));
+
+ if (!ges_layer_get_auto_transition (layer))
+ goto fail;
+
+ gst_object_unref (layer);
+ for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
+ GESAutoTransition *auto_transition = tmp->data;
+
+ if (GES_TIMELINE_ELEMENT (auto_transition->transition) == element ||
+ GES_TIMELINE_ELEMENT (auto_transition->transition_clip) == element) {
+ /* Trimming an auto transition means trimming its neighboors */
+ if (!auto_transition->positioning) {
+ if (edge == GES_EDGE_END) {
+ ges_container_edit (GES_CONTAINER (auto_transition->previous_clip),
+ NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, position);
+ } else {
+ ges_container_edit (GES_CONTAINER (auto_transition->next_clip),
+ NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_START, position);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+
+fail:
+ gst_object_unref (layer);
+ return FALSE;
+}
+
+
+gboolean
+timeline_trim_object (GESTimeline * timeline, GESTimelineElement * object,
+ guint32 new_layer_priority, GList * layers, GESEdge edge, guint64 position)
+{
+ if ((GES_IS_TRANSITION (object) || GES_IS_TRANSITION_CLIP (object)) &&
+ !ELEMENT_FLAG_IS_SET (object, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+ return _trim_transition (timeline, object, edge, position);
+ }
+
+ return timeline_tree_trim (timeline->priv->tree,
+ GES_TIMELINE_ELEMENT (object), new_layer_priority > 0 ? (gint64)
+ ges_timeline_element_get_layer_priority (GES_TIMELINE_ELEMENT (object)) -
+ new_layer_priority : 0, edge == GES_EDGE_END ? GST_CLOCK_DIFF (position,
+ _START (object) + _DURATION (object)) : GST_CLOCK_DIFF (position,
+ GES_TIMELINE_ELEMENT_START (object)), edge,
+ timeline->priv->snapping_distance);
+}
+
+gboolean
+timeline_roll_object (GESTimeline * timeline, GESTimelineElement * element,
+ GList * layers, GESEdge edge, guint64 position)
+{
+ return timeline_tree_roll (timeline->priv->tree,
+ element,
+ (edge == GES_EDGE_END) ?
+ GST_CLOCK_DIFF (position, _END (element)) :
+ GST_CLOCK_DIFF (position, _START (element)),
+ edge, timeline->priv->snapping_distance);
+}
+
+gboolean
+timeline_move_object (GESTimeline * timeline, GESTimelineElement * object,
+ guint32 new_layer_priority, GList * layers, GESEdge edge, guint64 position)
+{
+ gboolean ret = FALSE;
+ GstClockTimeDiff offset = edge == GES_EDGE_END ?
+ GST_CLOCK_DIFF (position, _START (object) + _DURATION (object)) :
+ GST_CLOCK_DIFF (position, GES_TIMELINE_ELEMENT_START (object));
+
+ ret = timeline_tree_move (timeline->priv->tree,
+ GES_TIMELINE_ELEMENT (object), new_layer_priority < 0 ? 0 : (gint64)
+ ges_timeline_element_get_layer_priority (GES_TIMELINE_ELEMENT (object)) -
+ new_layer_priority, offset, edge, timeline->priv->snapping_distance);
+
+ return ret;
+}
+
+gboolean
+ges_timeline_move_object_simple (GESTimeline * timeline,
+ GESTimelineElement * element, GList * layers, GESEdge edge,
+ guint64 position)
+{
+ return timeline_move_object (timeline, element,
+ ges_timeline_element_get_layer_priority (element), NULL, edge, position);
+}
+
+void
+timeline_add_group (GESTimeline * timeline, GESGroup * group)
+{
+ GST_DEBUG_OBJECT (timeline, "Adding group %" GST_PTR_FORMAT, group);
+
+ timeline->priv->groups = g_list_prepend (timeline->priv->groups,
+ gst_object_ref_sink (group));
+
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), timeline);
+}
+
+void
+timeline_update_transition (GESTimeline * timeline)
+{
+ GList *tmp, *auto_transs;
+
+ auto_transs = g_list_copy (timeline->priv->auto_transitions);
+ for (tmp = auto_transs; tmp; tmp = tmp->next)
+ ges_auto_transition_update (tmp->data);
+ g_list_free (auto_transs);
+}
+
+/**
+ * timeline_emit_group_added:
+ * @timeline: a #GESTimeline
+ * @group: group that was added
+ *
+ * Emit group-added signal.
+ */
+void
+timeline_emit_group_added (GESTimeline * timeline, GESGroup * group)
+{
+ g_signal_emit (timeline, ges_timeline_signals[GROUP_ADDED], 0, group);
+}
+
+/**
+ * timeline_emit_group_removed:
+ * @timeline: a #GESTimeline
+ * @group: group that was removed
+ *
+ * Emit group-removed signal.
+ */
+void
+timeline_emit_group_removed (GESTimeline * timeline, GESGroup * group,
+ GPtrArray * array)
+{
+ g_signal_emit (timeline, ges_timeline_signals[GROUP_REMOVED], 0, group,
+ array);
+}
+
+void
+timeline_remove_group (GESTimeline * timeline, GESGroup * group)
+{
+ GST_DEBUG_OBJECT (timeline, "Removing group %" GST_PTR_FORMAT, group);
+
+ timeline->priv->groups = g_list_remove (timeline->priv->groups, group);
+
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), NULL);
+ gst_object_unref (group);
+}
+
+static GPtrArray *
+select_tracks_for_object_default (GESTimeline * timeline,
+ GESClip * clip, GESTrackElement * tr_object, gpointer user_data)
+{
+ GPtrArray *result;
+ GList *tmp;
+
+ result = g_ptr_array_new ();
+
+ for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
+ GESTrack *track = GES_TRACK (tmp->data);
+
+ if ((track->type & ges_track_element_get_track_type (tr_object))) {
+ gst_object_ref (track);
+ g_ptr_array_add (result, track);
+ }
+ }
+
+ return result;
+}
+
+static void
+add_object_to_tracks (GESTimeline * timeline, GESClip * clip, GESTrack * track)
+{
+ gint i;
+ GList *tmp, *list;
+ GESTrackType types, visited_type = GES_TRACK_TYPE_UNKNOWN;
+
+ GST_DEBUG_OBJECT (timeline, "Creating %" GST_PTR_FORMAT
+ " trackelements and adding them to our tracks", clip);
+
+ types = ges_clip_get_supported_formats (clip);
+ if (track) {
+ if ((types & track->type) == 0)
+ return;
+ types = track->type;
+ }
+
+ for (i = 0, tmp = timeline->tracks; tmp; tmp = tmp->next, i++) {
+ GESTrack *track = GES_TRACK (tmp->data);
+
+ if (((track->type & types) == 0 || (track->type & visited_type)))
+ continue;
+
+ list = ges_clip_create_track_elements (clip, track->type);
+ g_list_free (list);
+ }
+}
+
+static void
+layer_auto_transition_changed_cb (GESLayer * layer,
+ GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
+{
+ GList *tmp, *clips;
+
+ timeline_tree_create_transitions (timeline->priv->tree,
+ _create_auto_transition_from_transitions);
+ clips = ges_layer_get_clips (layer);
+ for (tmp = clips; tmp; tmp = tmp->next) {
+ if (GES_IS_TRANSITION_CLIP (tmp->data)) {
+ GList *tmpautotrans;
+ gboolean found = FALSE;
+
+ for (tmpautotrans = timeline->priv->auto_transitions; tmpautotrans;
+ tmpautotrans = tmpautotrans->next) {
+ if (GES_AUTO_TRANSITION (tmpautotrans->data)->transition_clip ==
+ tmp->data) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ GST_ERROR_OBJECT (timeline,
+ "Transition %s could not be wrapped into an auto transition"
+ " REMOVING it", GES_TIMELINE_ELEMENT_NAME (tmp->data));
+
+ ges_layer_remove_clip (layer, tmp->data);
+ }
+ }
+ }
+ g_list_free_full (clips, gst_object_unref);
+}
+
+static void
+clip_track_element_added_cb (GESClip * clip,
+ GESTrackElement * track_element, GESTimeline * timeline)
+{
+ guint i;
+ GESTrack *track;
+ gboolean is_source;
+ GPtrArray *tracks = NULL;
+ GESTrackElement *existing_src = NULL;
+
+ if (timeline->priv->ignore_track_element_added == clip) {
+ GST_DEBUG_OBJECT (timeline, "Ignoring element added (%" GST_PTR_FORMAT
+ " in %" GST_PTR_FORMAT, track_element, clip);
+
+ return;
+ }
+
+ if (ges_track_element_get_track (track_element)) {
+ GST_WARNING_OBJECT (track_element, "Already in a track");
+
+ return;
+ }
+
+ g_signal_emit (G_OBJECT (timeline),
+ ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT], 0, clip, track_element,
+ &tracks);
+
+ if (!tracks || tracks->len == 0) {
+ GST_WARNING_OBJECT (timeline, "Got no Track to add %p (type %s), removing"
+ " from clip (stopping 'child-added' signal emission).",
+ track_element, ges_track_type_name (ges_track_element_get_track_type
+ (track_element)));
+
+ if (tracks)
+ g_ptr_array_unref (tracks);
+
+ g_signal_stop_emission_by_name (clip, "child-added");
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element));
+
+ return;
+ }
+
+ /* We add the current element to the first track */
+ track = g_ptr_array_index (tracks, 0);
+
+ is_source = g_type_is_a (G_OBJECT_TYPE (track_element), GES_TYPE_SOURCE);
+ if (is_source)
+ existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
+
+ if (existing_src == NULL) {
+ if (!ges_track_add_element (track, track_element)) {
+ GST_WARNING_OBJECT (clip, "Failed to add track element to track");
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element));
+ g_ptr_array_unref (tracks);
+ return;
+ }
+ } else {
+ GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
+ " of type %s, removing new one. (stopping 'child-added' emission)",
+ track, G_OBJECT_TYPE_NAME (track_element));
+ g_signal_stop_emission_by_name (clip, "child-added");
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element));
+ }
+ gst_object_unref (track);
+ g_clear_object (&existing_src);
+
+ /* And create copies to add to other tracks */
+ timeline->priv->ignore_track_element_added = clip;
+ for (i = 1; i < tracks->len; i++) {
+ GESTrack *track;
+ GESTrackElement *track_element_copy;
+
+ track = g_ptr_array_index (tracks, i);
+ if (is_source)
+ existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
+ if (existing_src == NULL) {
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element));
+ gst_object_unref (track);
+ g_ptr_array_unref (tracks);
+ continue;
+ } else {
+ GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
+ " of type %s, removing new one. (stopping 'child-added' emission)",
+ track, G_OBJECT_TYPE_NAME (track_element));
+ g_signal_stop_emission_by_name (clip, "child-added");
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element));
+ }
+ g_clear_object (&existing_src);
+
+ track_element_copy =
+ GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
+ (track_element), TRUE));
+
+ GST_LOG_OBJECT (timeline, "Trying to add %p to track %p",
+ track_element_copy, track);
+
+ if (!ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element_copy))) {
+ GST_WARNING_OBJECT (clip, "Failed to add track element to clip");
+ gst_object_unref (track_element_copy);
+ g_ptr_array_unref (tracks);
+ return;
+ }
+
+ if (!ges_track_add_element (track, track_element_copy)) {
+ GST_WARNING_OBJECT (clip, "Failed to add track element to track");
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (track_element_copy));
+ gst_object_unref (track_element_copy);
+ g_ptr_array_unref (tracks);
+ return;
+ }
+
+ gst_object_unref (track);
+ }
+ timeline->priv->ignore_track_element_added = NULL;
+ g_ptr_array_unref (tracks);
+ if (GES_IS_SOURCE (track_element))
+ timeline_tree_create_transitions (timeline->priv->tree,
+ ges_timeline_find_auto_transition);
+}
+
+static void
+clip_track_element_removed_cb (GESClip * clip,
+ GESTrackElement * track_element, GESTimeline * timeline)
+{
+ GESTrack *track = ges_track_element_get_track (track_element);
+
+ if (track)
+ ges_track_remove_element (track, track_element);
+}
+
+static void
+layer_object_added_cb (GESLayer * layer, GESClip * clip, GESTimeline * timeline)
+{
+ GESProject *project;
+
+ /* We make sure not to be connected twice */
+ g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
+ timeline);
+ g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
+ timeline);
+
+ /* And we connect to the object */
+ g_signal_connect (clip, "child-added",
+ G_CALLBACK (clip_track_element_added_cb), timeline);
+ g_signal_connect (clip, "child-removed",
+ G_CALLBACK (clip_track_element_removed_cb), timeline);
+
+ if (ges_clip_is_moving_from_layer (clip)) {
+ GST_DEBUG ("Clip %p moving from one layer to another, not creating "
+ "TrackElement", clip);
+ timeline_tree_create_transitions (timeline->priv->tree,
+ ges_timeline_find_auto_transition);
+ return;
+ }
+
+ add_object_to_tracks (timeline, clip, NULL);
+
+ GST_DEBUG ("Making sure that the asset is in our project");
+ project =
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
+ ges_project_add_asset (project,
+ ges_extractable_get_asset (GES_EXTRACTABLE (clip)));
+
+ GST_DEBUG ("Done");
+}
+
+static void
+layer_priority_changed_cb (GESLayer * layer,
+ GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
+{
+ if (timeline->priv->resyncing_layers)
+ return;
+
+ timeline->layers = g_list_sort (timeline->layers, (GCompareFunc)
+ sort_layers);
+}
+
+static void
+layer_object_removed_cb (GESLayer * layer, GESClip * clip,
+ GESTimeline * timeline)
+{
+ GList *trackelements, *tmp;
+
+ if (ges_clip_is_moving_from_layer (clip)) {
+ GST_DEBUG ("Clip %p is moving from a layer to another, not doing"
+ " anything on it", clip);
+ return;
+ }
+
+ GST_DEBUG ("Clip %p removed from layer %p", clip, layer);
+
+ /* Go over the clip's track element and figure out which one belongs to
+ * the list of tracks we control */
+
+ trackelements = ges_container_get_children (GES_CONTAINER (clip), FALSE);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ GESTrackElement *track_element = (GESTrackElement *) tmp->data;
+ GESTrack *track = ges_track_element_get_track (track_element);
+
+ if (!track)
+ continue;
+
+ GST_DEBUG_OBJECT (timeline, "Trying to remove TrackElement %p",
+ track_element);
+
+ /* FIXME Check if we should actually check that we control the
+ * track in the new management of TrackElement context */
+ LOCK_DYN (timeline);
+ if (G_LIKELY (g_list_find_custom (timeline->priv->priv_tracks, track,
+ (GCompareFunc) custom_find_track) || track == NULL)) {
+ GST_DEBUG ("Belongs to one of the tracks we control");
+
+ ges_track_remove_element (track, track_element);
+ }
+ UNLOCK_DYN (timeline);
+ }
+ g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
+ timeline);
+ g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
+ timeline);
+
+ g_list_free_full (trackelements, gst_object_unref);
+
+ GST_DEBUG ("Done");
+}
+
+static void
+trackelement_start_changed_cb (GESTrackElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
+{
+ timeline_update_duration (timeline);
+}
+
+static void
+track_element_added_cb (GESTrack * track, GESTrackElement * track_element,
+ GESTimeline * timeline)
+{
+ /* Auto transition should be updated before we receive the signal */
+ g_signal_connect_after (GES_TRACK_ELEMENT (track_element), "notify::start",
+ G_CALLBACK (trackelement_start_changed_cb), timeline);
+}
+
+static void
+track_element_removed_cb (GESTrack * track,
+ GESTrackElement * track_element, GESTimeline * timeline)
+{
+ /* Disconnect all signal handlers */
+ g_signal_handlers_disconnect_by_func (track_element,
+ trackelement_start_changed_cb, timeline);
+}
+
+static GstPadProbeReturn
+_pad_probe_cb (GstPad * mixer_pad, GstPadProbeInfo * info,
+ GESTimeline * timeline)
+{
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+ if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START) {
+ LOCK_DYN (timeline);
+ if (timeline->priv->stream_start_group_id == -1) {
+ if (!gst_event_parse_group_id (event,
+ &timeline->priv->stream_start_group_id))
+ timeline->priv->stream_start_group_id = gst_util_group_id_next ();
+ }
+
+ info->data = gst_event_make_writable (event);
+ gst_event_set_group_id (GST_PAD_PROBE_INFO_EVENT (info),
+ timeline->priv->stream_start_group_id);
+ UNLOCK_DYN (timeline);
+
+ return GST_PAD_PROBE_REMOVE;
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
+static void
+_ghost_track_srcpad (TrackPrivate * tr_priv)
+{
+ GstPad *pad;
+ gchar *padname;
+ gboolean no_more;
+ GList *tmp;
+ GESTrack *track = tr_priv->track;
+
+ pad = gst_element_get_static_pad (GST_ELEMENT (track), "src");
+
+ GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
+
+ /* Remember the pad */
+ LOCK_DYN (tr_priv->timeline);
+ GST_OBJECT_LOCK (track);
+ tr_priv->pad = pad;
+
+ no_more = TRUE;
+ for (tmp = tr_priv->timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
+ TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
+
+ if (!tr_priv->pad) {
+ GST_LOG ("Found track without pad %p", tr_priv->track);
+ no_more = FALSE;
+ }
+ }
+ GST_OBJECT_UNLOCK (track);
+
+ /* ghost it ! */
+ GST_DEBUG ("Ghosting pad and adding it to ourself");
+ padname = g_strdup_printf ("track_%p_src", track);
+ tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
+ g_free (padname);
+ gst_pad_set_active (tr_priv->ghostpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
+
+ if (no_more) {
+ GST_DEBUG ("Signaling no-more-pads");
+ gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
+ }
+
+ tr_priv->probe_id = gst_pad_add_probe (pad,
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ (GstPadProbeCallback) _pad_probe_cb, tr_priv->timeline, NULL);
+
+ UNLOCK_DYN (tr_priv->timeline);
+}
+
+gboolean
+timeline_add_element (GESTimeline * timeline, GESTimelineElement * element)
+{
+ GESTimelineElement *same_name =
+ g_hash_table_lookup (timeline->priv->all_elements,
+ element->name);
+
+ GST_DEBUG_OBJECT (timeline, "Adding element: %s", element->name);
+ if (same_name) {
+ GST_ERROR_OBJECT (timeline, "%s Already in the timeline %" GST_PTR_FORMAT,
+ element->name, same_name);
+ return FALSE;
+ }
+
+ g_hash_table_insert (timeline->priv->all_elements,
+ ges_timeline_element_get_name (element), gst_object_ref (element));
+
+ timeline_tree_track_element (timeline->priv->tree, element);
+
+ return TRUE;
+}
+
+gboolean
+timeline_remove_element (GESTimeline * timeline, GESTimelineElement * element)
+{
+ if (g_hash_table_remove (timeline->priv->all_elements, element->name)) {
+ timeline_tree_stop_tracking_element (timeline->priv->tree, element);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+timeline_fill_gaps (GESTimeline * timeline)
+{
+ GList *tmp;
+
+ for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
+ track_resort_and_fill_gaps (tmp->data);
+ }
+}
+
+GNode *
+timeline_get_tree (GESTimeline * timeline)
+{
+ return timeline->priv->tree;
+}
+
+/**** API *****/
+/**
+ * ges_timeline_new:
+ *
+ * Creates a new empty #GESTimeline.
+ *
+ * Returns: (transfer floating): The new timeline.
+ */
+
+GESTimeline *
+ges_timeline_new (void)
+{
+ GESProject *project = ges_project_new (NULL);
+ GESExtractable *timeline = g_object_new (GES_TYPE_TIMELINE, NULL);
+
+ ges_extractable_set_asset (timeline, GES_ASSET (project));
+ gst_object_unref (project);
+
+ return GES_TIMELINE (timeline);
+}
+
+/**
+ * ges_timeline_new_from_uri:
+ * @uri: the URI to load from
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Creates a timeline from the given URI.
+ *
+ * Returns: (transfer floating) (nullable): A new timeline if the uri was loaded
+ * successfully, or %NULL if the uri could not be loaded.
+ */
+GESTimeline *
+ges_timeline_new_from_uri (const gchar * uri, GError ** error)
+{
+ GESTimeline *ret;
+ GESProject *project = ges_project_new (uri);
+
+ ret = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), error));
+ gst_object_unref (project);
+
+ return ret;
+}
+
+/**
+ * ges_timeline_load_from_uri:
+ * @timeline: an empty #GESTimeline into which to load the formatter
+ * @uri: The URI to load from
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Loads the contents of URI into the given timeline.
+ *
+ * Returns: %TRUE if the timeline was loaded successfully, or %FALSE if the uri
+ * could not be loaded.
+ */
+gboolean
+ges_timeline_load_from_uri (GESTimeline * timeline, const gchar * uri,
+ GError ** error)
+{
+ GESProject *project;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail ((ges_extractable_get_asset (GES_EXTRACTABLE
+ (timeline)) == NULL), FALSE);
+
+ project = ges_project_new (uri);
+ ret = ges_project_load (project, timeline, error);
+ gst_object_unref (project);
+
+ return ret;
+}
+
+/**
+ * ges_timeline_save_to_uri:
+ * @timeline: a #GESTimeline
+ * @uri: The location to save to
+ * @formatter_asset: (allow-none): The formatter asset to use or %NULL. If %NULL,
+ * will try to save in the same format as the one from which the timeline as been loaded
+ * or default to the formatter with highest rank
+ * @overwrite: %TRUE to overwrite file if it exists
+ * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Saves the timeline to the given location
+ *
+ * Returns: %TRUE if the timeline was successfully saved to the given location,
+ * else %FALSE.
+ */
+gboolean
+ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri,
+ GESAsset * formatter_asset, gboolean overwrite, GError ** error)
+{
+ GESProject *project;
+
+ gboolean ret, created_proj = FALSE;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ project =
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
+
+ if (project == NULL) {
+ project = ges_project_new (NULL);
+ created_proj = TRUE;
+ }
+
+ ret = ges_project_save (project, timeline, uri, formatter_asset, overwrite,
+ error);
+
+ if (created_proj)
+ gst_object_unref (project);
+
+ return ret;
+}
+
+/**
+ * ges_timeline_get_groups:
+ * @timeline: a #GESTimeline
+ *
+ * Get the list of #GESGroup present in the Timeline.
+ *
+ * Returns: (transfer none) (element-type GESGroup): the list of
+ * #GESGroup that contain clips present in the timeline's layers.
+ * Must not be changed.
+ */
+GList *
+ges_timeline_get_groups (GESTimeline * timeline)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+ CHECK_THREAD (timeline);
+
+ return timeline->priv->groups;
+}
+
+/**
+ * ges_timeline_append_layer:
+ * @timeline: a #GESTimeline
+ *
+ * Append a newly created #GESLayer to @timeline
+ * Note that you do not own any reference to the returned layer.
+ *
+ * Returns: (transfer none): The newly created #GESLayer, or the last (empty)
+ * #GESLayer of @timeline.
+ */
+GESLayer *
+ges_timeline_append_layer (GESTimeline * timeline)
+{
+ guint32 priority;
+ GESLayer *layer;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+ CHECK_THREAD (timeline);
+
+ layer = ges_layer_new ();
+ priority = g_list_length (timeline->layers);
+ ges_layer_set_priority (layer, priority);
+
+ ges_timeline_add_layer (timeline, layer);
+
+ return layer;
+}
+
+/**
+ * ges_timeline_add_layer:
+ * @timeline: a #GESTimeline
+ * @layer: (transfer floating): the #GESLayer to add
+ *
+ * Add the layer to the timeline. The reference to the @layer will be stolen
+ * by the @timeline.
+ *
+ * Returns: %TRUE if the layer was properly added, else %FALSE.
+ */
+gboolean
+ges_timeline_add_layer (GESTimeline * timeline, GESLayer * layer)
+{
+ gboolean auto_transition;
+ GList *objects, *tmp;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+ CHECK_THREAD (timeline);
+
+ GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
+
+ /* We can only add a layer that doesn't already belong to another timeline */
+ if (G_UNLIKELY (layer->timeline)) {
+ GST_WARNING ("Layer belongs to another timeline, can't add it");
+ gst_object_ref_sink (layer);
+ gst_object_unref (layer);
+ return FALSE;
+ }
+
+ /* Add to the list of layers, make sure we don't already control it */
+ if (G_UNLIKELY (g_list_find (timeline->layers, (gconstpointer) layer))) {
+ GST_WARNING ("Layer is already controlled by this timeline");
+ gst_object_ref_sink (layer);
+ gst_object_unref (layer);
+ return FALSE;
+ }
+
+ auto_transition = ges_layer_get_auto_transition (layer);
+
+ /* If the user doesn't explicitely set layer auto_transition, then set our */
+ if (!auto_transition) {
+ auto_transition = ges_timeline_get_auto_transition (timeline);
+ ges_layer_set_auto_transition (layer, auto_transition);
+ }
+
+ gst_object_ref_sink (layer);
+ timeline->layers = g_list_insert_sorted (timeline->layers, layer,
+ (GCompareFunc) sort_layers);
+
+ /* Inform the layer that it belongs to a new timeline */
+ ges_layer_set_timeline (layer, timeline);
+
+ /* Connect to 'clip-added'/'clip-removed' signal from the new layer */
+ g_signal_connect_after (layer, "clip-added",
+ G_CALLBACK (layer_object_added_cb), timeline);
+ g_signal_connect_after (layer, "clip-removed",
+ G_CALLBACK (layer_object_removed_cb), timeline);
+ g_signal_connect (layer, "notify::priority",
+ G_CALLBACK (layer_priority_changed_cb), timeline);
+ g_signal_connect (layer, "notify::auto-transition",
+ G_CALLBACK (layer_auto_transition_changed_cb), timeline);
+
+ GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
+ g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
+
+ /* add any existing clips to the timeline */
+ objects = ges_layer_get_clips (layer);
+ for (tmp = objects; tmp; tmp = tmp->next) {
+ layer_object_added_cb (layer, tmp->data, timeline);
+ gst_object_unref (tmp->data);
+ tmp->data = NULL;
+ }
+ g_list_free (objects);
+
+ return TRUE;
+}
+
+/**
+ * ges_timeline_remove_layer:
+ * @timeline: a #GESTimeline
+ * @layer: the #GESLayer to remove
+ *
+ * Removes the layer from the timeline. The reference that the @timeline holds on
+ * the layer will be dropped. If you wish to use the @layer after calling this
+ * method, you need to take a reference before calling.
+ *
+ * Returns: %TRUE if the layer was properly removed, else %FALSE.
+ */
+
+gboolean
+ges_timeline_remove_layer (GESTimeline * timeline, GESLayer * layer)
+{
+ GList *layer_objects, *tmp;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+ CHECK_THREAD (timeline);
+
+ GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
+
+ if (G_UNLIKELY (!g_list_find (timeline->layers, layer))) {
+ GST_WARNING ("Layer doesn't belong to this timeline");
+ return FALSE;
+ }
+
+ /* remove objects from any private data structures */
+
+ layer_objects = ges_layer_get_clips (layer);
+ for (tmp = layer_objects; tmp; tmp = tmp->next) {
+ layer_object_removed_cb (layer, GES_CLIP (tmp->data), timeline);
+ gst_object_unref (G_OBJECT (tmp->data));
+ tmp->data = NULL;
+ }
+ g_list_free (layer_objects);
+
+ /* Disconnect signals */
+ GST_DEBUG ("Disconnecting signal callbacks");
+ g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
+ g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
+ timeline);
+ g_signal_handlers_disconnect_by_func (layer, layer_priority_changed_cb,
+ timeline);
+ g_signal_handlers_disconnect_by_func (layer,
+ layer_auto_transition_changed_cb, timeline);
+
+ timeline->layers = g_list_remove (timeline->layers, layer);
+ ges_layer_set_timeline (layer, NULL);
+
+ g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
+
+ gst_object_unref (layer);
+
+ return TRUE;
+}
+
+/**
+ * ges_timeline_add_track:
+ * @timeline: a #GESTimeline
+ * @track: (transfer full): the #GESTrack to add
+ *
+ * Add a track to the timeline. The reference to the track will be stolen by the
+ * pipeline.
+ *
+ * Returns: %TRUE if the track was properly added, else %FALSE.
+ */
+
+/* FIXME: create track elements for clips which have already been
+ * added to existing layers.
+ */
+
+gboolean
+ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
+{
+ TrackPrivate *tr_priv;
+ GList *tmp;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
+ CHECK_THREAD (timeline);
+
+ GST_DEBUG ("timeline:%p, track:%p", timeline, track);
+
+ /* make sure we don't already control it */
+ if (G_UNLIKELY (g_list_find (timeline->tracks, (gconstpointer) track))) {
+ GST_WARNING ("Track is already controlled by this timeline");
+ return FALSE;
+ }
+
+ /* Add the track to ourself (as a GstBin)
+ * Reference is stolen ! */
+ if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
+ GST_WARNING ("Couldn't add track to ourself (GST)");
+ return FALSE;
+ }
+
+ tr_priv = g_new0 (TrackPrivate, 1);
+ tr_priv->timeline = timeline;
+ tr_priv->track = track;
+
+ /* Add the track to the list of tracks we track */
+ LOCK_DYN (timeline);
+ timeline->priv->priv_tracks = g_list_append (timeline->priv->priv_tracks,
+ tr_priv);
+ UNLOCK_DYN (timeline);
+ timeline->tracks = g_list_append (timeline->tracks, track);
+
+ /* Inform the track that it's currently being used by ourself */
+ ges_track_set_timeline (track, timeline);
+
+ GST_DEBUG ("Done adding track, emitting 'track-added' signal");
+
+ _ghost_track_srcpad (tr_priv);
+
+ /* emit 'track-added' */
+ g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
+
+ /* ensure that each existing clip has the opportunity to create a
+ * track element for this track*/
+
+ /* We connect to the object for the timeline editing mode management */
+ g_signal_connect (G_OBJECT (track), "track-element-added",
+ G_CALLBACK (track_element_added_cb), timeline);
+ g_signal_connect (G_OBJECT (track), "track-element-removed",
+ G_CALLBACK (track_element_removed_cb), timeline);
+
+ for (tmp = timeline->layers; tmp; tmp = tmp->next) {
+ GList *objects, *obj;
+ objects = ges_layer_get_clips (tmp->data);
+
+ for (obj = objects; obj; obj = obj->next) {
+ GESClip *clip = obj->data;
+
+ add_object_to_tracks (timeline, clip, track);
+ gst_object_unref (clip);
+ }
+ g_list_free (objects);
+ }
+
+ /* FIXME Check if we should rollback if we can't sync state */
+ gst_element_sync_state_with_parent (GST_ELEMENT (track));
+ g_object_set (track, "message-forward", TRUE, NULL);
+
+ return TRUE;
+}
+
+/**
+ * ges_timeline_remove_track:
+ * @timeline: a #GESTimeline
+ * @track: the #GESTrack to remove
+ *
+ * Remove the @track from the @timeline. The reference stolen when adding the
+ * @track will be removed. If you wish to use the @track after calling this
+ * function you must ensure that you have a reference to it.
+ *
+ * Returns: %TRUE if the @track was properly removed, else %FALSE.
+ */
+
+/* FIXME: release any track elements associated with this layer. currenly this
+ * will not happen if you remove the track before removing *all*
+ * clips which have a track element in this track.
+ */
+
+gboolean
+ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
+{
+ GList *tmp;
+ TrackPrivate *tr_priv;
+ GESTimelinePrivate *priv;
+
+ g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ CHECK_THREAD (timeline);
+
+ GST_DEBUG ("timeline:%p, track:%p", timeline, track);
+
+ priv = timeline->priv;
+ LOCK_DYN (timeline);
+ if (G_UNLIKELY (!(tmp = g_list_find_custom (priv->priv_tracks,
+ track, (GCompareFunc) custom_find_track)))) {
+ GST_WARNING ("Track doesn't belong to this timeline");
+ UNLOCK_DYN (timeline);
+ return FALSE;
+ }
+
+ tr_priv = tmp->data;
+ gst_object_unref (tr_priv->pad);
+ priv->priv_tracks = g_list_remove (priv->priv_tracks, tr_priv);
+ UNLOCK_DYN (timeline);
+ timeline->tracks = g_list_remove (timeline->tracks, track);
+
+ ges_track_set_timeline (track, NULL);
+
+ /* Remove ghost pad */
+ if (tr_priv->ghostpad) {
+ GST_DEBUG ("Removing ghostpad");
+ gst_pad_set_active (tr_priv->ghostpad, FALSE);
+ gst_ghost_pad_set_target ((GstGhostPad *) tr_priv->ghostpad, NULL);
+ gst_element_remove_pad (GST_ELEMENT (timeline), tr_priv->ghostpad);
+ }
+
+ /* Remove pad-added/-removed handlers */
+ g_signal_handlers_disconnect_by_func (track, track_element_added_cb,
+ timeline);
+ g_signal_handlers_disconnect_by_func (track, track_element_removed_cb,
+ timeline);
+
+ /* Signal track removal to all layers/objects */
+ g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
+
+ /* remove track from our bin */
+ gst_object_ref (track);
+ if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
+ GST_WARNING ("Couldn't remove track to ourself (GST)");
+ gst_object_unref (track);
+ return FALSE;
+ }
+
+ /* set track state to NULL */
+ gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
+
+ gst_object_unref (track);
+
+ g_free (tr_priv);
+
+ return TRUE;
+}
+
+/**
+ * ges_timeline_get_track_for_pad:
+ * @timeline: The #GESTimeline
+ * @pad: The #GstPad
+ *
+ * Search the #GESTrack corresponding to the given @timeline's @pad.
+ *
+ * Returns: (transfer none) (nullable): The corresponding #GESTrack if it is
+ * found, or %NULL if there is an error.
+ */
+
+GESTrack *
+ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
+{
+ GList *tmp;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+
+ LOCK_DYN (timeline);
+ for (tmp = timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
+ TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
+ if (pad == tr_priv->ghostpad) {
+ UNLOCK_DYN (timeline);
+ return tr_priv->track;
+ }
+ }
+ UNLOCK_DYN (timeline);
+
+ return NULL;
+}
+
+/**
+ * ges_timeline_get_pad_for_track:
+ * @timeline: The #GESTimeline
+ * @track: The #GESTrack
+ *
+ * Search the #GstPad corresponding to the given @timeline's @track.
+ *
+ * Returns: (transfer none) (nullable): The corresponding #GstPad if it is
+ * found, or %NULL if there is an error.
+ */
+
+GstPad *
+ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack * track)
+{
+ GList *tmp;
+
+ LOCK_DYN (timeline);
+ for (tmp = timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
+ TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
+
+ if (track == tr_priv->track) {
+ if (tr_priv->ghostpad)
+ gst_object_ref (tr_priv->ghostpad);
+
+ UNLOCK_DYN (timeline);
+ return tr_priv->ghostpad;
+ }
+ }
+ UNLOCK_DYN (timeline);
+
+ return NULL;
+}
+
+/**
+ * ges_timeline_get_tracks:
+ * @timeline: a #GESTimeline
+ *
+ * Returns the list of #GESTrack used by the Timeline.
+ *
+ * Returns: (transfer full) (element-type GESTrack): A list of #GESTrack.
+ * The caller should unref each track once he is done with them.
+ */
+GList *
+ges_timeline_get_tracks (GESTimeline * timeline)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+ CHECK_THREAD (timeline);
+
+ return g_list_copy_deep (timeline->tracks, (GCopyFunc) gst_object_ref, NULL);
+}
+
+/**
+ * ges_timeline_get_layers:
+ * @timeline: a #GESTimeline
+ *
+ * Get the list of #GESLayer present in the Timeline.
+ *
+ * Returns: (transfer full) (element-type GESLayer): the list of
+ * #GESLayer present in the Timeline sorted by priority.
+ * The caller should unref each Layer once he is done with them.
+ */
+GList *
+ges_timeline_get_layers (GESTimeline * timeline)
+{
+ GList *tmp, *res = NULL;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+ CHECK_THREAD (timeline);
+
+ for (tmp = timeline->layers; tmp; tmp = g_list_next (tmp)) {
+ res = g_list_insert_sorted (res, gst_object_ref (tmp->data),
+ (GCompareFunc) sort_layers);
+ }
+
+ return res;
+}
+
+static void
+track_commited_cb (GESTrack * track, GESTimeline * timeline)
+{
+ gboolean emit_commited = FALSE;
+ GST_OBJECT_LOCK (timeline);
+ timeline->priv->expected_commited -= 1;
+ if (timeline->priv->expected_commited == 0)
+ emit_commited = TRUE;
+ g_signal_handlers_disconnect_by_func (track, track_commited_cb, timeline);
+ GST_OBJECT_UNLOCK (timeline);
+
+ if (emit_commited) {
+ g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
+ }
+}
+
+/* Must be called with the timeline's DYN_LOCK */
+static gboolean
+ges_timeline_commit_unlocked (GESTimeline * timeline)
+{
+ GList *tmp;
+ gboolean res = TRUE;
+
+ GST_DEBUG_OBJECT (timeline, "commiting changes");
+
+ timeline_tree_create_transitions (timeline->priv->tree,
+ ges_timeline_find_auto_transition);
+ for (tmp = timeline->layers; tmp; tmp = tmp->next) {
+ GESLayer *layer = tmp->data;
+
+ /* Ensure clip priorities are correct after an edit */
+ ges_layer_resync_priorities (layer);
+ }
+
+ timeline->priv->expected_commited =
+ g_list_length (timeline->priv->priv_tracks);
+
+ if (timeline->priv->expected_commited == 0) {
+ g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
+ } else {
+ for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
+ g_signal_connect (tmp->data, "commited", G_CALLBACK (track_commited_cb),
+ timeline);
+ if (!ges_track_commit (GES_TRACK (tmp->data)))
+ res = FALSE;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * ges_timeline_commit:
+ * @timeline: a #GESTimeline
+ *
+ * Commit all the pending changes of the clips contained in the
+ * @timeline.
+ *
+ * When changes happen in a timeline, they are not
+ * directly executed in the non-linear engine. Call this method once you are
+ * done with a set of changes and want it to be executed.
+ *
+ * The #GESTimeline::commited signal will be emitted when the (possibly updated)
+ * #GstPipeline is ready to output data again, except if the state of the
+ * timeline was #GST_STATE_READY or #GST_STATE_NULL.
+ *
+ * Note that all the pending changes will automatically be executed when the
+ * timeline goes from #GST_STATE_READY to #GST_STATE_PAUSED, which usually is
+ * triggered by corresponding state changes in a containing #GESPipeline.
+ *
+ * You should not try to change the state of the timeline, seek it or add
+ * tracks to it during a commit operation, that is between a call to this
+ * function and after receiving the #GESTimeline::commited signal.
+ *
+ * See #ges_timeline_commit_sync if you don't want to bother with waiting
+ * for the signal.
+ *
+ * Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
+ * to be commited
+ */
+gboolean
+ges_timeline_commit (GESTimeline * timeline)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+
+ LOCK_DYN (timeline);
+ ret = ges_timeline_commit_unlocked (timeline);
+ UNLOCK_DYN (timeline);
+
+ ges_timeline_emit_snapping (timeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+ return ret;
+}
+
+static void
+commited_cb (GESTimeline * timeline)
+{
+ g_mutex_lock (&timeline->priv->commited_lock);
+ g_cond_signal (&timeline->priv->commited_cond);
+ g_mutex_unlock (&timeline->priv->commited_lock);
+}
+
+/**
+ * ges_timeline_commit_sync:
+ * @timeline: a #GESTimeline
+ *
+ * Commit all the pending changes of the #GESClips contained in the
+ * @timeline.
+ *
+ * Will return once the update is complete, that is when the
+ * (possibly updated) #GstPipeline is ready to output data again, or if the
+ * state of the timeline was #GST_STATE_READY or #GST_STATE_NULL.
+ *
+ * This function will wait for any pending state change of the timeline by
+ * calling #gst_element_get_state with a #GST_CLOCK_TIME_NONE timeout, you
+ * should not try to change the state from another thread before this function
+ * has returned.
+ *
+ * See #ges_timeline_commit for more information.
+ *
+ * Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
+ * to be commited
+ */
+gboolean
+ges_timeline_commit_sync (GESTimeline * timeline)
+{
+ gboolean ret;
+ gboolean wait_for_signal;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+
+ /* Let's make sure our state is stable */
+ gst_element_get_state (GST_ELEMENT (timeline), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+
+ /* Let's make sure no track gets added between now and the actual commiting */
+ LOCK_DYN (timeline);
+ wait_for_signal = g_list_length (timeline->priv->priv_tracks) > 0
+ && GST_STATE (timeline) >= GST_STATE_PAUSED;
+
+ if (!wait_for_signal) {
+ ret = ges_timeline_commit_unlocked (timeline);
+ } else {
+ gulong handler_id =
+ g_signal_connect (timeline, "commited", (GCallback) commited_cb, NULL);
+
+ g_mutex_lock (&timeline->priv->commited_lock);
+
+ ret = ges_timeline_commit_unlocked (timeline);
+ g_cond_wait (&timeline->priv->commited_cond,
+ &timeline->priv->commited_lock);
+ g_mutex_unlock (&timeline->priv->commited_lock);
+ g_signal_handler_disconnect (timeline, handler_id);
+ }
+
+ UNLOCK_DYN (timeline);
+
+ return ret;
+}
+
+/**
+ * ges_timeline_get_duration:
+ * @timeline: a #GESTimeline
+ *
+ * Get the current duration of @timeline
+ *
+ * Returns: The current duration of @timeline
+ */
+GstClockTime
+ges_timeline_get_duration (GESTimeline * timeline)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
+ CHECK_THREAD (timeline);
+
+ return timeline->priv->duration;
+}
+
+/**
+ * ges_timeline_get_auto_transition:
+ * @timeline: a #GESTimeline
+ *
+ * Gets whether transitions are automatically added when objects
+ * overlap or not.
+ *
+ * Returns: %TRUE if transitions are automatically added, else %FALSE.
+ */
+gboolean
+ges_timeline_get_auto_transition (GESTimeline * timeline)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ CHECK_THREAD (timeline);
+
+ return timeline->priv->auto_transition;
+}
+
+/**
+ * ges_timeline_set_auto_transition:
+ * @timeline: a #GESLayer
+ * @auto_transition: whether the auto_transition is active
+ *
+ * Sets the layer to the given @auto_transition. See the documentation of the
+ * property auto_transition for more information.
+ */
+void
+ges_timeline_set_auto_transition (GESTimeline * timeline,
+ gboolean auto_transition)
+{
+ GList *layers;
+ GESLayer *layer;
+
+ g_return_if_fail (GES_IS_TIMELINE (timeline));
+ CHECK_THREAD (timeline);
+
+ timeline->priv->auto_transition = auto_transition;
+ g_object_notify (G_OBJECT (timeline), "auto-transition");
+
+ layers = timeline->layers;
+ for (; layers; layers = layers->next) {
+ layer = layers->data;
+ ges_layer_set_auto_transition (layer, auto_transition);
+ }
+}
+
+/**
+ * ges_timeline_get_snapping_distance:
+ * @timeline: a #GESTimeline
+ *
+ * Gets the configured snapping distance of the timeline. See
+ * the documentation of the property snapping_distance for more
+ * information.
+ *
+ * Returns: The @snapping_distance property of the timeline
+ */
+GstClockTime
+ges_timeline_get_snapping_distance (GESTimeline * timeline)
+{
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
+ CHECK_THREAD (timeline);
+
+ return timeline->priv->snapping_distance;
+
+}
+
+/**
+ * ges_timeline_set_snapping_distance:
+ * @timeline: a #GESLayer
+ * @snapping_distance: whether the snapping_distance is active
+ *
+ * Sets the @snapping_distance of the timeline. See the documentation of the
+ * property snapping_distance for more information.
+ */
+void
+ges_timeline_set_snapping_distance (GESTimeline * timeline,
+ GstClockTime snapping_distance)
+{
+ g_return_if_fail (GES_IS_TIMELINE (timeline));
+ CHECK_THREAD (timeline);
+
+ timeline->priv->snapping_distance = snapping_distance;
+}
+
+/**
+ * ges_timeline_get_element:
+ * @timeline: a #GESTimeline
+ *
+ * Gets a #GESTimelineElement contained in the timeline
+ *
+ * Returns: (transfer full) (nullable): The #GESTimelineElement or %NULL if
+ * not found.
+ */
+GESTimelineElement *
+ges_timeline_get_element (GESTimeline * timeline, const gchar * name)
+{
+ GESTimelineElement *ret;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+ CHECK_THREAD (timeline);
+
+ ret = g_hash_table_lookup (timeline->priv->all_elements, name);
+
+ if (ret)
+ return gst_object_ref (ret);
+
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ GList *element_names, *tmp;
+ element_names = g_hash_table_get_keys (timeline->priv->all_elements);
+
+ GST_INFO_OBJECT (timeline, "Does not contain element %s", name);
+
+ for (tmp = element_names; tmp; tmp = tmp->next) {
+ GST_DEBUG_OBJECT (timeline, "Containes: %s", (gchar *) tmp->data);
+ }
+ g_list_free (element_names);
+ }
+#endif
+
+ return NULL;
+}
+
+/**
+ * ges_timeline_is_empty:
+ * @timeline: a #GESTimeline
+ *
+ * Check whether a #GESTimeline is empty or not
+ *
+ * Returns: %TRUE if the timeline is empty %FALSE otherwize
+ */
+gboolean
+ges_timeline_is_empty (GESTimeline * timeline)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ CHECK_THREAD (timeline);
+
+ if (g_hash_table_size (timeline->priv->all_elements) == 0)
+ return TRUE;
+
+ g_hash_table_iter_init (&iter, timeline->priv->all_elements);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (GES_IS_SOURCE (value) &&
+ ges_track_element_is_active (GES_TRACK_ELEMENT (value)))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * ges_timeline_get_layer:
+ * @timeline: The #GESTimeline to retrive a layer from
+ * @priority: The priority of the layer to find
+ *
+ * Retrieve the layer with @priority as a priority
+ *
+ * Returns: (transfer full) (nullable): A #GESLayer or %NULL if no layer with
+ * @priority was found
+ *
+ * Since 1.6
+ */
+GESLayer *
+ges_timeline_get_layer (GESTimeline * timeline, guint priority)
+{
+ GList *tmp;
+ GESLayer *layer = NULL;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
+ CHECK_THREAD (timeline);
+
+ for (tmp = timeline->layers; tmp; tmp = tmp->next) {
+ GESLayer *tmp_layer = GES_LAYER (tmp->data);
+ guint tmp_priority;
+
+ g_object_get (tmp_layer, "priority", &tmp_priority, NULL);
+ if (tmp_priority == priority) {
+ layer = gst_object_ref (tmp_layer);
+ break;
+ }
+ }
+
+ return layer;
+}
+
+/**
+ * ges_timeline_paste_element:
+ * @timeline: The #GESTimeline onto which the #GESTimelineElement should be pasted
+ * @element: The #GESTimelineElement to paste
+ * @position: The position in the timeline the element should
+ * be pasted to, meaning it will become the start of @element
+ * @layer_priority: The #GESLayer to which the element should be pasted to.
+ * -1 means paste to the same layer from which the @element has been copied from.
+ *
+ * Paste @element inside the timeline. @element must have been
+ * created using ges_timeline_element_copy with deep=TRUE set,
+ * i.e. it must be a deep copy, otherwise it will fail.
+ *
+ * Returns: (transfer none): Shallow copy of the @element pasted
+ */
+GESTimelineElement *
+ges_timeline_paste_element (GESTimeline * timeline,
+ GESTimelineElement * element, GstClockTime position, gint layer_priority)
+{
+ GESTimelineElement *res, *copied_from;
+ GESTimelineElementClass *element_class;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (element), FALSE);
+ CHECK_THREAD (timeline);
+
+ element_class = GES_TIMELINE_ELEMENT_GET_CLASS (element);
+ copied_from = ges_timeline_element_get_copied_from (element);
+
+ if (!copied_from) {
+ GST_ERROR_OBJECT (element, "Is not being 'deeply' copied!");
+
+ return NULL;
+ }
+
+ if (!element_class->paste) {
+ GST_ERROR_OBJECT (element, "No paste vmethod implemented");
+
+ return NULL;
+ }
+
+ /*
+ * Currently the API only supports pasting onto the same layer from which
+ * the @element has been copied from, i.e., @layer_priority needs to be -1.
+ */
+ if (layer_priority != -1) {
+ GST_WARNING_OBJECT (timeline,
+ "Only -1 value for layer priority is supported");
+ }
+
+ res = element_class->paste (element, copied_from, position);
+
+ g_clear_object (&copied_from);
+
+ return g_object_ref (res);
+}
+
+/**
+ * ges_timeline_move_layer:
+ * @timeline: The timeline in which @layer must be
+ * @layer: The layer to move at @new_layer_priority
+ * @new_layer_priority: The index at which @layer should land
+ *
+ * Moves @layer at @new_layer_priority meaning that @layer
+ * we land at that position in the stack of layers inside
+ * the timeline. If @new_layer_priority is superior than the number
+ * of layers present in the time, it will move to the end of the
+ * stack of layers.
+ *
+ * Since: 1.16
+ */
+gboolean
+ges_timeline_move_layer (GESTimeline * timeline, GESLayer * layer,
+ guint new_layer_priority)
+{
+ gint current_priority;
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+ g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+ g_return_val_if_fail (ges_layer_get_timeline (layer) == timeline, FALSE);
+ CHECK_THREAD (timeline);
+
+ current_priority = ges_layer_get_priority (layer);
+
+ if (new_layer_priority == current_priority) {
+ GST_DEBUG_OBJECT (timeline,
+ "Nothing to do for %" GST_PTR_FORMAT ", same priorities", layer);
+
+ return TRUE;
+ }
+
+ timeline->layers = g_list_remove (timeline->layers, layer);
+ timeline->layers = g_list_insert (timeline->layers, layer,
+ (gint) new_layer_priority);
+
+ _resync_layers (timeline);
+
+ return TRUE;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_TIMELINE
+#define _GES_TIMELINE
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <gst/pbutils/gstdiscoverer.h>
+#include <ges/ges-types.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TIMELINE ges_timeline_get_type()
+
+#define GES_TIMELINE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TIMELINE, GESTimeline))
+
+#define GES_TIMELINE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TIMELINE, GESTimelineClass))
+
+#define GES_IS_TIMELINE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TIMELINE))
+
+#define GES_IS_TIMELINE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TIMELINE))
+
+#define GES_TIMELINE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TIMELINE, GESTimelineClass))
+
+#define GES_TIMELINE_GET_TRACKS(obj) (GES_TIMELINE (obj)->tracks)
+#define GES_TIMELINE_GET_LAYERS(obj) (GES_TIMELINE (obj)->layers)
+
+/**
+ * ges_timeline_get_project:
+ * @obj: The #GESTimeline from which to retrieve the project
+ *
+ * Helper macro to retrieve the project from which a #GESTimeline as been extracted
+ */
+#define ges_timeline_get_project(obj) (GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE(obj))))
+
+typedef struct _GESTimelinePrivate GESTimelinePrivate;
+
+/**
+ * GESTimeline:
+ * @layers: (element-type GES.Layer): A list of #GESLayer sorted by priority NOTE: Do not modify.
+ * @tracks: (element-type GES.Track): A list of #GESTrack sorted by priority NOTE: Do not modify.
+ */
+struct _GESTimeline {
+ GstBin parent;
+
+ /*< public > */
+ /* <readonly> */
+ GList *layers;
+ GList *tracks;
+
+ /*< private >*/
+ GESTimelinePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTimelineClass:
+ * @parent_class: parent class
+ */
+
+struct _GESTimelineClass {
+ GstBinClass parent_class;
+
+ /*< private >*/
+
+ void (*track_added) (GESTimeline *timeline, GESTrack * track);
+ void (*track_removed) (GESTimeline *timeline, GESTrack * track);
+ void (*layer_added) (GESTimeline *timeline, GESLayer *layer);
+ void (*layer_removed) (GESTimeline *timeline, GESLayer *layer);
+ void (*group_added) (GESTimeline *timeline, GESGroup *group);
+ void (*group_removed) (GESTimeline *timeline, GESGroup *group, GPtrArray *children);
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_timeline_get_type (void);
+
+GES_API
+GESTimeline* ges_timeline_new (void);
+GES_API
+GESTimeline* ges_timeline_new_from_uri (const gchar *uri, GError **error);
+
+GES_API
+gboolean ges_timeline_load_from_uri (GESTimeline *timeline, const gchar *uri, GError **error);
+GES_API
+gboolean ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri,
+ GESAsset *formatter_asset, gboolean overwrite, GError ** error);
+GES_API
+gboolean ges_timeline_add_layer (GESTimeline *timeline, GESLayer *layer);
+GES_API
+GESLayer * ges_timeline_append_layer (GESTimeline * timeline);
+GES_API
+gboolean ges_timeline_remove_layer (GESTimeline *timeline, GESLayer *layer);
+GES_API
+GList* ges_timeline_get_layers (GESTimeline *timeline);
+GES_API
+GESLayer* ges_timeline_get_layer (GESTimeline *timeline, guint priority);
+
+GES_API
+gboolean ges_timeline_add_track (GESTimeline *timeline, GESTrack *track);
+GES_API
+gboolean ges_timeline_remove_track (GESTimeline *timeline, GESTrack *track);
+
+GES_API
+GESTrack * ges_timeline_get_track_for_pad (GESTimeline *timeline, GstPad *pad);
+GES_API
+GstPad * ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack *track);
+GES_API
+GList *ges_timeline_get_tracks (GESTimeline *timeline);
+
+GES_API
+GList* ges_timeline_get_groups (GESTimeline * timeline);
+
+GES_API
+gboolean ges_timeline_commit (GESTimeline * timeline);
+GES_API
+gboolean ges_timeline_commit_sync (GESTimeline * timeline);
+
+GES_API
+GstClockTime ges_timeline_get_duration (GESTimeline *timeline);
+
+GES_API
+gboolean ges_timeline_get_auto_transition (GESTimeline * timeline);
+GES_API
+void ges_timeline_set_auto_transition (GESTimeline * timeline, gboolean auto_transition);
+GES_API
+GstClockTime ges_timeline_get_snapping_distance (GESTimeline * timeline);
+GES_API
+void ges_timeline_set_snapping_distance (GESTimeline * timeline, GstClockTime snapping_distance);
+GES_API
+GESTimelineElement * ges_timeline_get_element (GESTimeline * timeline, const gchar *name);
+GES_API
+gboolean ges_timeline_is_empty (GESTimeline * timeline);
+GES_API
+GESTimelineElement * ges_timeline_paste_element (GESTimeline * timeline,
+ GESTimelineElement * element, GstClockTime position, gint layer_priority);
+GES_API
+gboolean ges_timeline_move_layer (GESTimeline *timeline, GESLayer *layer, guint new_layer_priority);
+
+G_END_DECLS
+
+#endif /* _GES_TIMELINE */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestitleclip
+ * @title: GESTitleClip
+ * @short_description: Render stand-alone titles in GESLayer.
+ *
+ * Renders the given text in the specified font, at specified position, and
+ * with the specified background pattern.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-title-clip.h"
+#include "ges-source-clip.h"
+#include "ges-track-element.h"
+#include "ges-title-source.h"
+#include <string.h>
+
+#define DEFAULT_TEXT ""
+#define DEFAULT_FONT_DESC "Serif 36"
+#define GES_TITLE_CLIP_VALIGN_TYPE (ges_title_clip_valign_get_type())
+#define GES_TITLE_CLIP_HALIGN_TYPE (ges_title_clip_halign_get_type())
+
+struct _GESTitleClipPrivate
+{
+ GSList *track_titles;
+};
+
+enum
+{
+ PROP_0,
+ PROP_TEXT,
+ PROP_FONT_DESC,
+ PROP_HALIGNMENT,
+ PROP_VALIGNMENT,
+ PROP_COLOR,
+ PROP_BACKGROUND,
+ PROP_XPOS,
+ PROP_YPOS,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESTitleClip, ges_title_clip, GES_TYPE_SOURCE_CLIP);
+
+static GESTrackElement
+ * ges_title_clip_create_track_element (GESClip * clip, GESTrackType type);
+
+static void _child_added (GESContainer * container,
+ GESTimelineElement * element);
+static void _child_removed (GESContainer * container,
+ GESTimelineElement * element);
+
+static void
+ges_title_clip_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTimelineElement *child, *tmpsrc = NULL;
+ GESTitleClipPrivate *priv = GES_TITLE_CLIP (object)->priv;
+
+ if (!priv->track_titles)
+ child = tmpsrc = GES_TIMELINE_ELEMENT (ges_title_source_new ());
+ else
+ child = priv->track_titles->data;
+
+ switch (property_id) {
+ /* Falltrough all over */
+ case PROP_TEXT:
+ case PROP_FONT_DESC:
+ case PROP_HALIGNMENT:
+ case PROP_VALIGNMENT:
+ case PROP_COLOR:
+ case PROP_BACKGROUND:
+ case PROP_XPOS:
+ case PROP_YPOS:
+ ges_timeline_element_get_child_property (child, pspec->name, value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+
+ if (tmpsrc)
+ g_object_unref (tmpsrc);
+}
+
+static void
+ges_title_clip_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTitleClip *uriclip = GES_TITLE_CLIP (object);
+
+ switch (property_id) {
+ case PROP_TEXT:
+ ges_title_clip_set_text (uriclip, g_value_get_string (value));
+ break;
+ case PROP_FONT_DESC:
+ ges_title_clip_set_font_desc (uriclip, g_value_get_string (value));
+ break;
+ case PROP_HALIGNMENT:
+ ges_title_clip_set_halignment (uriclip, g_value_get_enum (value));
+ break;
+ case PROP_VALIGNMENT:
+ ges_title_clip_set_valignment (uriclip, g_value_get_enum (value));
+ break;
+ case PROP_COLOR:
+ ges_title_clip_set_color (uriclip, g_value_get_uint (value));
+ break;
+ case PROP_BACKGROUND:
+ ges_title_clip_set_background (uriclip, g_value_get_uint (value));
+ break;
+ case PROP_XPOS:
+ ges_title_clip_set_xpos (uriclip, g_value_get_double (value));
+ break;
+ case PROP_YPOS:
+ ges_title_clip_set_ypos (uriclip, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_title_clip_dispose (GObject * object)
+{
+ G_OBJECT_CLASS (ges_title_clip_parent_class)->dispose (object);
+}
+
+static void
+ges_title_clip_class_init (GESTitleClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESTimelineElementClass *timeline_element_class =
+ GES_TIMELINE_ELEMENT_CLASS (klass);
+ GESClipClass *clip_class = GES_CLIP_CLASS (klass);
+ GESContainerClass *container_class = GES_CONTAINER_CLASS (klass);
+
+ object_class->get_property = ges_title_clip_get_property;
+ object_class->set_property = ges_title_clip_set_property;
+ object_class->dispose = ges_title_clip_dispose;
+
+ /**
+ * GESTitleClip:text:
+ *
+ * The text to diplay
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+ g_object_class_install_property (object_class, PROP_TEXT,
+ g_param_spec_string ("text", "Text", "The text to display",
+ DEFAULT_TEXT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+ GES_PARAM_NO_SERIALIZATION));
+
+ /**
+ * GESTitleClip:font-desc:
+ *
+ * Pango font description string
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
+ g_param_spec_string ("font-desc", "font description",
+ "Pango font description of font to be used for rendering. "
+ "See documentation of pango_font_description_from_string "
+ "for syntax.", DEFAULT_FONT_DESC,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
+ GES_PARAM_NO_SERIALIZATION));
+
+ /**
+ * GESTitleClip:valignment:
+ *
+ * Vertical alignent of the text
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
+ g_param_spec_enum ("valignment", "vertical alignment",
+ "Vertical alignment of the text", GES_TEXT_VALIGN_TYPE,
+ DEFAULT_VALIGNMENT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
+ GES_PARAM_NO_SERIALIZATION));
+ /**
+ * GESTitleClip:halignment:
+ *
+ * Horizontal alignment of the text
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
+ g_param_spec_enum ("halignment", "horizontal alignment",
+ "Horizontal alignment of the text",
+ GES_TEXT_HALIGN_TYPE, DEFAULT_HALIGNMENT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS |
+ GES_PARAM_NO_SERIALIZATION));
+
+ clip_class->create_track_element = ges_title_clip_create_track_element;
+ timeline_element_class->set_inpoint = NULL;
+
+ container_class->child_added = _child_added;
+ container_class->child_removed = _child_removed;
+
+ /**
+ * GESTitleClip:color:
+ *
+ * The color of the text
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+
+ g_object_class_install_property (object_class, PROP_COLOR,
+ g_param_spec_uint ("color", "Color", "The color of the text",
+ 0, G_MAXUINT32, G_MAXUINT32, G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+ GES_PARAM_NO_SERIALIZATION));
+
+ /**
+ * GESTitleClip:background:
+ *
+ * The background of the text
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+
+ g_object_class_install_property (object_class, PROP_BACKGROUND,
+ g_param_spec_uint ("background", "Background",
+ "The background of the text", 0, G_MAXUINT32, G_MAXUINT32,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | GES_PARAM_NO_SERIALIZATION));
+
+ /**
+ * GESTitleClip:xpos:
+ *
+ * The horizontal position of the text
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+
+ g_object_class_install_property (object_class, PROP_XPOS,
+ g_param_spec_double ("xpos", "Xpos", "The horizontal position",
+ 0, 1, 0.5, G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ | GES_PARAM_NO_SERIALIZATION));
+
+ /**
+ * GESTitleClip:ypos:
+ *
+ * The vertical position of the text
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+
+ g_object_class_install_property (object_class, PROP_YPOS,
+ g_param_spec_double ("ypos", "Ypos", "The vertical position",
+ 0, 1, 0.5, G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ | GES_PARAM_NO_SERIALIZATION));
+}
+
+static void
+ges_title_clip_init (GESTitleClip * self)
+{
+ self->priv = ges_title_clip_get_instance_private (self);
+
+ GES_TIMELINE_ELEMENT (self)->duration = 0;
+ /* Not 100% required since a new gobject's content will always be memzero'd */
+}
+
+/**
+ * ges_title_clip_set_text:
+ * @self: the #GESTitleClip* to set text on
+ * @text: the text to render. an internal copy of this text will be
+ * made.
+ *
+ * Sets the text this clip will render.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_text (GESTitleClip * self, const gchar * text)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "text:%s", text);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data, "text", text, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_set_font_desc:
+ * @self: the #GESTitleClip*
+ * @font_desc: the pango font description
+ *
+ * Sets the pango font description of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_font_desc (GESTitleClip * self, const gchar * font_desc)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "font_desc:%s", font_desc);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data,
+ "font-desc", font_desc, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_set_halignment:
+ * @self: the #GESTitleClip* to set horizontal alignement of text on
+ * @halign: #GESTextHAlign
+ *
+ * Sets the horizontal aligment of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_halignment (GESTitleClip * self, GESTextHAlign halign)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "halign:%d", halign);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data,
+ "halignment", halign, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_set_valignment:
+ * @self: the #GESTitleClip* to set vertical alignement of text on
+ * @valign: #GESTextVAlign
+ *
+ * Sets the vertical aligment of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_valignment (GESTitleClip * self, GESTextVAlign valign)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "valign:%d", valign);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data,
+ "valignment", valign, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_set_color:
+ * @self: the #GESTitleClip* to set
+ * @color: The color @self is being set to
+ *
+ * Sets the color of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_color (GESTitleClip * self, guint32 color)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "color:%d", color);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data, "color", color, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_set_background:
+ * @self: the #GESTitleClip* to set
+ * @background: The color @self is being set to
+ *
+ * Sets the background of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_background (GESTitleClip * self, guint32 background)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "background:%d", background);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data,
+ "foreground-color", background, NULL);
+ }
+}
+
+
+/**
+ * ges_title_clip_set_xpos:
+ * @self: the #GESTitleClip* to set
+ * @position: The horizontal position @self is being set to
+ *
+ * Sets the horizontal position of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_xpos (GESTitleClip * self, gdouble position)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "xpos:%f", position);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data, "xpos", position, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_set_ypos:
+ * @self: the #GESTitleClip* to set
+ * @position: The vertical position @self is being set to
+ *
+ * Sets the vertical position of the text.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+void
+ges_title_clip_set_ypos (GESTitleClip * self, gdouble position)
+{
+ GSList *tmp;
+
+ GST_DEBUG_OBJECT (self, "ypos:%f", position);
+
+ for (tmp = self->priv->track_titles; tmp; tmp = tmp->next) {
+ ges_track_element_set_child_properties (tmp->data, "ypos", position, NULL);
+ }
+}
+
+/**
+ * ges_title_clip_get_text:
+ * @self: a #GESTitleClip
+ *
+ * Get the text currently set on @self.
+ *
+ * Returns: The text currently set on @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+const gchar *
+ges_title_clip_get_text (GESTitleClip * self)
+{
+ gchar *text;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "text", &text, NULL);
+
+ return text;
+}
+
+/**
+ * ges_title_clip_get_font_desc:
+ * @self: a #GESTitleClip
+ *
+ * Get the pango font description used by @self.
+ *
+ * Returns: The pango font description used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+const char *
+ges_title_clip_get_font_desc (GESTitleClip * self)
+{
+ gchar *font_desc;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "font-desc", &font_desc, NULL);
+
+ return font_desc;
+}
+
+/**
+ * ges_title_clip_get_halignment:
+ * @self: a #GESTitleClip
+ *
+ * Get the horizontal aligment used by @self.
+ *
+ * Returns: The horizontal aligment used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+GESTextHAlign
+ges_title_clip_get_halignment (GESTitleClip * self)
+{
+ GESTextHAlign halign;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "halignment", &halign, NULL);
+
+ return halign;
+}
+
+/**
+ * ges_title_clip_get_valignment:
+ * @self: a #GESTitleClip
+ *
+ * Get the vertical aligment used by @self.
+ *
+ * Returns: The vertical aligment used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+GESTextVAlign
+ges_title_clip_get_valignment (GESTitleClip * self)
+{
+ GESTextVAlign valign;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "valignment", &valign, NULL);
+
+ return valign;
+}
+
+/**
+ * ges_title_clip_get_text_color:
+ * @self: a #GESTitleClip
+ *
+ * Get the color used by @self.
+ *
+ * Returns: The color used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+const guint32
+ges_title_clip_get_text_color (GESTitleClip * self)
+{
+ guint32 color;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "color", &color, NULL);
+
+ return color;
+}
+
+/**
+ * ges_title_clip_get_background_color:
+ * @self: a #GESTitleClip
+ *
+ * Get the background used by @self.
+ *
+ * Returns: The color used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+const guint32
+ges_title_clip_get_background_color (GESTitleClip * self)
+{
+ guint32 color;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "foreground-color", &color, NULL);
+
+ return color;
+}
+
+/**
+ * ges_title_clip_get_xpos:
+ * @self: a #GESTitleClip
+ *
+ * Get the horizontal position used by @self.
+ *
+ * Returns: The horizontal position used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+const gdouble
+ges_title_clip_get_xpos (GESTitleClip * self)
+{
+ gdouble xpos;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "xpos", &xpos, NULL);
+
+ return xpos;
+}
+
+/**
+ * ges_title_clip_get_ypos:
+ * @self: a #GESTitleClip
+ *
+ * Get the vertical position used by @self.
+ *
+ * Returns: The vertical position used by @self.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * underlying GESTrackElement instead
+ */
+const gdouble
+ges_title_clip_get_ypos (GESTitleClip * self)
+{
+ gdouble ypos;
+
+ ges_track_element_get_child_properties (self->priv->track_titles->data,
+ "ypos", &ypos, NULL);
+
+ return ypos;
+}
+
+static void
+_child_removed (GESContainer * container, GESTimelineElement * element)
+{
+ GESTitleClipPrivate *priv = GES_TITLE_CLIP (container)->priv;
+
+ /* If this is called, we should be sure the element exists */
+ if (GES_IS_TITLE_SOURCE (element)) {
+ GST_DEBUG_OBJECT (container, "%" GST_PTR_FORMAT " removed", element);
+ priv->track_titles = g_slist_remove (priv->track_titles, element);
+ gst_object_unref (element);
+ }
+
+ GES_CONTAINER_CLASS (ges_title_clip_parent_class)->child_removed (container,
+ element);
+}
+
+static void
+_child_added (GESContainer * container, GESTimelineElement * element)
+{
+ GESTitleClipPrivate *priv = GES_TITLE_CLIP (container)->priv;
+
+ if (GES_IS_TITLE_SOURCE (element)) {
+ GST_DEBUG_OBJECT (container, "%" GST_PTR_FORMAT " added", element);
+ priv->track_titles = g_slist_prepend (priv->track_titles,
+ gst_object_ref (element));
+ }
+
+ GES_CONTAINER_CLASS (ges_title_clip_parent_class)->child_added (container,
+ element);
+}
+
+static GESTrackElement *
+ges_title_clip_create_track_element (GESClip * clip, GESTrackType type)
+{
+
+ GESTrackElement *res = NULL;
+
+ GST_DEBUG_OBJECT (clip, "a GESTitleSource");
+
+ if (type == GES_TRACK_TYPE_VIDEO)
+ res = (GESTrackElement *) ges_title_source_new ();
+
+ return res;
+}
+
+/**
+ * ges_title_clip_new:
+ *
+ * Creates a new #GESTitleClip
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESTitleClip,
+ * or %NULL if there was an error.
+ */
+GESTitleClip *
+ges_title_clip_new (void)
+{
+ GESTitleClip *new_clip;
+ GESAsset *asset = ges_asset_request (GES_TYPE_TITLE_CLIP, NULL, NULL);
+
+ new_clip = GES_TITLE_CLIP (ges_asset_extract (asset, NULL));
+ gst_object_unref (asset);
+
+ return new_clip;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_TIMELINE_TITLESOURCE
+#define _GES_TIMELINE_TITLESOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-source-clip.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TITLE_CLIP ges_title_clip_get_type()
+
+#define GES_TITLE_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TITLE_CLIP, GESTitleClip))
+
+#define GES_TITLE_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TITLE_CLIP, GESTitleClipClass))
+
+#define GES_IS_TITLE_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TITLE_CLIP))
+
+#define GES_IS_TITLE_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TITLE_CLIP))
+
+#define GES_TITLE_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TITLE_CLIP, GESTitleClipClass))
+
+typedef struct _GESTitleClipPrivate GESTitleClipPrivate;
+
+/**
+ * GESTitleClip:
+ *
+ * Render stand-alone titles in GESLayer.
+ */
+
+struct _GESTitleClip {
+ GESSourceClip parent;
+
+ /*< private >*/
+ GESTitleClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESTitleClipClass {
+ /*< private >*/
+ GESSourceClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_title_clip_get_type (void);
+
+GES_API void
+ges_title_clip_set_text( GESTitleClip * self,
+ const gchar * text);
+
+GES_API void
+ges_title_clip_set_font_desc (GESTitleClip * self,
+ const gchar * font_desc);
+
+GES_API void
+ges_title_clip_set_valignment (GESTitleClip * self,
+ GESTextVAlign valign);
+
+GES_API void
+ges_title_clip_set_halignment (GESTitleClip * self,
+ GESTextHAlign halign);
+
+GES_API void
+ges_title_clip_set_color (GESTitleClip * self,
+ guint32 color);
+
+GES_API void
+ges_title_clip_set_background (GESTitleClip * self,
+ guint32 background);
+
+GES_API void
+ges_title_clip_set_xpos (GESTitleClip * self,
+ gdouble position);
+
+GES_API void
+ges_title_clip_set_ypos (GESTitleClip * self,
+ gdouble position);
+
+GES_API const gchar*
+ges_title_clip_get_font_desc (GESTitleClip * self);
+
+GES_API GESTextVAlign
+ges_title_clip_get_valignment (GESTitleClip * self);
+
+GES_API GESTextHAlign
+ges_title_clip_get_halignment (GESTitleClip * self);
+
+GES_API const guint32
+ges_title_clip_get_text_color (GESTitleClip * self);
+
+GES_API const guint32
+ges_title_clip_get_background_color (GESTitleClip * self);
+
+GES_API const gdouble
+ges_title_clip_get_xpos (GESTitleClip * self);
+
+GES_API const gdouble
+ges_title_clip_get_ypos (GESTitleClip * self);
+
+GES_API
+const gchar* ges_title_clip_get_text (GESTitleClip * self);
+
+GES_API
+GESTitleClip* ges_title_clip_new (void);
+
+G_END_DECLS
+
+#endif /* _GES_TIMELINE_TITLESOURCE */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestitlesource
+ * @title: GESTitleSource
+ * @short_description: render stand-alone text titles
+ *
+ * #GESTitleSource is a GESTimelineElement that implements the notion
+ * of titles in GES.
+ *
+ * ## Children Properties
+ *
+ * You can use the following children properties through the
+ * #ges_track_element_set_child_property and alike set of methods:
+ * <informaltable frame="none">
+ * <tgroup cols="3">
+ * <colspec colname="properties_type" colwidth="150px"/>
+ * <colspec colname="properties_name" colwidth="200px"/>
+ * <colspec colname="properties_flags" colwidth="400px"/>
+ * <tbody>
+ * <row>
+ * <entry role="property_type"><link linkend="guint"><type>guint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--background">background</link></entry>
+ * <entry>The color of the background</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="guint"><type>guint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--color">color</link></entry>
+ * <entry>The color of the text</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gchar"><type>gchar</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--font-desc">font-desc</link></entry>
+ * <entry>Pango font description string</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="GESTextHAlign"><type>GESTextHAlign</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--halignment">halignment</link></entry>
+ * <entry>Horizontal alignment of the text</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gchar"><type>gchar</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--text">text</link></entry>
+ * <entry>The text to be rendered</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="GESTextVAlign"><type>GESTextVAlign</type></link>
+ * </entry><entry role="property_name"><link linkend="GESTileSource--valignment">valignment</link>
+ * </entry><entry>Vertical alignent of the text</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gdouble"><type>gdouble</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--xpos">xpos</link></entry>
+ * <entry>The horizontal position of the text</entry>
+ * </row>
+ * <row><entry role="property_type"><link linkend="gdouble"><type>gdouble</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--ypos">ypos</link></entry>
+ * <entry>The vertical position of the text</entry>
+ * </row>
+ * <row><entry role="property_type"><link linkend="gboolean"><type>gboolean</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--shaded-background">shaded-background</link></entry>
+ * <entry>Whether to shade the background under the text area</entry>
+ * </row>
+ * <row><entry role="property_type"><link linkend="guint"><type>guint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESTileSource--outline-color">outline-color</link></entry>
+ * <entry>Color to use for outline the text (big-endian ARGB).</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-title-source.h"
+#include "ges-video-test-source.h"
+
+#define DEFAULT_TEXT ""
+#define DEFAULT_FONT_DESC "Serif 36"
+
+struct _GESTitleSourcePrivate
+{
+ gchar *text;
+ gchar *font_desc;
+ GESTextHAlign halign;
+ GESTextVAlign valign;
+ guint32 color;
+ guint32 background;
+ gdouble xpos;
+ gdouble ypos;
+ GstElement *text_el;
+ GstElement *background_el;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESTitleSource, ges_title_source,
+ GES_TYPE_VIDEO_SOURCE);
+
+enum
+{
+ PROP_0,
+};
+
+static void ges_title_source_dispose (GObject * object);
+
+static void ges_title_source_get_property (GObject * object, guint
+ property_id, GValue * value, GParamSpec * pspec);
+
+static void ges_title_source_set_property (GObject * object, guint
+ property_id, const GValue * value, GParamSpec * pspec);
+
+static GstElement *ges_title_source_create_source (GESTrackElement * self);
+
+static gboolean
+_lookup_child (GESTimelineElement * object,
+ const gchar * prop_name, GObject ** element, GParamSpec ** pspec)
+{
+ gboolean res;
+
+ gchar *clean_name;
+
+ if (!g_strcmp0 (prop_name, "background"))
+ clean_name = g_strdup ("foreground-color");
+ else if (!g_strcmp0 (prop_name, "GstTextOverlay:background"))
+ clean_name = g_strdup ("foreground-color");
+ else
+ clean_name = g_strdup (prop_name);
+
+ res =
+ GES_TIMELINE_ELEMENT_CLASS (ges_title_source_parent_class)->lookup_child
+ (object, clean_name, element, pspec);
+
+ g_free (clean_name);
+
+ return res;
+}
+
+static void
+ges_title_source_class_init (GESTitleSourceClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass);
+ GESTimelineElementClass *timeline_element_class =
+ GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_title_source_get_property;
+ object_class->set_property = ges_title_source_set_property;
+ object_class->dispose = ges_title_source_dispose;
+
+ timeline_element_class->set_inpoint = NULL;
+ timeline_element_class->lookup_child = _lookup_child;
+ source_class->ABI.abi.disable_scale_in_compositor = TRUE;
+ source_class->create_source = ges_title_source_create_source;
+}
+
+static void
+ges_title_source_init (GESTitleSource * self)
+{
+ self->priv = ges_title_source_get_instance_private (self);
+
+ self->priv->text = g_strdup (DEFAULT_TEXT);
+ self->priv->font_desc = g_strdup (DEFAULT_FONT_DESC);
+ self->priv->text_el = NULL;
+ self->priv->halign = DEFAULT_HALIGNMENT;
+ self->priv->valign = DEFAULT_VALIGNMENT;
+ self->priv->color = G_MAXUINT32;
+ self->priv->background = G_MAXUINT32;
+ self->priv->xpos = 0.5;
+ self->priv->ypos = 0.5;
+ self->priv->background_el = NULL;
+}
+
+static void
+ges_title_source_dispose (GObject * object)
+{
+ GESTitleSource *self = GES_TITLE_SOURCE (object);
+ if (self->priv->text) {
+ g_free (self->priv->text);
+ }
+
+ if (self->priv->font_desc) {
+ g_free (self->priv->font_desc);
+ }
+
+ if (self->priv->text_el) {
+ gst_object_unref (self->priv->text_el);
+ self->priv->text_el = NULL;
+ }
+
+ if (self->priv->background_el) {
+ gst_object_unref (self->priv->background_el);
+ self->priv->background_el = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_title_source_parent_class)->dispose (object);
+}
+
+static void
+ges_title_source_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_title_source_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GstElement *
+ges_title_source_create_source (GESTrackElement * object)
+{
+ GstElement *topbin, *background, *text;
+ GstPad *src, *pad;
+
+ GESTitleSource *self = GES_TITLE_SOURCE (object);
+ GESTitleSourcePrivate *priv = self->priv;
+ const gchar *bg_props[] = { "pattern", "foreground-color", NULL };
+ const gchar *text_props[] = { "text", "font-desc", "valignment", "halignment",
+ "color", "xpos", "ypos", "x-absolute", "y-absolute", "outline-color",
+ "shaded-background",
+ "text-x", "text-y", "text-width", "text-height", NULL
+ };
+
+ topbin = gst_bin_new ("titlesrc-bin");
+ background = gst_element_factory_make ("videotestsrc", "titlesrc-bg");
+
+ text = gst_element_factory_make ("textoverlay", "titlsrc-text");
+ if (priv->text) {
+ g_object_set (text, "text", priv->text, NULL);
+ }
+ if (priv->font_desc) {
+ g_object_set (text, "font-desc", priv->font_desc, NULL);
+ }
+ g_object_set (text, "valignment", (gint) priv->valign, "halignment",
+ (gint) priv->halign, NULL);
+ g_object_set (text, "color", (guint) self->priv->color, NULL);
+ g_object_set (text, "xpos", (gdouble) self->priv->xpos, NULL);
+ g_object_set (text, "ypos", (gdouble) self->priv->ypos, NULL);
+
+
+ g_object_set (background, "pattern", (gint) GES_VIDEO_TEST_PATTERN_SOLID,
+ NULL);
+ g_object_set (background, "foreground-color", (guint) self->priv->background,
+ NULL);
+
+ gst_bin_add_many (GST_BIN (topbin), background, text, NULL);
+
+ gst_element_link_pads_full (background, "src", text, "video_sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+
+ pad = gst_element_get_static_pad (text, "src");
+ src = gst_ghost_pad_new ("src", pad);
+ gst_object_unref (pad);
+ gst_element_add_pad (topbin, src);
+
+ gst_object_ref (text);
+ gst_object_ref (background);
+
+ priv->text_el = text;
+ priv->background_el = background;
+
+ ges_track_element_add_children_props (object, text, NULL, NULL, text_props);
+ ges_track_element_add_children_props (object, background, NULL, NULL,
+ bg_props);
+
+ return topbin;
+}
+
+/**
+ * ges_title_source_set_text:
+ * @self: the #GESTitleSource* to set text on
+ * @text: the text to render. an internal copy of this text will be
+ * made.
+ *
+ * Sets the text this track element will render.
+ *
+ * Deprecated: use ges_track_element_get/set_children_properties on the
+ * GESTrackElement instead
+ */
+
+void
+ges_title_source_set_text (GESTitleSource * self, const gchar * text)
+{
+ if (self->priv->text)
+ g_free (self->priv->text);
+
+ GST_DEBUG ("self:%p, text:%s", self, text);
+
+ self->priv->text = g_strdup (text);
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "text", text, NULL);
+}
+
+/**
+ * ges_title_source_set_font_desc:
+ * @self: the #GESTitleSource
+ * @font_desc: the pango font description
+ *
+ * Set the pango font description this source will use to render
+ * the text.
+ */
+
+void
+ges_title_source_set_font_desc (GESTitleSource * self, const gchar * font_desc)
+{
+ if (self->priv->font_desc)
+ g_free (self->priv->font_desc);
+
+ GST_DEBUG ("self:%p, font_dec:%s", self, font_desc);
+
+ self->priv->font_desc = g_strdup (font_desc);
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "font-desc", font_desc, NULL);
+}
+
+/**
+ * ges_title_source_set_valignment:
+ * @self: the #GESTitleSource* to set text on
+ * @valign: #GESTextVAlign
+ *
+ * Sets the vertical aligment of the text.
+ */
+void
+ges_title_source_set_valignment (GESTitleSource * self, GESTextVAlign valign)
+{
+ GST_DEBUG ("self:%p, valign:%d", self, valign);
+
+ self->priv->valign = valign;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "valignment", valign, NULL);
+}
+
+/**
+ * ges_title_source_set_halignment:
+ * @self: the #GESTitleSource* to set text on
+ * @halign: #GESTextHAlign
+ *
+ * Sets the vertical aligment of the text.
+ */
+void
+ges_title_source_set_halignment (GESTitleSource * self, GESTextHAlign halign)
+{
+ GST_DEBUG ("self:%p, halign:%d", self, halign);
+
+ self->priv->halign = halign;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "halignment", halign, NULL);
+}
+
+/**
+ * ges_title_source_set_text_color:
+ * @self: the #GESTitleSource* to set
+ * @color: the color @self is being set to
+ *
+ * Sets the color of the text.
+ */
+void
+ges_title_source_set_text_color (GESTitleSource * self, guint32 color)
+{
+ GST_DEBUG ("self:%p, color:%d", self, color);
+
+ self->priv->color = color;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "color", color, NULL);
+}
+
+/**
+ * ges_title_source_set_background_color:
+ * @self: the #GESTitleSource* to set
+ * @color: the color @self is being set to
+ *
+ * Sets the color of the background
+ */
+void
+ges_title_source_set_background_color (GESTitleSource * self, guint32 color)
+{
+ GST_DEBUG ("self:%p, background color:%d", self, color);
+
+ self->priv->background = color;
+ if (self->priv->background_el)
+ g_object_set (self->priv->background_el, "foreground-color", color, NULL);
+}
+
+/**
+ * ges_title_source_set_xpos:
+ * @self: the #GESTitleSource* to set
+ * @position: the horizontal position @self is being set to
+ *
+ * Sets the horizontal position of the text.
+ */
+void
+ges_title_source_set_xpos (GESTitleSource * self, gdouble position)
+{
+ GST_DEBUG ("self:%p, xpos:%f", self, position);
+
+ self->priv->xpos = position;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "xpos", position, NULL);
+}
+
+/**
+ * ges_title_source_set_ypos:
+ * @self: the #GESTitleSource* to set
+ * @position: the color @self is being set to
+ *
+ * Sets the vertical position of the text.
+ */
+void
+ges_title_source_set_ypos (GESTitleSource * self, gdouble position)
+{
+ GST_DEBUG ("self:%p, ypos:%f", self, position);
+
+ self->priv->ypos = position;
+ if (self->priv->text_el)
+ g_object_set (self->priv->text_el, "ypos", position, NULL);
+}
+
+/**
+ * ges_title_source_get_text:
+ * @source: a #GESTitleSource
+ *
+ * Get the text currently set on the @source.
+ *
+ * Returns: (transfer full): The text currently set on the @source.
+ *
+ * Deprecated: 1.16: Use ges_timeline_element_get_child_property instead
+ * (this actually returns a newly allocated string)
+ */
+const gchar *
+ges_title_source_get_text (GESTitleSource * source)
+{
+ gchar *text;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source), "text",
+ &text, NULL);
+
+ return text;
+}
+
+/**
+ * ges_title_source_get_font_desc:
+ * @source: a #GESTitleSource
+ *
+ * Get the pango font description used by @source.
+ *
+ * Returns: (transfer full): The pango font description used by this
+ * @source.
+ *
+ * Deprecated: 1.16: Use ges_timeline_element_get_child_property instead
+ * (this actually returns a newly allocated string)
+ */
+const gchar *
+ges_title_source_get_font_desc (GESTitleSource * source)
+{
+ gchar *font_desc;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source),
+ "font-desc", &font_desc, NULL);
+
+ return font_desc;
+}
+
+/**
+ * ges_title_source_get_halignment:
+ * @source: a #GESTitleSource
+ *
+ * Get the horizontal aligment used by @source.
+ *
+ * Returns: The horizontal aligment used by @source.
+ */
+GESTextHAlign
+ges_title_source_get_halignment (GESTitleSource * source)
+{
+ GESTextHAlign halign;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source),
+ "halignment", &halign, NULL);
+
+ return halign;
+}
+
+/**
+ * ges_title_source_get_valignment:
+ * @source: a #GESTitleSource
+ *
+ * Get the vertical aligment used by @source.
+ *
+ * Returns: The vertical aligment used by @source.
+ */
+GESTextVAlign
+ges_title_source_get_valignment (GESTitleSource * source)
+{
+ GESTextVAlign valign;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source),
+ "valignment", &valign, NULL);
+
+ return valign;
+}
+
+/**
+ * ges_title_source_get_text_color:
+ * @source: a #GESTitleSource
+ *
+ * Get the color used by @source.
+ *
+ * Returns: The color used by @source.
+ */
+const guint32
+ges_title_source_get_text_color (GESTitleSource * source)
+{
+ guint32 color;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source), "color",
+ &color, NULL);
+
+ return color;
+}
+
+/**
+ * ges_title_source_get_background_color:
+ * @source: a #GESTitleSource
+ *
+ * Get the background used by @source.
+ *
+ * Returns: The background used by @source.
+ */
+const guint32
+ges_title_source_get_background_color (GESTitleSource * source)
+{
+ guint32 color;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source),
+ "foreground-color", &color, NULL);
+
+ return color;
+}
+
+/**
+ * ges_title_source_get_xpos:
+ * @source: a #GESTitleSource
+ *
+ * Get the horizontal position used by @source.
+ *
+ * Returns: The horizontal position used by @source.
+ */
+const gdouble
+ges_title_source_get_xpos (GESTitleSource * source)
+{
+ gdouble xpos;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source), "xpos",
+ &xpos, NULL);
+
+ return xpos;
+}
+
+/**
+ * ges_title_source_get_ypos:
+ * @source: a #GESTitleSource
+ *
+ * Get the vertical position used by @source.
+ *
+ * Returns: The vertical position used by @source.
+ */
+const gdouble
+ges_title_source_get_ypos (GESTitleSource * source)
+{
+ gdouble ypos;
+
+ ges_track_element_get_child_properties (GES_TRACK_ELEMENT (source), "ypos",
+ &ypos, NULL);
+
+ return ypos;
+}
+
+/**
+ * ges_title_source_new:
+ *
+ * Creates a new #GESTitleSource.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESTitleSource,
+ * or %NULL if there was an error.
+ */
+GESTitleSource *
+ges_title_source_new (void)
+{
+ return g_object_new (GES_TYPE_TITLE_SOURCE, "track-type",
+ GES_TRACK_TYPE_VIDEO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_TITLE_SOURCE
+#define _GES_TITLE_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-video-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TITLE_SOURCE ges_title_source_get_type()
+
+#define GES_TITLE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TITLE_SOURCE, GESTitleSource))
+
+#define GES_TITLE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TITLE_SOURCE, GESTitleSourceClass))
+
+#define GES_IS_TITLE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TITLE_SOURCE))
+
+#define GES_IS_TITLE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TITLE_SOURCE))
+
+#define GES_TITLE_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TITLE_SOURCE, GESTitleSourceClass))
+
+typedef struct _GESTitleSourcePrivate GESTitleSourcePrivate;
+
+/**
+ * GESTitleSource:
+ *
+ */
+struct _GESTitleSource {
+ GESVideoSource parent;
+
+ /*< private >*/
+ GESTitleSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTitleSourceClass:
+ * @parent_class: parent class
+ */
+struct _GESTitleSourceClass {
+ GESVideoSourceClass parent_class;
+
+ /*< private >*/
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING - 1];
+};
+
+GES_API
+GType ges_title_source_get_type (void);
+
+GES_API
+void ges_title_source_set_text (GESTitleSource *self,
+ const gchar *text);
+
+GES_API
+void ges_title_source_set_font_desc (GESTitleSource *self,
+ const gchar *font_desc);
+
+GES_API
+void ges_title_source_set_halignment (GESTitleSource *self,
+ GESTextHAlign halign);
+
+GES_API
+void ges_title_source_set_valignment (GESTitleSource *self,
+ GESTextVAlign valign);
+
+GES_API
+void ges_title_source_set_text_color (GESTitleSource *self,
+ guint32 color);
+GES_API
+void ges_title_source_set_background_color (GESTitleSource *self,
+ guint32 color);
+GES_API
+void ges_title_source_set_xpos (GESTitleSource *self,
+ gdouble position);
+GES_API
+void ges_title_source_set_ypos (GESTitleSource *self,
+ gdouble position);
+
+GES_API
+const gchar *ges_title_source_get_text (GESTitleSource *source);
+GES_API
+const gchar *ges_title_source_get_font_desc (GESTitleSource *source);
+GES_API
+GESTextHAlign ges_title_source_get_halignment (GESTitleSource *source);
+GES_API
+GESTextVAlign ges_title_source_get_valignment (GESTitleSource *source);
+GES_API
+const guint32 ges_title_source_get_text_color (GESTitleSource *source);
+GES_API
+const guint32 ges_title_source_get_background_color (GESTitleSource *source);
+GES_API
+const gdouble ges_title_source_get_xpos (GESTitleSource *source);
+GES_API
+const gdouble ges_title_source_get_ypos (GESTitleSource *source);
+
+G_END_DECLS
+
+#endif /* _GES_TITLE_SOURCE */
+
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+
+/**
+ * SECTION: gestrackelementasset
+ * @title: GESTrackElementAsset
+ * @short_description: A GESAsset subclass specialized in GESTrackElement extraction
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-track-element-asset.h"
+
+enum
+{
+ PROP_0,
+ PROP_TRACK_TYPE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _GESTrackElementAssetPrivate
+{
+ GESTrackType type;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESTrackElementAsset, ges_track_element_asset,
+ GES_TYPE_ASSET);
+
+static void
+_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTrackElementAsset *asset = GES_TRACK_ELEMENT_ASSET (object);
+
+ switch (property_id) {
+ case PROP_TRACK_TYPE:
+ g_value_set_flags (value, asset->priv->type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTrackElementAsset *asset = GES_TRACK_ELEMENT_ASSET (object);
+
+ switch (property_id) {
+ case PROP_TRACK_TYPE:
+ asset->priv->type = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_track_element_asset_class_init (GESTrackElementAssetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = _get_property;
+ object_class->set_property = _set_property;
+
+ /**
+ * GESClip:track-type:
+ *
+ * The formats supported by the object.
+ */
+ properties[PROP_TRACK_TYPE] = g_param_spec_flags ("track-type",
+ "Track type", "The GESTrackType in which the object is",
+ GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_property (object_class, PROP_TRACK_TYPE,
+ properties[PROP_TRACK_TYPE]);
+}
+
+static void
+ges_track_element_asset_init (GESTrackElementAsset * self)
+{
+ GESTrackElementAssetPrivate *priv;
+
+ priv = self->priv = ges_track_element_asset_get_instance_private (self);
+
+ priv->type = GES_TRACK_TYPE_UNKNOWN;
+
+}
+
+/**
+ * ges_track_element_asset_set_track_type:
+ * @asset: A #GESAssetObject
+ * @type: A #GESTrackType
+ *
+ * Set the #GESAssetTrackType the #GESTrackElement extracted from @self
+ * should get into
+ */
+void
+ges_track_element_asset_set_track_type (GESTrackElementAsset * asset,
+ GESTrackType type)
+{
+ g_return_if_fail (GES_IS_TRACK_ELEMENT_ASSET (asset));
+
+ asset->priv->type = type;
+}
+
+/**
+ * ges_track_element_asset_get_track_type:
+ * @asset: A #GESAssetObject
+ *
+ * Get the GESAssetTrackType the #GESTrackElement extracted from @self
+ * should get into
+ *
+ * Returns: a #GESTrackType
+ */
+const GESTrackType
+ges_track_element_asset_get_track_type (GESTrackElementAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT_ASSET (asset),
+ GES_TRACK_TYPE_UNKNOWN);
+
+ return asset->priv->type;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 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 _GES_TRACK_ELEMENT_ASSET_
+#define _GES_TRACK_ELEMENT_ASSET_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <ges/ges-types.h>
+#include <ges/ges-asset.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TRACK_ELEMENT_ASSET ges_track_element_asset_get_type()
+#define GES_TRACK_ELEMENT_ASSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TRACK_ELEMENT_ASSET, GESTrackElementAsset))
+#define GES_TRACK_ELEMENT_ASSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TRACK_ELEMENT_ASSET, GESTrackElementAssetClass))
+#define GES_IS_TRACK_ELEMENT_ASSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TRACK_ELEMENT_ASSET))
+#define GES_IS_TRACK_ELEMENT_ASSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TRACK_ELEMENT_ASSET))
+#define GES_TRACK_ELEMENT_ASSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TRACK_ELEMENT_ASSET, GESTrackElementAssetClass))
+
+typedef struct _GESTrackElementAssetPrivate GESTrackElementAssetPrivate;
+
+GES_API
+GType ges_track_element_asset_get_type (void);
+
+struct _GESTrackElementAsset
+{
+ GESAsset parent;
+
+ /* <private> */
+ GESTrackElementAssetPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer __ges_reserved[GES_PADDING];
+};
+
+struct _GESTrackElementAssetClass
+{
+ GESAssetClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+const GESTrackType ges_track_element_asset_get_track_type (GESTrackElementAsset *asset);
+GES_API
+void ges_track_element_asset_set_track_type (GESTrackElementAsset * asset, GESTrackType type);
+
+G_END_DECLS
+#endif /* _GES_TRACK_ELEMENT_ASSET */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestrackelement
+ * @title: GESTrackElement
+ * @short_description: Base Class for objects contained in a GESTrack
+ *
+ * #GESTrackElement is the Base Class for any object that can be contained in a
+ * #GESTrack.
+ *
+ * It contains the basic information as to the location of the object within
+ * its container, like the start position, the inpoint, the duration and the
+ * priority.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-extractable.h"
+#include "ges-track-element.h"
+#include "ges-clip.h"
+#include "ges-meta-container.h"
+
+struct _GESTrackElementPrivate
+{
+ GESTrackType track_type;
+
+ GstElement *nleobject; /* The NleObject */
+ GstElement *element; /* The element contained in the nleobject (can be NULL) */
+
+ GESTrack *track;
+
+ gboolean locked; /* If TRUE, then moves in sync with its controlling
+ * GESClip */
+
+ GHashTable *bindings_hashtable; /* We need this if we want to be able to serialize
+ and deserialize keyframes */
+};
+
+enum
+{
+ PROP_0,
+ PROP_ACTIVE,
+ PROP_TRACK_TYPE,
+ PROP_TRACK,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+enum
+{
+ CONTROL_BINDING_ADDED,
+ CONTROL_BINDING_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint ges_track_element_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESTrackElement, ges_track_element,
+ GES_TYPE_TIMELINE_ELEMENT);
+
+static GstElement *ges_track_element_create_gnl_object_func (GESTrackElement *
+ object);
+
+static gboolean _set_start (GESTimelineElement * element, GstClockTime start);
+static gboolean _set_inpoint (GESTimelineElement * element,
+ GstClockTime inpoint);
+static gboolean _set_duration (GESTimelineElement * element,
+ GstClockTime duration);
+static gboolean _set_priority (GESTimelineElement * element, guint32 priority);
+GESTrackType _get_track_types (GESTimelineElement * object);
+
+static GParamSpec **default_list_children_properties (GESTrackElement * object,
+ guint * n_properties);
+
+static void
+_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
+ GstClockTime duration);
+
+static gboolean
+_lookup_child (GESTrackElement * object,
+ const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
+{
+ return
+ GES_TIMELINE_ELEMENT_GET_CLASS (object)->lookup_child
+ (GES_TIMELINE_ELEMENT (object), prop_name, (GObject **) element, pspec);
+}
+
+static gboolean
+strv_find_str (const gchar ** strv, const char *str)
+{
+ guint i;
+
+ if (strv == NULL)
+ return FALSE;
+
+ for (i = 0; strv[i]; i++) {
+ if (g_strcmp0 (strv[i], str) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static guint32
+_get_layer_priority (GESTimelineElement * element)
+{
+ if (!element->parent)
+ return GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY;
+
+ return ges_timeline_element_get_layer_priority (element->parent);
+}
+
+static void
+ges_track_element_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTrackElement *track_element = GES_TRACK_ELEMENT (object);
+
+ switch (property_id) {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, ges_track_element_is_active (track_element));
+ break;
+ case PROP_TRACK_TYPE:
+ g_value_set_flags (value, track_element->priv->track_type);
+ break;
+ case PROP_TRACK:
+ g_value_set_object (value, track_element->priv->track);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_track_element_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTrackElement *track_element = GES_TRACK_ELEMENT (object);
+
+ switch (property_id) {
+ case PROP_ACTIVE:
+ ges_track_element_set_active (track_element, g_value_get_boolean (value));
+ break;
+ case PROP_TRACK_TYPE:
+ track_element->priv->track_type = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_track_element_dispose (GObject * object)
+{
+ GESTrackElement *element = GES_TRACK_ELEMENT (object);
+ GESTrackElementPrivate *priv = element->priv;
+
+ if (priv->bindings_hashtable)
+ g_hash_table_destroy (priv->bindings_hashtable);
+
+ if (priv->nleobject) {
+ GstState cstate;
+
+ if (priv->track != NULL) {
+ g_error ("%p Still in %p, this means that you forgot"
+ " to remove it from the GESTrack it is contained in. You always need"
+ " to remove a GESTrackElement from its track before dropping the last"
+ " reference\n"
+ "This problem may also be caused by a refcounting bug in"
+ " the application or GES itself.", object, priv->track);
+ gst_element_get_state (priv->nleobject, &cstate, NULL, 0);
+ if (cstate != GST_STATE_NULL)
+ gst_element_set_state (priv->nleobject, GST_STATE_NULL);
+ }
+
+ g_object_set_qdata (G_OBJECT (priv->nleobject),
+ NLE_OBJECT_TRACK_ELEMENT_QUARK, NULL);
+ gst_object_unref (priv->nleobject);
+ priv->nleobject = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_track_element_parent_class)->dispose (object);
+}
+
+static void
+ges_track_element_constructed (GObject * gobject)
+{
+ GESTrackElementClass *class;
+ GstElement *nleobject;
+ gdouble media_duration_factor;
+ gchar *tmp;
+ GESTrackElement *object = GES_TRACK_ELEMENT (gobject);
+
+ GST_DEBUG_OBJECT (object, "Creating NleObject");
+
+ class = GES_TRACK_ELEMENT_GET_CLASS (object);
+ g_assert (class->create_gnl_object);
+
+ nleobject = class->create_gnl_object (object);
+ if (G_UNLIKELY (nleobject == NULL)) {
+ GST_ERROR_OBJECT (object, "Could not create NleObject");
+
+ return;
+ }
+
+ tmp = g_strdup_printf ("%s:%s", G_OBJECT_TYPE_NAME (object),
+ GST_OBJECT_NAME (nleobject));
+ gst_object_set_name (GST_OBJECT (nleobject), tmp);
+ g_free (tmp);
+
+ GST_DEBUG_OBJECT (object, "Got a valid NleObject, now filling it in");
+
+ object->priv->nleobject = gst_object_ref (nleobject);
+ g_object_set_qdata (G_OBJECT (nleobject), NLE_OBJECT_TRACK_ELEMENT_QUARK,
+ object);
+
+ /* Set some properties on the NleObject */
+ g_object_set (object->priv->nleobject,
+ "start", GES_TIMELINE_ELEMENT_START (object),
+ "inpoint", GES_TIMELINE_ELEMENT_INPOINT (object),
+ "duration", GES_TIMELINE_ELEMENT_DURATION (object),
+ "priority", GES_TIMELINE_ELEMENT_PRIORITY (object),
+ "active", object->active, NULL);
+
+ media_duration_factor =
+ ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
+ (object));
+ g_object_set (object->priv->nleobject,
+ "media-duration-factor", media_duration_factor, NULL);
+
+ G_OBJECT_CLASS (ges_track_element_parent_class)->constructed (gobject);
+}
+
+static void
+ges_track_element_class_init (GESTrackElementClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_track_element_get_property;
+ object_class->set_property = ges_track_element_set_property;
+ object_class->dispose = ges_track_element_dispose;
+ object_class->constructed = ges_track_element_constructed;
+
+
+ /**
+ * GESTrackElement:active:
+ *
+ * Whether the object should be taken into account in the #GESTrack output.
+ * If #FALSE, then its contents will not be used in the resulting track.
+ */
+ properties[PROP_ACTIVE] =
+ g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_ACTIVE,
+ properties[PROP_ACTIVE]);
+
+ properties[PROP_TRACK_TYPE] = g_param_spec_flags ("track-type", "Track Type",
+ "The track type of the object", GES_TYPE_TRACK_TYPE,
+ GES_TRACK_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_TRACK_TYPE,
+ properties[PROP_TRACK_TYPE]);
+
+ properties[PROP_TRACK] = g_param_spec_object ("track", "Track",
+ "The track the object is in", GES_TYPE_TRACK, G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_TRACK,
+ properties[PROP_TRACK]);
+
+ /**
+ * GESTrackElement::control-binding-added:
+ * @track_element: a #GESTrackElement
+ * @control_binding: the #GstControlBinding that has been added
+ *
+ * The control-binding-added signal is emitted each time a control binding
+ * is added for a child property of @track_element
+ */
+ ges_track_element_signals[CONTROL_BINDING_ADDED] =
+ g_signal_new ("control-binding-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GST_TYPE_CONTROL_BINDING);
+
+ /**
+ * GESTrackElement::control-binding-removed:
+ * @track_element: a #GESTrackElement
+ * @control_binding: the #GstControlBinding that has been removed
+ *
+ * The control-binding-removed signal is emitted each time a control binding
+ * is removed for a child property of @track_element
+ */
+ ges_track_element_signals[CONTROL_BINDING_REMOVED] =
+ g_signal_new ("control-binding-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GST_TYPE_CONTROL_BINDING);
+
+ element_class->set_start = _set_start;
+ element_class->set_duration = _set_duration;
+ element_class->set_inpoint = _set_inpoint;
+ element_class->set_priority = _set_priority;
+ element_class->get_track_types = _get_track_types;
+ element_class->deep_copy = ges_track_element_copy_properties;
+ element_class->get_layer_priority = _get_layer_priority;
+
+ klass->create_gnl_object = ges_track_element_create_gnl_object_func;
+ klass->list_children_properties = default_list_children_properties;
+ klass->lookup_child = _lookup_child;
+}
+
+static void
+ges_track_element_init (GESTrackElement * self)
+{
+ GESTrackElementPrivate *priv = self->priv =
+ ges_track_element_get_instance_private (self);
+
+ /* Sane default values */
+ GES_TIMELINE_ELEMENT_START (self) = 0;
+ GES_TIMELINE_ELEMENT_INPOINT (self) = 0;
+ GES_TIMELINE_ELEMENT_DURATION (self) = GST_SECOND;
+ GES_TIMELINE_ELEMENT_PRIORITY (self) = 0;
+ self->active = TRUE;
+
+ priv->bindings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+}
+
+static gfloat
+interpolate_values_for_position (GstTimedValue * first_value,
+ GstTimedValue * second_value, guint64 position, gboolean absolute)
+{
+ gfloat diff;
+ GstClockTime interval;
+ gfloat value_at_pos;
+
+ g_assert (second_value || first_value);
+
+ if (first_value == NULL)
+ return second_value->value;
+
+ if (second_value == NULL)
+ return first_value->value;
+
+ diff = second_value->value - first_value->value;
+ interval = second_value->timestamp - first_value->timestamp;
+
+ if (position > first_value->timestamp)
+ value_at_pos =
+ first_value->value + ((float) (position -
+ first_value->timestamp) / (float) interval) * diff;
+ else
+ value_at_pos =
+ first_value->value - ((float) (first_value->timestamp -
+ position) / (float) interval) * diff;
+
+ if (!absolute)
+ value_at_pos = CLAMP (value_at_pos, 0.0, 1.0);
+
+ return value_at_pos;
+}
+
+static void
+_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
+ GstClockTime duration)
+{
+ GParamSpec **specs;
+ guint n, n_specs;
+ GstControlBinding *binding;
+ GstTimedValueControlSource *source;
+ GESTrackElement *self = GES_TRACK_ELEMENT (element);
+
+ specs = ges_track_element_list_children_properties (self, &n_specs);
+
+ for (n = 0; n < n_specs; ++n) {
+ GList *values, *tmp;
+ gboolean absolute;
+ GstTimedValue *last, *first, *prev = NULL, *next = NULL;
+ gfloat value_at_pos;
+
+ binding = ges_track_element_get_control_binding (self, specs[n]->name);
+
+ if (!binding)
+ continue;
+
+ g_object_get (binding, "control_source", &source, NULL);
+
+ g_object_get (binding, "absolute", &absolute, NULL);
+ if (duration == 0) {
+ gst_timed_value_control_source_unset_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+ continue;
+ }
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+
+ if (g_list_length (values) == 0)
+ continue;
+
+ first = values->data;
+
+ for (tmp = values->next; tmp; tmp = tmp->next) {
+ next = tmp->data;
+
+ if (next->timestamp > inpoint)
+ break;
+ }
+ g_list_free (values);
+
+ value_at_pos =
+ interpolate_values_for_position (first, next, inpoint, absolute);
+ gst_timed_value_control_source_unset (source, first->timestamp);
+ gst_timed_value_control_source_set (source, inpoint, value_at_pos);
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+
+ if (duration != GST_CLOCK_TIME_NONE) {
+ last = g_list_last (values)->data;
+
+ for (tmp = g_list_last (values)->prev; tmp; tmp = tmp->prev) {
+ prev = tmp->data;
+
+ if (prev->timestamp < duration + inpoint)
+ break;
+ }
+ g_list_free (values);
+
+ value_at_pos =
+ interpolate_values_for_position (prev, last, duration + inpoint,
+ absolute);
+
+ gst_timed_value_control_source_unset (source, last->timestamp);
+ gst_timed_value_control_source_set (source, duration + inpoint,
+ value_at_pos);
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+ }
+
+ for (tmp = values; tmp; tmp = tmp->next) {
+ GstTimedValue *value = tmp->data;
+ if (value->timestamp < inpoint)
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ else if (duration != GST_CLOCK_TIME_NONE
+ && value->timestamp > duration + inpoint)
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ }
+ g_list_free (values);
+ }
+
+ g_free (specs);
+}
+
+static gboolean
+_set_start (GESTimelineElement * element, GstClockTime start)
+{
+ GESTrackElement *object = GES_TRACK_ELEMENT (element);
+
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ if (G_UNLIKELY (start == _START (object)))
+ return FALSE;
+
+ g_object_set (object->priv->nleobject, "start", start, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
+{
+ GESTrackElement *object = GES_TRACK_ELEMENT (element);
+
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ if (G_UNLIKELY (inpoint == _INPOINT (object)))
+
+ return FALSE;
+
+ g_object_set (object->priv->nleobject, "inpoint", inpoint, NULL);
+ _update_control_bindings (element, inpoint, GST_CLOCK_TIME_NONE);
+
+ return TRUE;
+}
+
+static gboolean
+_set_duration (GESTimelineElement * element, GstClockTime duration)
+{
+ GESTrackElement *object = GES_TRACK_ELEMENT (element);
+ GESTrackElementPrivate *priv = object->priv;
+
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ if (GST_CLOCK_TIME_IS_VALID (_MAXDURATION (element)) &&
+ duration > _INPOINT (object) + _MAXDURATION (element))
+ duration = _MAXDURATION (element) - _INPOINT (object);
+
+ if (G_UNLIKELY (duration == _DURATION (object)))
+ return FALSE;
+
+ g_object_set (priv->nleobject, "duration", duration, NULL);
+
+ _update_control_bindings (element, ges_timeline_element_get_inpoint (element),
+ duration);
+
+ return TRUE;
+}
+
+static gboolean
+_set_priority (GESTimelineElement * element, guint32 priority)
+{
+ GESTrackElement *object = GES_TRACK_ELEMENT (element);
+
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ if (priority < MIN_NLE_PRIO) {
+ GST_INFO_OBJECT (element, "Priority (%d) < MIN_NLE_PRIO, setting it to %d",
+ priority, MIN_NLE_PRIO);
+ priority = MIN_NLE_PRIO;
+ }
+
+ GST_DEBUG_OBJECT (object, "priority:%" G_GUINT32_FORMAT, priority);
+
+ if (G_UNLIKELY (priority == _PRIORITY (object)))
+ return FALSE;
+
+ g_object_set (object->priv->nleobject, "priority", priority, NULL);
+
+ return TRUE;
+}
+
+GESTrackType
+_get_track_types (GESTimelineElement * object)
+{
+ return ges_track_element_get_track_type (GES_TRACK_ELEMENT (object));
+}
+
+/**
+ * ges_track_element_set_active:
+ * @object: a #GESTrackElement
+ * @active: visibility
+ *
+ * Sets the usage of the @object. If @active is %TRUE, the object will be used for
+ * playback and rendering, else it will be ignored.
+ *
+ * Returns: %TRUE if the property was toggled, else %FALSE
+ */
+gboolean
+ges_track_element_set_active (GESTrackElement * object, gboolean active)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ GST_DEBUG_OBJECT (object, "object:%p, active:%d", object, active);
+
+ if (G_UNLIKELY (active == object->active))
+ return FALSE;
+
+ g_object_set (object->priv->nleobject, "active", active, NULL);
+
+ if (active != object->active) {
+ object->active = active;
+ if (GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed)
+ GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed (object, active);
+ }
+
+ return TRUE;
+}
+
+void
+ges_track_element_set_track_type (GESTrackElement * object, GESTrackType type)
+{
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
+
+ if (object->priv->track_type != type) {
+ object->priv->track_type = type;
+ g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK_TYPE]);
+ }
+}
+
+GESTrackType
+ges_track_element_get_track_type (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), GES_TRACK_TYPE_UNKNOWN);
+
+ return object->priv->track_type;
+}
+
+/* default 'create_gnl_object' virtual method implementation */
+static GstElement *
+ges_track_element_create_gnl_object_func (GESTrackElement * self)
+{
+ GESTrackElementClass *klass = NULL;
+ GstElement *child = NULL;
+ GstElement *nleobject;
+
+ klass = GES_TRACK_ELEMENT_GET_CLASS (self);
+
+ if (G_UNLIKELY (self->priv->nleobject != NULL))
+ goto already_have_nleobject;
+
+ if (G_UNLIKELY (klass->nleobject_factorytype == NULL))
+ goto no_nlefactory;
+
+ GST_DEBUG ("Creating a supporting nleobject of type '%s'",
+ klass->nleobject_factorytype);
+
+ nleobject = gst_element_factory_make (klass->nleobject_factorytype, NULL);
+
+ if (G_UNLIKELY (nleobject == NULL))
+ goto no_nleobject;
+
+ if (klass->create_element) {
+ GST_DEBUG ("Calling subclass 'create_element' vmethod");
+ child = klass->create_element (self);
+
+ if (G_UNLIKELY (!child))
+ goto child_failure;
+
+ if (!gst_bin_add (GST_BIN (nleobject), child))
+ goto add_failure;
+
+ GST_DEBUG ("Succesfully got the element to put in the nleobject");
+ self->priv->element = child;
+ }
+
+ GST_DEBUG ("done");
+ return nleobject;
+
+
+ /* ERROR CASES */
+
+already_have_nleobject:
+ {
+ GST_ERROR ("Already controlling a NleObject %s",
+ GST_ELEMENT_NAME (self->priv->nleobject));
+ return NULL;
+ }
+
+no_nlefactory:
+ {
+ GST_ERROR ("No GESTrackElement::nleobject_factorytype implementation!");
+ return NULL;
+ }
+
+no_nleobject:
+ {
+ GST_ERROR ("Error creating a nleobject of type '%s'",
+ klass->nleobject_factorytype);
+ return NULL;
+ }
+
+child_failure:
+ {
+ GST_ERROR ("create_element returned NULL");
+ gst_object_unref (nleobject);
+ return NULL;
+ }
+
+add_failure:
+ {
+ GST_ERROR ("Error adding the contents to the nleobject");
+ gst_object_unref (child);
+ gst_object_unref (nleobject);
+ return NULL;
+ }
+}
+
+static void
+ges_track_element_add_child_props (GESTrackElement * self,
+ GstElement * child, const gchar ** wanted_categories,
+ const gchar ** blacklist, const gchar ** whitelist)
+{
+ GstElementFactory *factory;
+ const gchar *klass;
+ GParamSpec **parray;
+ GObjectClass *gobject_klass;
+ gchar **categories;
+ guint i;
+
+ factory = gst_element_get_factory (child);
+ klass = gst_element_factory_get_metadata (factory,
+ GST_ELEMENT_METADATA_KLASS);
+
+ if (strv_find_str (blacklist, GST_OBJECT_NAME (factory))) {
+ GST_DEBUG_OBJECT (self, "%s blacklisted", GST_OBJECT_NAME (factory));
+ return;
+ }
+
+ GST_DEBUG_OBJECT (self, "Looking at element '%s' of klass '%s'",
+ GST_ELEMENT_NAME (child), klass);
+
+ categories = g_strsplit (klass, "/", 0);
+
+ for (i = 0; categories[i]; i++) {
+ if ((!wanted_categories ||
+ strv_find_str (wanted_categories, categories[i]))) {
+ guint i, nb_specs;
+
+ gobject_klass = G_OBJECT_GET_CLASS (child);
+ parray = g_object_class_list_properties (gobject_klass, &nb_specs);
+ for (i = 0; i < nb_specs; i++) {
+ if ((!whitelist && (parray[i]->flags & G_PARAM_WRITABLE))
+ || (strv_find_str (whitelist, parray[i]->name))) {
+ ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT
+ (self), parray[i], G_OBJECT (child));
+ }
+ }
+ g_free (parray);
+
+ GST_DEBUG
+ ("%d configurable properties of '%s' added to property hashtable",
+ nb_specs, GST_ELEMENT_NAME (child));
+ break;
+ }
+ }
+
+ g_strfreev (categories);
+}
+
+/**
+ * ges_track_element_add_children_props:
+ * @self: The #GESTrackElement to set chidlren props on
+ * @element: The GstElement to retrieve properties from
+ * @wanted_categories: (array zero-terminated=1) (transfer none) (allow-none):
+ * An array of categories of GstElement to
+ * take into account (as defined in the factory meta "klass" field)
+ * @blacklist: (array zero-terminated=1) (transfer none) (allow-none): A
+ * blacklist of elements factory names to not take into account
+ * @whitelist: (array zero-terminated=1) (transfer none) (allow-none): A list
+ * of propery names to add as children properties
+ *
+ * Looks for the properties defines with the various parametters and add
+ * them to the hashtable of children properties.
+ *
+ * To be used by subclasses only
+ */
+void
+ges_track_element_add_children_props (GESTrackElement * self,
+ GstElement * element, const gchar ** wanted_categories,
+ const gchar ** blacklist, const gchar ** whitelist)
+{
+ GValue item = { 0, };
+ GstIterator *it;
+ gboolean done = FALSE;
+
+ if (!GST_IS_BIN (element)) {
+ ges_track_element_add_child_props (self, element, wanted_categories,
+ blacklist, whitelist);
+ return;
+ }
+
+ /* We go over child elements recursivly, and add writable properties to the
+ * hashtable */
+ it = gst_bin_iterate_recurse (GST_BIN (element));
+ while (!done) {
+ switch (gst_iterator_next (it, &item)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = g_value_get_object (&item);
+ ges_track_element_add_child_props (self, child, wanted_categories,
+ blacklist, whitelist);
+ g_value_reset (&item);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ /* FIXME, properly restart the process */
+ GST_DEBUG ("iterator resync");
+ gst_iterator_resync (it);
+ break;
+
+ case GST_ITERATOR_DONE:
+ GST_DEBUG ("iterator done");
+ done = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ g_value_unset (&item);
+ }
+ gst_iterator_free (it);
+}
+
+/* INTERNAL USAGE */
+gboolean
+ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
+{
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ GST_DEBUG_OBJECT (object, "new track: %" GST_PTR_FORMAT, track);
+
+ object->priv->track = track;
+
+ if (object->priv->track) {
+ ges_track_element_set_track_type (object, track->type);
+
+ g_object_set (object->priv->nleobject,
+ "caps", ges_track_get_caps (object->priv->track), NULL);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK]);
+ return ret;
+}
+
+/**
+ * ges_track_element_get_all_control_bindings
+ * @trackelement: The #TrackElement from which to get all set bindings
+ *
+ * Returns: (element-type gchar* GstControlBinding)(transfer none): A
+ * #GHashTable containing all property_name: GstControlBinding
+ */
+GHashTable *
+ges_track_element_get_all_control_bindings (GESTrackElement * trackelement)
+{
+ GESTrackElementPrivate *priv = GES_TRACK_ELEMENT (trackelement)->priv;
+
+ return priv->bindings_hashtable;
+}
+
+/**
+ * ges_track_element_get_track:
+ * @object: a #GESTrackElement
+ *
+ * Get the #GESTrack to which this object belongs.
+ *
+ * Returns: (transfer none) (nullable): The #GESTrack to which this object
+ * belongs. Can be %NULL if it is not in any track
+ */
+GESTrack *
+ges_track_element_get_track (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
+
+ return object->priv->track;
+}
+
+/**
+ * ges_track_element_get_gnlobject:
+ * @object: a #GESTrackElement
+ *
+ * Get the NleObject object this object is controlling.
+ *
+ * Returns: (transfer none): the NleObject object this object is controlling.
+ *
+ * Deprecated: use #ges_track_element_get_nleobject instead.
+ */
+GstElement *
+ges_track_element_get_gnlobject (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
+
+ return object->priv->nleobject;
+}
+
+/**
+ * ges_track_element_get_nleobject:
+ * @object: a #GESTrackElement
+ *
+ * Get the GNonLin object this object is controlling.
+ *
+ * Returns: (transfer none): the GNonLin object this object is controlling.
+ *
+ * Since: 1.6
+ */
+GstElement *
+ges_track_element_get_nleobject (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
+
+ return object->priv->nleobject;
+}
+
+/**
+ * ges_track_element_get_element:
+ * @object: a #GESTrackElement
+ *
+ * Get the #GstElement this track element is controlling within GNonLin.
+ *
+ * Returns: (transfer none): the #GstElement this track element is controlling
+ * within GNonLin.
+ */
+GstElement *
+ges_track_element_get_element (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
+
+ return object->priv->element;
+}
+
+/**
+ * ges_track_element_is_active:
+ * @object: a #GESTrackElement
+ *
+ * Lets you know if @object will be used for playback and rendering,
+ * or not.
+ *
+ * Returns: %TRUE if @object is active, %FALSE otherwize
+ */
+gboolean
+ges_track_element_is_active (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+ g_return_val_if_fail (object->priv->nleobject, FALSE);
+
+ return object->active;
+}
+
+/**
+ * ges_track_element_lookup_child:
+ * @object: object to lookup the property in
+ * @prop_name: name of the property to look up. You can specify the name of the
+ * class as such: "ClassName::property-name", to guarantee that you get the
+ * proper GParamSpec in case various GstElement-s contain the same property
+ * name. If you don't do so, you will get the first element found, having
+ * this property and the and the corresponding GParamSpec.
+ * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
+ * takes the real object to set property on
+ * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
+ * describing the property
+ *
+ * Looks up which @element and @pspec would be effected by the given @name. If various
+ * contained elements have this property name you will get the first one, unless you
+ * specify the class name in @name.
+ *
+ * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
+ * case the values for @pspec and @element are not modified. Unref @element after
+ * usage.
+ *
+ * Deprecated: Use #ges_timeline_element_lookup_child
+ */
+gboolean
+ges_track_element_lookup_child (GESTrackElement * object,
+ const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
+{
+ return ges_timeline_element_lookup_child (GES_TIMELINE_ELEMENT (object),
+ prop_name, ((GObject **) element), pspec);
+}
+
+/**
+ * ges_track_element_set_child_property_by_pspec: (skip):
+ * @object: a #GESTrackElement
+ * @pspec: The #GParamSpec that specifies the property you want to set
+ * @value: the value
+ *
+ * Sets a property of a child of @object.
+ *
+ * Deprecated: Use #ges_timeline_element_set_child_property_by_spec
+ */
+void
+ges_track_element_set_child_property_by_pspec (GESTrackElement * object,
+ GParamSpec * pspec, GValue * value)
+{
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
+
+ ges_timeline_element_set_child_property_by_pspec (GES_TIMELINE_ELEMENT
+ (object), pspec, value);
+
+ return;
+}
+
+/**
+ * ges_track_element_set_child_property_valist: (skip):
+ * @object: The #GESTrackElement parent object
+ * @first_property_name: The name of the first property to set
+ * @var_args: value for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Sets a property of a child of @object. If there are various child elements
+ * that have the same property name, you can distinguish them using the following
+ * syntax: 'ClasseName::property_name' as property name. If you don't, the
+ * corresponding property of the first element found will be set.
+ *
+ * Deprecated: Use #ges_timeline_element_set_child_property_valist
+ */
+void
+ges_track_element_set_child_property_valist (GESTrackElement * object,
+ const gchar * first_property_name, va_list var_args)
+{
+ ges_timeline_element_set_child_property_valist (GES_TIMELINE_ELEMENT (object),
+ first_property_name, var_args);
+}
+
+/**
+ * ges_track_element_set_child_properties: (skip):
+ * @object: The #GESTrackElement parent object
+ * @first_property_name: The name of the first property to set
+ * @...: value for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Sets a property of a child of @object. If there are various child elements
+ * that have the same property name, you can distinguish them using the following
+ * syntax: 'ClasseName::property_name' as property name. If you don't, the
+ * corresponding property of the first element found will be set.
+ *
+ * Deprecated: Use #ges_timeline_element_set_child_properties
+ */
+void
+ges_track_element_set_child_properties (GESTrackElement * object,
+ const gchar * first_property_name, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
+
+ va_start (var_args, first_property_name);
+ ges_track_element_set_child_property_valist (object, first_property_name,
+ var_args);
+ va_end (var_args);
+}
+
+/**
+ * ges_track_element_get_child_property_valist: (skip):
+ * @object: The #GESTrackElement parent object
+ * @first_property_name: The name of the first property to get
+ * @var_args: value for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Gets a property of a child of @object. If there are various child elements
+ * that have the same property name, you can distinguish them using the following
+ * syntax: 'ClasseName::property_name' as property name. If you don't, the
+ * corresponding property of the first element found will be set.
+ *
+ * Deprecated: Use #ges_timeline_element_get_child_property_valist
+ */
+void
+ges_track_element_get_child_property_valist (GESTrackElement * object,
+ const gchar * first_property_name, va_list var_args)
+{
+ ges_timeline_element_get_child_property_valist (GES_TIMELINE_ELEMENT (object),
+ first_property_name, var_args);
+}
+
+/**
+ * ges_track_element_list_children_properties:
+ * @object: The #GESTrackElement to get the list of children properties from
+ * @n_properties: (out): return location for the length of the returned array
+ *
+ * Gets an array of #GParamSpec* for all configurable properties of the
+ * children of @object.
+ *
+ * Returns: (transfer full) (array length=n_properties): an array of #GParamSpec* which should be freed after use or
+ * %NULL if something went wrong
+ *
+ * Deprecated: Use #ges_timeline_element_list_children_properties
+ */
+GParamSpec **
+ges_track_element_list_children_properties (GESTrackElement * object,
+ guint * n_properties)
+{
+ return
+ ges_timeline_element_list_children_properties (GES_TIMELINE_ELEMENT
+ (object), n_properties);
+}
+
+/**
+ * ges_track_element_get_child_properties: (skip):
+ * @object: The origin #GESTrackElement
+ * @first_property_name: The name of the first property to get
+ * @...: return location for the first property, followed optionally by more
+ * name/return location pairs, followed by NULL
+ *
+ * Gets properties of a child of @object.
+ *
+ * Deprecated: Use #ges_timeline_element_get_child_properties
+ */
+void
+ges_track_element_get_child_properties (GESTrackElement * object,
+ const gchar * first_property_name, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
+
+ va_start (var_args, first_property_name);
+ ges_track_element_get_child_property_valist (object, first_property_name,
+ var_args);
+ va_end (var_args);
+}
+
+/**
+ * ges_track_element_get_child_property_by_pspec: (skip):
+ * @object: a #GESTrackElement
+ * @pspec: The #GParamSpec that specifies the property you want to get
+ * @value: (out): return location for the value
+ *
+ * Gets a property of a child of @object.
+ *
+ * Deprecated: Use #ges_timeline_element_get_child_property_by_pspec
+ */
+void
+ges_track_element_get_child_property_by_pspec (GESTrackElement * object,
+ GParamSpec * pspec, GValue * value)
+{
+ ges_timeline_element_get_child_property_by_pspec (GES_TIMELINE_ELEMENT
+ (object), pspec, value);
+}
+
+/**
+ * ges_track_element_set_child_property: (skip):
+ * @object: The origin #GESTrackElement
+ * @property_name: The name of the property
+ * @value: the value
+ *
+ * Sets a property of a GstElement contained in @object.
+ *
+ * Note that #ges_track_element_set_child_property is really
+ * intended for language bindings, #ges_track_element_set_child_properties
+ * is much more convenient for C programming.
+ *
+ * Returns: %TRUE if the property was set, %FALSE otherwize
+ *
+ * Deprecated: use #ges_timeline_element_set_child_property instead
+ */
+gboolean
+ges_track_element_set_child_property (GESTrackElement * object,
+ const gchar * property_name, GValue * value)
+{
+ return ges_timeline_element_set_child_property (GES_TIMELINE_ELEMENT (object),
+ property_name, value);
+}
+
+/**
+ * ges_track_element_get_child_property: (skip):
+ * @object: The origin #GESTrackElement
+ * @property_name: The name of the property
+ * @value: (out): return location for the property value, it will
+ * be initialized if it is initialized with 0
+ *
+ * In general, a copy is made of the property contents and
+ * the caller is responsible for freeing the memory by calling
+ * g_value_unset().
+ *
+ * Gets a property of a GstElement contained in @object.
+ *
+ * Note that #ges_track_element_get_child_property is really
+ * intended for language bindings, #ges_track_element_get_child_properties
+ * is much more convenient for C programming.
+ *
+ * Returns: %TRUE if the property was found, %FALSE otherwize
+ *
+ * Deprecated: Use #ges_timeline_element_get_child_property
+ */
+gboolean
+ges_track_element_get_child_property (GESTrackElement * object,
+ const gchar * property_name, GValue * value)
+{
+ return ges_timeline_element_get_child_property (GES_TIMELINE_ELEMENT (object),
+ property_name, value);
+}
+
+static GParamSpec **
+default_list_children_properties (GESTrackElement * object,
+ guint * n_properties)
+{
+ return
+ GES_TIMELINE_ELEMENT_GET_CLASS (object)->list_children_properties
+ (GES_TIMELINE_ELEMENT (object), n_properties);
+}
+
+void
+ges_track_element_copy_properties (GESTimelineElement * element,
+ GESTimelineElement * elementcopy)
+{
+ GParamSpec **specs;
+ guint n, n_specs;
+ GValue val = { 0 };
+ GESTrackElement *copy = GES_TRACK_ELEMENT (elementcopy);
+
+ specs =
+ ges_track_element_list_children_properties (GES_TRACK_ELEMENT (element),
+ &n_specs);
+ for (n = 0; n < n_specs; ++n) {
+ if (!(specs[n]->flags & G_PARAM_WRITABLE))
+ continue;
+ g_value_init (&val, specs[n]->value_type);
+ ges_track_element_get_child_property_by_pspec (GES_TRACK_ELEMENT (element),
+ specs[n], &val);
+ ges_track_element_set_child_property_by_pspec (copy, specs[n], &val);
+ g_value_unset (&val);
+ }
+
+ g_free (specs);
+}
+
+static void
+_split_binding (GESTrackElement * element, GESTrackElement * new_element,
+ guint64 position, GstTimedValueControlSource * source,
+ GstTimedValueControlSource * new_source, gboolean absolute)
+{
+ GstTimedValue *last_value = NULL;
+ gboolean past_position = FALSE;
+ GList *values, *tmp;
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+
+ for (tmp = values; tmp; tmp = tmp->next) {
+ GstTimedValue *value = tmp->data;
+
+ if (value->timestamp > position && !past_position) {
+ gfloat value_at_pos;
+
+ /* FIXME We should be able to use gst_control_source_get_value so
+ * all modes are handled. Right now that method only works if the value
+ * we are looking for is between two actual keyframes which is not enough
+ * in our case. bug #706621 */
+ value_at_pos =
+ interpolate_values_for_position (last_value, value, position,
+ absolute);
+
+ past_position = TRUE;
+
+ gst_timed_value_control_source_set (new_source, position, value_at_pos);
+ gst_timed_value_control_source_set (new_source, value->timestamp,
+ value->value);
+
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ gst_timed_value_control_source_set (source, position, value_at_pos);
+ } else if (past_position) {
+ gst_timed_value_control_source_set (new_source, value->timestamp,
+ value->value);
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ }
+ last_value = value;
+
+ }
+ g_list_free (values);
+}
+
+static void
+_copy_binding (GESTrackElement * element, GESTrackElement * new_element,
+ guint64 position, GstTimedValueControlSource * source,
+ GstTimedValueControlSource * new_source, gboolean absolute)
+{
+ GList *values, *tmp;
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+ for (tmp = values; tmp; tmp = tmp->next) {
+ GstTimedValue *value = tmp->data;
+
+ gst_timed_value_control_source_set (new_source, value->timestamp,
+ value->value);
+ }
+ g_list_free (values);
+}
+
+/* position == GST_CLOCK_TIME_NONE means that we do a simple copy
+ * other position means that the function will do a splitting
+ * and thus interpollate the values in the element and new_element
+ */
+void
+ges_track_element_copy_bindings (GESTrackElement * element,
+ GESTrackElement * new_element, guint64 position)
+{
+ GParamSpec **specs;
+ guint n, n_specs;
+ gboolean absolute;
+ GstControlBinding *binding;
+ GstTimedValueControlSource *source, *new_source;
+
+ specs =
+ ges_track_element_list_children_properties (GES_TRACK_ELEMENT (element),
+ &n_specs);
+ for (n = 0; n < n_specs; ++n) {
+ GstInterpolationMode mode;
+
+ binding = ges_track_element_get_control_binding (element, specs[n]->name);
+ if (!binding)
+ continue;
+
+ /* FIXME : this should work as well with other types of control sources */
+ g_object_get (binding, "control_source", &source, NULL);
+ if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source))
+ continue;
+
+ g_object_get (binding, "absolute", &absolute, NULL);
+ g_object_get (source, "mode", &mode, NULL);
+
+ new_source =
+ GST_TIMED_VALUE_CONTROL_SOURCE (gst_interpolation_control_source_new
+ ());
+ g_object_set (new_source, "mode", mode, NULL);
+
+ if (GST_CLOCK_TIME_IS_VALID (position))
+ _split_binding (element, new_element, position, source, new_source,
+ absolute);
+ else
+ _copy_binding (element, new_element, position, source, new_source,
+ absolute);
+
+ /* We only manage direct (absolute) bindings, see TODO in set_control_source */
+ if (absolute)
+ ges_track_element_set_control_source (new_element,
+ GST_CONTROL_SOURCE (new_source), specs[n]->name, "direct-absolute");
+ else
+ ges_track_element_set_control_source (new_element,
+ GST_CONTROL_SOURCE (new_source), specs[n]->name, "direct");
+ }
+
+ g_free (specs);
+}
+
+/**
+ * ges_track_element_edit:
+ * @object: the #GESTrackElement to edit
+ * @layers: (element-type GESLayer): The layers you want the edit to
+ * happen in, %NULL means that the edition is done in all the
+ * #GESLayers contained in the current timeline.
+ * FIXME: This is not implemented yet.
+ * @mode: The #GESEditMode in which the edition will happen.
+ * @edge: The #GESEdge the edit should happen on.
+ * @position: The position at which to edit @object (in nanosecond)
+ *
+ * Edit @object in the different exisiting #GESEditMode modes. In the case of
+ * slide, and roll, you need to specify a #GESEdge
+ *
+ * Returns: %TRUE if the object as been edited properly, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_track_element_edit (GESTrackElement * object,
+ GList * layers, GESEditMode mode, GESEdge edge, guint64 position)
+{
+ GESTrack *track = ges_track_element_get_track (object);
+ GESTimeline *timeline;
+
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+
+ if (G_UNLIKELY (!track)) {
+ GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in "
+ "any Track yet.", mode);
+ return FALSE;
+ }
+
+ timeline = GES_TIMELINE (ges_track_get_timeline (track));
+
+ if (G_UNLIKELY (!timeline)) {
+ GST_WARNING_OBJECT (object, "Trying to edit in %d mode but "
+ "track %p is not in any timeline yet.", mode, track);
+ return FALSE;
+ }
+
+ switch (mode) {
+ case GES_EDIT_MODE_NORMAL:
+ return timeline_move_object (timeline, GES_TIMELINE_ELEMENT (object), -1,
+ layers, edge, position);
+ break;
+ case GES_EDIT_MODE_TRIM:
+ return timeline_trim_object (timeline, GES_TIMELINE_ELEMENT (object), -1,
+ layers, edge, position);
+ break;
+ case GES_EDIT_MODE_RIPPLE:
+ return timeline_ripple_object (timeline, GES_TIMELINE_ELEMENT (object),
+ GES_TIMELINE_ELEMENT_PRIORITY (object) / LAYER_HEIGHT,
+ layers, edge, position);
+ break;
+ case GES_EDIT_MODE_ROLL:
+ return timeline_roll_object (timeline, GES_TIMELINE_ELEMENT (object),
+ layers, edge, position);
+ break;
+ case GES_EDIT_MODE_SLIDE:
+ return timeline_slide_object (timeline, object, layers, edge, position);
+ break;
+ default:
+ GST_ERROR ("Unkown edit mode: %d", mode);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * ges_track_element_remove_control_binding:
+ * @object: the #GESTrackElement on which to set a control binding
+ * @property_name: The name of the property to control.
+ *
+ * Removes a #GstControlBinding from @object.
+ *
+ * Returns: %TRUE if the binding could be removed, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_track_element_remove_control_binding (GESTrackElement * object,
+ const gchar * property_name)
+{
+ GESTrackElementPrivate *priv;
+ GstControlBinding *binding;
+ GstObject *target;
+
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+
+ priv = GES_TRACK_ELEMENT (object)->priv;
+ binding =
+ (GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
+ property_name);
+
+ if (binding) {
+ g_object_get (binding, "object", &target, NULL);
+ GST_DEBUG_OBJECT (object, "Removing binding %p for property %s", binding,
+ property_name);
+
+ gst_object_ref (binding);
+ gst_object_remove_control_binding (target, binding);
+
+ g_signal_emit (object, ges_track_element_signals[CONTROL_BINDING_REMOVED],
+ 0, binding);
+
+ gst_object_unref (target);
+ gst_object_unref (binding);
+ g_hash_table_remove (priv->bindings_hashtable, property_name);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * ges_track_element_set_control_source:
+ * @object: the #GESTrackElement on which to set a control binding
+ * @source: the #GstControlSource to set on the binding.
+ * @property_name: The name of the property to control.
+ * @binding_type: The type of binding to create. Currently the following values are valid:
+ * - "direct": See #gst_direct_control_binding_new
+ * - "direct-absolute": See #gst_direct_control_binding_new_absolute
+ *
+ * Creates a #GstControlBinding and adds it to the #GstElement concerned by the
+ * property. Use the same syntax as #ges_track_element_lookup_child for
+ * the property name.
+ *
+ * Returns: %TRUE if the binding could be created and added, %FALSE if an error
+ * occured
+ */
+gboolean
+ges_track_element_set_control_source (GESTrackElement * object,
+ GstControlSource * source,
+ const gchar * property_name, const gchar * binding_type)
+{
+ GESTrackElementPrivate *priv;
+ GstElement *element;
+ GParamSpec *pspec;
+ GstControlBinding *binding;
+ gboolean direct, direct_absolute;
+
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+ priv = GES_TRACK_ELEMENT (object)->priv;
+
+ if (G_UNLIKELY (!(GST_IS_CONTROL_SOURCE (source)))) {
+ GST_WARNING
+ ("You need to provide a non-null control source to build a new control binding");
+ return FALSE;
+ }
+
+ if (!ges_track_element_lookup_child (object, property_name, &element, &pspec)) {
+ GST_WARNING ("You need to provide a valid and controllable property name");
+ return FALSE;
+ }
+
+ /* TODO : update this according to new types of bindings */
+ direct = !g_strcmp0 (binding_type, "direct");
+ direct_absolute = !g_strcmp0 (binding_type, "direct-absolute");
+
+ if (direct || direct_absolute) {
+ /* First remove existing binding */
+ if (ges_track_element_remove_control_binding (object, property_name)) {
+ GST_LOG ("Removed old binding for property %s", property_name);
+ }
+
+ if (direct_absolute)
+ binding =
+ gst_direct_control_binding_new_absolute (GST_OBJECT (element),
+ property_name, source);
+ else
+ binding =
+ gst_direct_control_binding_new (GST_OBJECT (element), property_name,
+ source);
+
+ gst_object_add_control_binding (GST_OBJECT (element), binding);
+ g_hash_table_insert (priv->bindings_hashtable, g_strdup (property_name),
+ binding);
+ g_signal_emit (object, ges_track_element_signals[CONTROL_BINDING_ADDED],
+ 0, binding);
+ return TRUE;
+ }
+
+ GST_WARNING ("Binding type must be in [direct]");
+
+ return FALSE;
+}
+
+/**
+ * ges_track_element_get_control_binding:
+ * @object: the #GESTrackElement in which to lookup the bindings.
+ * @property_name: The property_name to which the binding is associated.
+ *
+ * Looks up the various controlled properties for that #GESTrackElement,
+ * and returns the #GstControlBinding which controls @property_name.
+ *
+ * Returns: (transfer none) (nullable): the #GstControlBinding associated with
+ * @property_name, or %NULL if that property is not controlled.
+ */
+GstControlBinding *
+ges_track_element_get_control_binding (GESTrackElement * object,
+ const gchar * property_name)
+{
+ GESTrackElementPrivate *priv;
+ GstControlBinding *binding;
+
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
+
+ priv = GES_TRACK_ELEMENT (object)->priv;
+
+ binding =
+ (GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
+ property_name);
+ return binding;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_TRACK_ELEMENT
+#define _GES_TRACK_ELEMENT
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-clip.h>
+#include <ges/ges-track.h>
+#include <gst/controller/gstdirectcontrolbinding.h>
+#include <gst/controller/gstinterpolationcontrolsource.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TRACK_ELEMENT ges_track_element_get_type()
+
+#define GES_TRACK_ELEMENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TRACK_ELEMENT, GESTrackElement))
+
+#define GES_TRACK_ELEMENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TRACK_ELEMENT, GESTrackElementClass))
+
+#define GES_IS_TRACK_ELEMENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TRACK_ELEMENT))
+
+#define GES_IS_TRACK_ELEMENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TRACK_ELEMENT))
+
+#define GES_TRACK_ELEMENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TRACK_ELEMENT, GESTrackElementClass))
+
+typedef struct _GESTrackElementPrivate GESTrackElementPrivate;
+
+/**
+ * GESTrackElement:
+ *
+ * The GESTrackElement base class.
+ */
+struct _GESTrackElement {
+ GESTimelineElement parent;
+
+ /*< private >*/
+ gboolean active;
+
+ GESTrackElementPrivate *priv;
+
+ GESAsset *asset;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+/**
+ * GESTrackElementClass:
+ * @nleobject_factorytype: name of the GNonLin GStElementFactory type to use.
+ * @create_gnl_object: method to create the GNonLin container object.
+ * @create_element: method to return the GstElement to put in the nleobject.
+ * @active_changed: active property of nleobject has changed
+ * @list_children_properties: method to get children properties that user could
+ * like to configure.
+ * The default implementation will create an object
+ * of type @nleobject_factorytype and call
+ * @create_element.
+ * DeprecatedUse: GESTimelineElement.list_children_properties instead
+ * @lookup_child: method letting subclasses look for a child, overriding the
+ * simple standard behaviour. This vmethod can be used for example
+ * in the case where you want the name of a child property to be
+ * 'overriden'. A good example of where it is usefull is the
+ * GESTitleSource where we have a videotestsrc which has a
+ * 'foreground-color' property that is used in the TitleSource to
+ * set the background color of the title, in that case, this method
+ * has been overriden so that we tweak the name passed has parametter
+ * to rename "background" to "foreground-backend" making our API
+ * understandable.
+ * Deprecated: use GESTimelineElement.lookup_child instead
+ *
+ * Subclasses can override the @create_gnl_object method to override what type
+ * of GNonLin object will be created.
+ */
+struct _GESTrackElementClass {
+ /*< private >*/
+ GESTimelineElementClass parent_class;
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+ const gchar *nleobject_factorytype;
+ GstElement* (*create_gnl_object) (GESTrackElement * object);
+ GstElement* (*create_element) (GESTrackElement * object);
+
+ void (*active_changed) (GESTrackElement *object, gboolean active);
+
+ /*< private >*/
+ /* signals (currently unused) */
+ void (*changed) (GESTrackElement * object);
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+ GParamSpec** (*list_children_properties) (GESTrackElement * object,
+ guint *n_properties);
+ gboolean (*lookup_child) (GESTrackElement *object,
+ const gchar *prop_name,
+ GstElement **element,
+ GParamSpec **pspec);
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING_LARGE];
+};
+
+GES_API
+GType ges_track_element_get_type (void);
+
+GES_API
+GESTrack* ges_track_element_get_track (GESTrackElement * object);
+
+GES_API
+GESTrackType ges_track_element_get_track_type (GESTrackElement * object);
+GES_API
+void ges_track_element_set_track_type (GESTrackElement * object,
+ GESTrackType type);
+
+GES_API
+GstElement * ges_track_element_get_nleobject (GESTrackElement * object);
+GES_API
+GstElement * ges_track_element_get_gnlobject (GESTrackElement * object);
+
+GES_API
+GstElement * ges_track_element_get_element (GESTrackElement * object);
+
+GES_API
+gboolean ges_track_element_set_active (GESTrackElement * object,
+ gboolean active);
+
+GES_API
+gboolean ges_track_element_is_active (GESTrackElement * object);
+
+GES_API GParamSpec **
+ges_track_element_list_children_properties (GESTrackElement *object,
+ guint *n_properties);
+
+GES_API
+gboolean ges_track_element_lookup_child (GESTrackElement *object,
+ const gchar *prop_name,
+ GstElement **element,
+ GParamSpec **pspec);
+
+GES_API void
+ges_track_element_get_child_property_by_pspec (GESTrackElement * object,
+ GParamSpec * pspec,
+ GValue * value);
+
+GES_API void
+ges_track_element_get_child_property_valist (GESTrackElement * object,
+ const gchar * first_property_name,
+ va_list var_args);
+
+GES_API
+void ges_track_element_get_child_properties (GESTrackElement *object,
+ const gchar * first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GES_API void
+ges_track_element_set_child_property_valist (GESTrackElement * object,
+ const gchar * first_property_name,
+ va_list var_args);
+
+GES_API void
+ges_track_element_set_child_property_by_pspec (GESTrackElement * object,
+ GParamSpec * pspec,
+ GValue * value);
+
+GES_API
+void ges_track_element_set_child_properties (GESTrackElement * object,
+ const gchar * first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GES_API
+gboolean ges_track_element_set_child_property (GESTrackElement *object,
+ const gchar *property_name,
+ GValue * value);
+
+GES_API
+gboolean ges_track_element_get_child_property (GESTrackElement *object,
+ const gchar *property_name,
+ GValue * value);
+
+GES_API gboolean
+ges_track_element_edit (GESTrackElement * object,
+ GList *layers, GESEditMode mode,
+ GESEdge edge, guint64 position);
+
+GES_API gboolean
+ges_track_element_set_control_source (GESTrackElement *object,
+ GstControlSource *source,
+ const gchar *property_name,
+ const gchar *binding_type);
+
+GES_API GstControlBinding *
+ges_track_element_get_control_binding (GESTrackElement *object,
+ const gchar *property_name);
+GES_API void
+ges_track_element_add_children_props (GESTrackElement *self,
+ GstElement *element,
+ const gchar ** wanted_categories,
+ const gchar **blacklist,
+ const gchar **whitelist);
+GES_API GHashTable *
+ges_track_element_get_all_control_bindings (GESTrackElement * trackelement);
+GES_API gboolean
+ges_track_element_remove_control_binding (GESTrackElement * object,
+ const gchar * property_name);
+G_END_DECLS
+#endif /* _GES_TRACK_ELEMENT */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestrack
+ * @title: GESTrack
+ * @short_description: Composition of objects
+ *
+ * Corresponds to one output format (i.e. audio OR video).
+ *
+ * Contains the compatible TrackElement(s).
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track.h"
+#include "ges-track-element.h"
+#include "ges-meta-container.h"
+#include "ges-video-track.h"
+#include "ges-audio-track.h"
+
+#define CHECK_THREAD(track) g_assert(track->priv->valid_thread == g_thread_self())
+
+static GstStaticPadTemplate ges_track_src_pad_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+/* Structure that represents gaps and keep knowledge
+ * of the gaps filled in the track */
+typedef struct
+{
+ GstElement *nleobj;
+
+ GstClockTime start;
+ GstClockTime duration;
+ GESTrack *track;
+} Gap;
+
+struct _GESTrackPrivate
+{
+ /*< private > */
+ GESTimeline *timeline;
+ GSequence *trackelements_by_start;
+ GHashTable *trackelements_iter;
+ GList *gaps;
+ gboolean last_gap_disabled;
+
+ guint64 duration;
+
+ GstCaps *caps;
+ GstCaps *restriction_caps;
+
+ GstElement *composition; /* The composition associated with this track */
+ GstPad *srcpad; /* The source GhostPad */
+
+ gboolean updating;
+
+ gboolean mixing;
+ GstElement *mixing_operation;
+ GstElement *capsfilter;
+
+ /* Virtual method to create GstElement that fill gaps */
+ GESCreateElementForGapFunc create_element_for_gaps;
+
+ GThread *valid_thread;
+};
+
+enum
+{
+ ARG_0,
+ ARG_CAPS,
+ ARG_RESTRICTION_CAPS,
+ ARG_TYPE,
+ ARG_DURATION,
+ ARG_MIXING,
+ ARG_LAST,
+ TRACK_ELEMENT_ADDED,
+ TRACK_ELEMENT_REMOVED,
+ COMMITED,
+ LAST_SIGNAL
+};
+
+static guint ges_track_signals[LAST_SIGNAL] = { 0 };
+
+static GParamSpec *properties[ARG_LAST];
+
+G_DEFINE_TYPE_WITH_CODE (GESTrack, ges_track, GST_TYPE_BIN,
+ G_ADD_PRIVATE (GESTrack)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
+
+
+static void composition_duration_cb (GstElement * composition, GParamSpec * arg
+ G_GNUC_UNUSED, GESTrack * obj);
+
+/* Private methods/functions/callbacks */
+static void
+add_trackelement_to_list_foreach (GESTrackElement * trackelement, GList ** list)
+{
+ gst_object_ref (trackelement);
+ *list = g_list_prepend (*list, trackelement);
+}
+
+static Gap *
+gap_new (GESTrack * track, GstClockTime start, GstClockTime duration)
+{
+ GstElement *nlesrc, *elem;
+
+ Gap *new_gap;
+
+ nlesrc = gst_element_factory_make ("nlesource", NULL);
+ elem = track->priv->create_element_for_gaps (track);
+ if (G_UNLIKELY (gst_bin_add (GST_BIN (nlesrc), elem) == FALSE)) {
+ GST_WARNING_OBJECT (track, "Could not create gap filler");
+
+ if (nlesrc)
+ gst_object_unref (nlesrc);
+
+ if (elem)
+ gst_object_unref (elem);
+
+ return NULL;
+ }
+
+ if (G_UNLIKELY (ges_nle_composition_add_object (track->priv->composition,
+ nlesrc) == FALSE)) {
+ GST_WARNING_OBJECT (track, "Could not add gap to the composition");
+
+ if (nlesrc)
+ gst_object_unref (nlesrc);
+
+ if (elem)
+ gst_object_unref (elem);
+
+ return NULL;
+ }
+
+ new_gap = g_slice_new (Gap);
+ new_gap->start = start;
+ new_gap->duration = duration;
+ new_gap->track = track;
+ new_gap->nleobj = nlesrc;
+
+
+ g_object_set (nlesrc, "start", new_gap->start, "duration", new_gap->duration,
+ "priority", 1, NULL);
+
+ GST_DEBUG_OBJECT (track,
+ "Created gap with start %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (new_gap->start), GST_TIME_ARGS (new_gap->duration));
+
+
+ return new_gap;
+}
+
+static void
+free_gap (Gap * gap)
+{
+ GESTrack *track = gap->track;
+
+ GST_DEBUG_OBJECT (track, "Removed gap with start %" GST_TIME_FORMAT
+ " duration %" GST_TIME_FORMAT, GST_TIME_ARGS (gap->start),
+ GST_TIME_ARGS (gap->duration));
+ ges_nle_composition_remove_object (track->priv->composition, gap->nleobj);
+
+ g_slice_free (Gap, gap);
+}
+
+static inline void
+update_gaps (GESTrack * track)
+{
+ Gap *gap;
+ GList *gaps;
+ GSequenceIter *it;
+
+ GESTrackElement *trackelement;
+ GstClockTime start, end, duration = 0, timeline_duration = 0;
+
+ GESTrackPrivate *priv = track->priv;
+
+ if (priv->create_element_for_gaps == NULL) {
+ GST_INFO ("Not filling the gaps as no create_element_for_gaps vmethod"
+ " provided");
+ return;
+ }
+
+ gaps = priv->gaps;
+ priv->gaps = NULL;
+
+ /* 1- And recalculate gaps */
+ for (it = g_sequence_get_begin_iter (priv->trackelements_by_start);
+ g_sequence_iter_is_end (it) == FALSE; it = g_sequence_iter_next (it)) {
+ trackelement = g_sequence_get (it);
+
+ if (!ges_track_element_is_active (trackelement))
+ continue;
+
+ start = _START (trackelement);
+ end = start + _DURATION (trackelement);
+
+ if (start > duration) {
+ /* 2- Fill gap */
+ gap = gap_new (track, duration, start - duration);
+
+ if (G_LIKELY (gap != NULL))
+ priv->gaps = g_list_prepend (priv->gaps, gap);
+ }
+
+ duration = MAX (duration, end);
+ }
+
+ /* 3- Add a gap at the end of the timeline if needed */
+ if (priv->timeline) {
+ g_object_get (priv->timeline, "duration", &timeline_duration, NULL);
+
+ if (duration < timeline_duration) {
+ gap = gap_new (track, duration, timeline_duration - duration);
+
+ if (G_LIKELY (gap != NULL)) {
+ priv->gaps = g_list_prepend (priv->gaps, gap);
+ }
+
+ priv->duration = timeline_duration;
+ }
+ }
+
+ if (!track->priv->last_gap_disabled) {
+ GST_DEBUG_OBJECT (track, "Adding a one second gap at the end");
+ gap = gap_new (track, timeline_duration, 1);
+ priv->gaps = g_list_prepend (priv->gaps, gap);
+ }
+
+ /* 4- Remove old gaps */
+ g_list_free_full (gaps, (GDestroyNotify) free_gap);
+}
+
+void
+track_disable_last_gap (GESTrack * track, gboolean disabled)
+{
+ track->priv->last_gap_disabled = disabled;
+ update_gaps (track);
+}
+
+void
+track_resort_and_fill_gaps (GESTrack * track)
+{
+ g_sequence_sort (track->priv->trackelements_by_start,
+ (GCompareDataFunc) element_start_compare, NULL);
+
+ if (track->priv->updating == TRUE) {
+ update_gaps (track);
+ }
+}
+
+static gboolean
+update_field (GQuark field_id, const GValue * value, GstStructure * original)
+{
+ gst_structure_id_set_value (original, field_id, value);
+ return TRUE;
+}
+
+/* callbacks */
+static void
+_ghost_nlecomposition_srcpad (GESTrack * track)
+{
+ GstPad *capsfilter_sink;
+ GstPad *capsfilter_src;
+ GESTrackPrivate *priv = track->priv;
+ GstPad *pad = gst_element_get_static_pad (priv->composition, "src");
+
+ capsfilter_sink = gst_element_get_static_pad (priv->capsfilter, "sink");
+
+ GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
+
+ gst_pad_link (pad, capsfilter_sink);
+ gst_object_unref (capsfilter_sink);
+ gst_object_unref (pad);
+
+ capsfilter_src = gst_element_get_static_pad (priv->capsfilter, "src");
+ /* ghost the pad */
+ priv->srcpad = gst_ghost_pad_new ("src", capsfilter_src);
+ gst_object_unref (capsfilter_src);
+ gst_pad_set_active (priv->srcpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
+
+ GST_DEBUG ("done");
+}
+
+static void
+composition_duration_cb (GstElement * composition,
+ GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
+{
+ guint64 duration;
+
+ g_object_get (composition, "duration", &duration, NULL);
+
+ if (track->priv->duration != duration) {
+ GST_DEBUG_OBJECT (track,
+ "composition duration : %" GST_TIME_FORMAT " current : %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (duration),
+ GST_TIME_ARGS (track->priv->duration));
+
+ track->priv->duration = duration;
+
+ g_object_notify_by_pspec (G_OBJECT (track), properties[ARG_DURATION]);
+ }
+}
+
+static void
+composition_commited_cb (GstElement * composition, gboolean changed,
+ GESTrack * self)
+{
+ g_signal_emit (self, ges_track_signals[COMMITED], 0);
+}
+
+/* Internal */
+GstElement *
+ges_track_get_composition (GESTrack * track)
+{
+ return track->priv->composition;
+}
+
+/* FIXME: Find out how to avoid doing this "hack" using the GDestroyNotify
+ * function pointer in the trackelements_by_start GSequence
+ *
+ * Remove @object from @track, but keeps it in the sequence this is needed
+ * when finalizing as we can not change a GSequence at the same time we are
+ * accessing it
+ */
+static gboolean
+remove_object_internal (GESTrack * track, GESTrackElement * object)
+{
+ GESTrackPrivate *priv;
+ GstElement *nleobject;
+
+ GST_DEBUG_OBJECT (track, "object:%p", object);
+
+ priv = track->priv;
+
+ if (G_UNLIKELY (ges_track_element_get_track (object) != track)) {
+ GST_WARNING ("Object belongs to another track");
+ return FALSE;
+ }
+
+ if ((nleobject = ges_track_element_get_nleobject (object))) {
+ GST_DEBUG ("Removing NleObject '%s' from composition '%s'",
+ GST_ELEMENT_NAME (nleobject), GST_ELEMENT_NAME (priv->composition));
+
+ if (!ges_nle_composition_remove_object (priv->composition, nleobject)) {
+ GST_WARNING ("Failed to remove nleobject from composition");
+ return FALSE;
+ }
+ }
+
+ ges_track_element_set_track (object, NULL);
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
+
+ g_signal_emit (track, ges_track_signals[TRACK_ELEMENT_REMOVED], 0,
+ GES_TRACK_ELEMENT (object));
+
+ gst_object_unref (object);
+
+ return TRUE;
+}
+
+static void
+dispose_trackelements_foreach (GESTrackElement * trackelement, GESTrack * track)
+{
+ remove_object_internal (track, trackelement);
+}
+
+/* GstElement virtual methods */
+
+static GstStateChangeReturn
+ges_track_change_state (GstElement * element, GstStateChange transition)
+{
+ if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
+ track_resort_and_fill_gaps (GES_TRACK (element));
+
+ return GST_ELEMENT_CLASS (ges_track_parent_class)->change_state (element,
+ transition);
+}
+
+/* GObject virtual methods */
+static void
+ges_track_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESTrack *track = GES_TRACK (object);
+
+ switch (property_id) {
+ case ARG_CAPS:
+ gst_value_set_caps (value, track->priv->caps);
+ break;
+ case ARG_TYPE:
+ g_value_set_flags (value, track->type);
+ break;
+ case ARG_DURATION:
+ g_value_set_uint64 (value, track->priv->duration);
+ break;
+ case ARG_RESTRICTION_CAPS:
+ gst_value_set_caps (value, track->priv->restriction_caps);
+ break;
+ case ARG_MIXING:
+ g_value_set_boolean (value, track->priv->mixing);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_track_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESTrack *track = GES_TRACK (object);
+
+ switch (property_id) {
+ case ARG_CAPS:
+ ges_track_set_caps (track, gst_value_get_caps (value));
+ break;
+ case ARG_TYPE:
+ track->type = g_value_get_flags (value);
+ break;
+ case ARG_RESTRICTION_CAPS:
+ ges_track_set_restriction_caps (track, gst_value_get_caps (value));
+ break;
+ case ARG_MIXING:
+ ges_track_set_mixing (track, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_track_dispose (GObject * object)
+{
+ GESTrack *track = (GESTrack *) object;
+ GESTrackPrivate *priv = track->priv;
+
+ /* Remove all TrackElements and drop our reference */
+ g_hash_table_unref (priv->trackelements_iter);
+ g_sequence_foreach (track->priv->trackelements_by_start,
+ (GFunc) dispose_trackelements_foreach, track);
+ g_sequence_free (priv->trackelements_by_start);
+ g_list_free_full (priv->gaps, (GDestroyNotify) free_gap);
+ ges_nle_object_commit (track->priv->composition, TRUE);
+
+ if (priv->composition) {
+ gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
+ gst_bin_remove (GST_BIN (object), priv->composition);
+ priv->composition = NULL;
+ }
+
+ if (priv->caps) {
+ gst_caps_unref (priv->caps);
+ priv->caps = NULL;
+ }
+
+ if (priv->restriction_caps) {
+ gst_caps_unref (priv->restriction_caps);
+ priv->restriction_caps = NULL;
+ }
+
+ G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
+}
+
+static void
+ges_track_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
+}
+
+static void
+ges_track_constructed (GObject * object)
+{
+ GESTrack *self = GES_TRACK (object);
+ gchar *componame = NULL;
+
+ if (self->type == GES_TRACK_TYPE_VIDEO) {
+ componame =
+ g_strdup_printf ("video_%s", GST_OBJECT_NAME (self->priv->composition));
+ } else if (self->type == GES_TRACK_TYPE_AUDIO) {
+ componame =
+ g_strdup_printf ("audio_%s", GST_OBJECT_NAME (self->priv->composition));
+ }
+
+ if (componame) {
+ gst_object_set_name (GST_OBJECT (self->priv->composition), componame);
+
+ g_free (componame);
+ }
+
+ if (!gst_bin_add (GST_BIN (self), self->priv->composition))
+ GST_ERROR ("Couldn't add composition to bin !");
+
+ if (!gst_bin_add (GST_BIN (self), self->priv->capsfilter))
+ GST_ERROR ("Couldn't add capsfilter to bin !");
+
+ _ghost_nlecomposition_srcpad (self);
+ if (GES_TRACK_GET_CLASS (self)->get_mixing_element) {
+ GstElement *nleobject;
+ GstElement *mixer = GES_TRACK_GET_CLASS (self)->get_mixing_element (self);
+
+ if (mixer == NULL) {
+ GST_WARNING_OBJECT (self, "Got no element fron get_mixing_element");
+
+ return;
+ }
+
+ nleobject = gst_element_factory_make ("nleoperation", "mixing-operation");
+ if (!gst_bin_add (GST_BIN (nleobject), mixer)) {
+ GST_WARNING_OBJECT (self, "Could not add the mixer to our composition");
+ gst_object_unref (mixer);
+ gst_object_unref (nleobject);
+
+ return;
+ }
+ g_object_set (nleobject, "expandable", TRUE, NULL);
+
+ if (self->priv->mixing) {
+ if (!ges_nle_composition_add_object (self->priv->composition, nleobject)) {
+ GST_WARNING_OBJECT (self, "Could not add the mixer to our composition");
+ gst_object_unref (nleobject);
+
+ return;
+ }
+ }
+
+ self->priv->mixing_operation = nleobject;
+
+ } else {
+ GST_INFO_OBJECT (self, "No way to create a main mixer");
+ }
+}
+
+static void
+ges_track_class_init (GESTrackClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (ges_track_change_state);
+
+ object_class->get_property = ges_track_get_property;
+ object_class->set_property = ges_track_set_property;
+ object_class->dispose = ges_track_dispose;
+ object_class->finalize = ges_track_finalize;
+ object_class->constructed = ges_track_constructed;
+
+ /**
+ * GESTrack:caps:
+ *
+ * Caps used to filter/choose the output stream. This is generally set to
+ * a generic set of caps like 'video/x-raw' for raw video.
+ *
+ * Default value: #GST_CAPS_ANY.
+ */
+ properties[ARG_CAPS] = g_param_spec_boxed ("caps", "Caps",
+ "Caps used to filter/choose the output stream",
+ GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, ARG_CAPS,
+ properties[ARG_CAPS]);
+
+ /**
+ * GESTrack:restriction-caps:
+ *
+ * Caps used to filter/choose the output stream.
+ *
+ * Default value: #GST_CAPS_ANY.
+ */
+ properties[ARG_RESTRICTION_CAPS] =
+ g_param_spec_boxed ("restriction-caps", "Restriction caps",
+ "Caps used to filter/choose the output stream", GST_TYPE_CAPS,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, ARG_RESTRICTION_CAPS,
+ properties[ARG_RESTRICTION_CAPS]);
+
+ /**
+ * GESTrack:duration:
+ *
+ * Current duration of the track
+ *
+ * Default value: O
+ */
+ properties[ARG_DURATION] = g_param_spec_uint64 ("duration", "Duration",
+ "The current duration of the track", 0, G_MAXUINT64, GST_SECOND,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, ARG_DURATION,
+ properties[ARG_DURATION]);
+
+ /**
+ * GESTrack:track-type:
+ *
+ * Type of stream the track outputs. This is used when creating the #GESTrack
+ * to specify in generic terms what type of content will be outputted.
+ *
+ * It also serves as a 'fast' way to check what type of data will be outputted
+ * from the #GESTrack without having to actually check the #GESTrack's caps
+ * property.
+ */
+ properties[ARG_TYPE] = g_param_spec_flags ("track-type", "TrackType",
+ "Type of stream the track outputs",
+ GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, ARG_TYPE,
+ properties[ARG_TYPE]);
+
+ /**
+ * GESTrack:mixing:
+ *
+ * Whether layer mixing is activated or not on the track.
+ */
+ properties[ARG_MIXING] = g_param_spec_boolean ("mixing", "Mixing",
+ "Whether layer mixing is activated on the track or not",
+ TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, ARG_MIXING,
+ properties[ARG_MIXING]);
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &ges_track_src_pad_template);
+
+ /**
+ * GESTrack::track-element-added:
+ * @object: the #GESTrack
+ * @effect: the #GESTrackElement that was added.
+ *
+ * Will be emitted after a track element was added to the track.
+ */
+ ges_track_signals[TRACK_ELEMENT_ADDED] =
+ g_signal_new ("track-element-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GES_TYPE_TRACK_ELEMENT);
+
+ /**
+ * GESTrack::track-element-removed:
+ * @object: the #GESTrack
+ * @effect: the #GESTrackElement that was removed.
+ *
+ * Will be emitted after a track element was removed from the track.
+ */
+ ges_track_signals[TRACK_ELEMENT_REMOVED] =
+ g_signal_new ("track-element-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GES_TYPE_TRACK_ELEMENT);
+
+ /**
+ * GESTrack::commited:
+ * @track: the #GESTrack
+ */
+ ges_track_signals[COMMITED] =
+ g_signal_new ("commited", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ klass->get_mixing_element = NULL;
+}
+
+static void
+ges_track_init (GESTrack * self)
+{
+ self->priv = ges_track_get_instance_private (self);
+ self->priv->valid_thread = g_thread_self ();
+
+ self->priv->composition = gst_element_factory_make ("nlecomposition", NULL);
+ self->priv->capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ self->priv->updating = TRUE;
+ self->priv->trackelements_by_start = g_sequence_new (NULL);
+ self->priv->trackelements_iter =
+ g_hash_table_new (g_direct_hash, g_direct_equal);
+ self->priv->create_element_for_gaps = NULL;
+ self->priv->gaps = NULL;
+ self->priv->mixing = TRUE;
+ self->priv->restriction_caps = NULL;
+
+ g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
+ G_CALLBACK (composition_duration_cb), self);
+ g_signal_connect (G_OBJECT (self->priv->composition), "commited",
+ G_CALLBACK (composition_commited_cb), self);
+}
+
+/**
+ * ges_track_new:
+ * @type: The type of track
+ * @caps: (transfer full): The caps to restrict the output of the track to.
+ *
+ * Creates a new #GESTrack with the given @type and @caps.
+ *
+ * The newly created track will steal a reference to the caps. If you wish to
+ * use those caps elsewhere, you will have to take an extra reference.
+ *
+ * Returns: (transfer floating): A new #GESTrack.
+ */
+GESTrack *
+ges_track_new (GESTrackType type, GstCaps * caps)
+{
+ GESTrack *track;
+ GstCaps *tmpcaps;
+
+ /* TODO Be smarter with well known track types */
+ if (type == GES_TRACK_TYPE_VIDEO) {
+ tmpcaps = gst_caps_new_empty_simple ("video/x-raw");
+ gst_caps_set_features (tmpcaps, 0, gst_caps_features_new_any ());
+
+ if (gst_caps_is_subset (caps, tmpcaps)) {
+ track = GES_TRACK (ges_video_track_new ());
+ ges_track_set_caps (track, caps);
+
+ gst_caps_unref (tmpcaps);
+ return track;
+ }
+ gst_caps_unref (tmpcaps);
+ } else if (type == GES_TRACK_TYPE_AUDIO) {
+ tmpcaps = gst_caps_new_empty_simple ("audio/x-raw");
+ gst_caps_set_features (tmpcaps, 0, gst_caps_features_new_any ());
+
+ if (gst_caps_is_subset (caps, tmpcaps)) {
+ track = GES_TRACK (ges_audio_track_new ());
+ ges_track_set_caps (track, caps);
+
+ gst_caps_unref (tmpcaps);
+ return track;
+ }
+
+ gst_caps_unref (tmpcaps);
+ }
+
+ track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
+ gst_caps_unref (caps);
+
+ return track;
+}
+
+
+/**
+ * ges_track_set_timeline:
+ * @track: a #GESTrack
+ * @timeline: a #GESTimeline
+ *
+ * Sets @timeline as the timeline controlling @track.
+ */
+void
+ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
+{
+ GST_DEBUG ("track:%p, timeline:%p", track, timeline);
+
+ track->priv->timeline = timeline;
+ track_resort_and_fill_gaps (track);
+}
+
+/**
+ * ges_track_set_caps:
+ * @track: a #GESTrack
+ * @caps: the #GstCaps to set
+ *
+ * Sets the given @caps on the track.
+ * Note that the capsfeatures of @caps will always be set
+ * to ANY. If you want to restrict them, you should
+ * do it in #ges_track_set_restriction_caps.
+ */
+void
+ges_track_set_caps (GESTrack * track, const GstCaps * caps)
+{
+ GESTrackPrivate *priv;
+ gint i;
+
+ g_return_if_fail (GES_IS_TRACK (track));
+ CHECK_THREAD (track);
+
+ GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
+ g_return_if_fail (GST_IS_CAPS (caps));
+
+ priv = track->priv;
+
+ if (priv->caps)
+ gst_caps_unref (priv->caps);
+ priv->caps = gst_caps_copy (caps);
+
+ for (i = 0; i < (int) gst_caps_get_size (priv->caps); i++)
+ gst_caps_set_features (priv->caps, i, gst_caps_features_new_any ());
+
+ g_object_set (priv->composition, "caps", caps, NULL);
+ /* FIXME : update all trackelements ? */
+}
+
+/**
+ * ges_track_set_restriction_caps:
+ * @track: a #GESTrack
+ * @caps: the #GstCaps to set
+ *
+ * Sets the given @caps as the caps the track has to output.
+ */
+void
+ges_track_set_restriction_caps (GESTrack * track, const GstCaps * caps)
+{
+ GESTrackPrivate *priv;
+
+ g_return_if_fail (GES_IS_TRACK (track));
+ CHECK_THREAD (track);
+
+ GST_DEBUG ("track:%p, restriction caps:%" GST_PTR_FORMAT, track, caps);
+ g_return_if_fail (GST_IS_CAPS (caps));
+
+ priv = track->priv;
+
+ if (priv->restriction_caps)
+ gst_caps_unref (priv->restriction_caps);
+ priv->restriction_caps = gst_caps_copy (caps);
+
+ g_object_set (priv->capsfilter, "caps", caps, NULL);
+
+ g_object_notify (G_OBJECT (track), "restriction-caps");
+}
+
+/**
+ * ges_track_update_restriction_caps:
+ * @track: a #GESTrack
+ * @caps: the #GstCaps to update with
+ *
+ * Updates the restriction caps by modifying all the fields present in @caps
+ * in the original restriction caps. If for example the current restriction caps
+ * are video/x-raw, format=I420, width=360 and @caps is video/x-raw, format=RGB,
+ * the restriction caps will be updated to video/x-raw, format=RGB, width=360.
+ *
+ * Modification happens for each structure in the new caps, and
+ * one can add new fields or structures through that function.
+ */
+void
+ges_track_update_restriction_caps (GESTrack * self, const GstCaps * caps)
+{
+ guint i;
+ GstCaps *new_restriction_caps;
+
+ g_return_if_fail (GES_IS_TRACK (self));
+ CHECK_THREAD (self);
+
+ if (!self->priv->restriction_caps) {
+ ges_track_set_restriction_caps (self, caps);
+ return;
+ }
+
+ new_restriction_caps = gst_caps_copy (self->priv->restriction_caps);
+ for (i = 0; i < gst_caps_get_size (caps); i++) {
+ GstStructure *new = gst_caps_get_structure (caps, i);
+
+ if (gst_caps_get_size (new_restriction_caps) > i) {
+ GstStructure *original = gst_caps_get_structure (new_restriction_caps, i);
+ gst_structure_foreach (new, (GstStructureForeachFunc) update_field,
+ original);
+ } else
+ gst_caps_append_structure (new_restriction_caps,
+ gst_structure_copy (new));
+ }
+
+ ges_track_set_restriction_caps (self, new_restriction_caps);
+ gst_caps_unref (new_restriction_caps);
+}
+
+/**
+ * ges_track_set_mixing:
+ * @track: a #GESTrack
+ * @mixing: TRUE if the track should be mixing, FALSE otherwise.
+ *
+ * Sets if the #GESTrack should be mixing.
+ */
+void
+ges_track_set_mixing (GESTrack * track, gboolean mixing)
+{
+ g_return_if_fail (GES_IS_TRACK (track));
+ CHECK_THREAD (track);
+
+ if (!track->priv->mixing_operation) {
+ GST_DEBUG_OBJECT (track, "Track will be set to mixing = %d", mixing);
+ track->priv->mixing = mixing;
+ return;
+ }
+
+ if (mixing == track->priv->mixing) {
+ GST_DEBUG_OBJECT (track, "Mixing is already set to the same value");
+ }
+
+ if (mixing) {
+ if (!ges_nle_composition_add_object (track->priv->composition,
+ track->priv->mixing_operation)) {
+ GST_WARNING_OBJECT (track, "Could not add the mixer to our composition");
+ return;
+ }
+ } else {
+ if (!ges_nle_composition_remove_object (track->priv->composition,
+ track->priv->mixing_operation)) {
+ GST_WARNING_OBJECT (track,
+ "Could not remove the mixer from our composition");
+ return;
+ }
+ }
+
+ track->priv->mixing = mixing;
+
+ GST_DEBUG_OBJECT (track, "The track has been set to mixing = %d", mixing);
+}
+
+/**
+ * ges_track_add_element:
+ * @track: a #GESTrack
+ * @object: (transfer floating): the #GESTrackElement to add
+ *
+ * Adds the given object to the track. Sets the object's controlling track,
+ * and thus takes ownership of the @object.
+ *
+ * An object can only be added to one track.
+ *
+ * Returns: #TRUE if the object was properly added. #FALSE if the track does not
+ * want to accept the object.
+ */
+gboolean
+ges_track_add_element (GESTrack * track, GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+ CHECK_THREAD (track);
+
+ GST_DEBUG ("track:%p, object:%p", track, object);
+
+ if (G_UNLIKELY (ges_track_element_get_track (object) != NULL)) {
+ GST_WARNING ("Object already belongs to another track");
+ gst_object_ref_sink (object);
+ gst_object_unref (object);
+ return FALSE;
+ }
+
+ if (G_UNLIKELY (!ges_track_element_set_track (object, track))) {
+ GST_ERROR ("Couldn't properly add the object to the Track");
+ gst_object_ref_sink (object);
+ gst_object_unref (object);
+ return FALSE;
+ }
+
+ GST_DEBUG ("Adding object %s to ourself %s",
+ GST_OBJECT_NAME (ges_track_element_get_nleobject (object)),
+ GST_OBJECT_NAME (track->priv->composition));
+
+ if (G_UNLIKELY (!ges_nle_composition_add_object (track->priv->composition,
+ ges_track_element_get_nleobject (object)))) {
+ GST_WARNING ("Couldn't add object to the NleComposition");
+ gst_object_ref_sink (object);
+ gst_object_unref (object);
+ return FALSE;
+ }
+
+ gst_object_ref_sink (object);
+ g_hash_table_insert (track->priv->trackelements_iter, object,
+ g_sequence_insert_sorted (track->priv->trackelements_by_start, object,
+ (GCompareDataFunc) element_start_compare, NULL));
+
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object),
+ track->priv->timeline);
+ g_signal_emit (track, ges_track_signals[TRACK_ELEMENT_ADDED], 0,
+ GES_TRACK_ELEMENT (object));
+
+ return TRUE;
+}
+
+/**
+ * ges_track_get_elements:
+ * @track: a #GESTrack
+ *
+ * Gets the #GESTrackElement contained in @track
+ *
+ * Returns: (transfer full) (element-type GESTrackElement): the list of
+ * #GESTrackElement present in the Track sorted by priority and start.
+ */
+GList *
+ges_track_get_elements (GESTrack * track)
+{
+ GList *ret = NULL;
+
+ g_return_val_if_fail (GES_IS_TRACK (track), NULL);
+ CHECK_THREAD (track);
+
+ g_sequence_foreach (track->priv->trackelements_by_start,
+ (GFunc) add_trackelement_to_list_foreach, &ret);
+
+ ret = g_list_reverse (ret);
+ return ret;
+}
+
+/**
+ * ges_track_remove_element:
+ * @track: a #GESTrack
+ * @object: the #GESTrackElement to remove
+ *
+ * Removes the object from the track and unparents it.
+ * Unparenting it means the reference owned by @track on the @object will be
+ * removed. If you wish to use the @object after this function, make sure you
+ * call gst_object_ref() before removing it from the @track.
+ *
+ * Returns: #TRUE if the object was removed, else #FALSE if the track
+ * could not remove the object (like if it didn't belong to the track).
+ */
+gboolean
+ges_track_remove_element (GESTrack * track, GESTrackElement * object)
+{
+ GSequenceIter *it;
+ GESTrackPrivate *priv;
+
+ g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+ CHECK_THREAD (track);
+
+ priv = track->priv;
+
+ GST_DEBUG_OBJECT (track, "Removing %" GST_PTR_FORMAT, object);
+
+ it = g_hash_table_lookup (priv->trackelements_iter, object);
+ g_sequence_remove (it);
+
+ if (remove_object_internal (track, object) == TRUE) {
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
+
+ return TRUE;
+ }
+
+ g_hash_table_insert (track->priv->trackelements_iter, object,
+ g_sequence_insert_sorted (track->priv->trackelements_by_start, object,
+ (GCompareDataFunc) element_start_compare, NULL));
+
+ return FALSE;
+}
+
+/**
+ * ges_track_get_caps:
+ * @track: a #GESTrack
+ *
+ * Get the #GstCaps this track is configured to output.
+ *
+ * Returns: The #GstCaps this track is configured to output.
+ */
+const GstCaps *
+ges_track_get_caps (GESTrack * track)
+{
+ g_return_val_if_fail (GES_IS_TRACK (track), NULL);
+ CHECK_THREAD (track);
+
+ return track->priv->caps;
+}
+
+/**
+ * ges_track_get_timeline:
+ * @track: a #GESTrack
+ *
+ * Get the #GESTimeline this track belongs to. Can be %NULL.
+ *
+ * Returns: (nullable): The #GESTimeline this track belongs to. Can be %NULL.
+ */
+const GESTimeline *
+ges_track_get_timeline (GESTrack * track)
+{
+ g_return_val_if_fail (GES_IS_TRACK (track), NULL);
+ CHECK_THREAD (track);
+
+ return track->priv->timeline;
+}
+
+/**
+ * ges_track_get_mixing:
+ * @track: a #GESTrack
+ *
+ * Gets if the underlying #NleComposition contains an expandable mixer.
+ *
+ * Returns: #True if there is a mixer, #False otherwise.
+ */
+gboolean
+ges_track_get_mixing (GESTrack * track)
+{
+ g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
+
+ return track->priv->mixing;
+}
+
+/**
+ * ges_track_commit:
+ * @track: a #GESTrack
+ *
+ * Commits all the pending changes of the TrackElement contained in the
+ * track.
+ *
+ * When timing changes happen in a timeline, the changes are not
+ * directly done inside NLE. This method needs to be called so any changes
+ * on a clip contained in the timeline actually happen at the media
+ * processing level.
+ *
+ * Returns: %TRUE if something as been commited %FALSE if nothing needed
+ * to be commited
+ */
+gboolean
+ges_track_commit (GESTrack * track)
+{
+ g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
+ CHECK_THREAD (track);
+
+ track_resort_and_fill_gaps (track);
+
+ return ges_nle_object_commit (track->priv->composition, TRUE);
+}
+
+
+/**
+ * ges_track_set_create_element_for_gap_func:
+ * @track: a #GESTrack
+ * @func: (scope notified): The #GESCreateElementForGapFunc that will be used
+ * to create #GstElement to fill gaps
+ *
+ * Sets the function that should be used to create the GstElement used to fill gaps.
+ * To avoid to provide such a function we advice you to use the
+ * #ges_audio_track_new and #ges_video_track_new constructor when possible.
+ */
+void
+ges_track_set_create_element_for_gap_func (GESTrack * track,
+ GESCreateElementForGapFunc func)
+{
+ g_return_if_fail (GES_IS_TRACK (track));
+ CHECK_THREAD (track);
+
+ track->priv->create_element_for_gaps = func;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_TRACK
+#define _GES_TRACK
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-enums.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TRACK ges_track_get_type()
+#define GES_TRACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TRACK, GESTrack))
+#define GES_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TRACK, GESTrackClass))
+#define GES_IS_TRACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TRACK))
+#define GES_IS_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TRACK))
+#define GES_TRACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TRACK, GESTrackClass))
+
+typedef struct _GESTrackPrivate GESTrackPrivate;
+
+/**
+ * GESCreateElementForGapFunc:
+ * @track: the #GESTrack
+ *
+ * A function that will be called to create the #GstElement that will be used
+ * as a source to fill the gaps in @track.
+ *
+ * Returns: A #GstElement (must be a source) that will be used to
+ * fill the gaps (periods of time in @track that containes no source).
+ */
+typedef GstElement* (*GESCreateElementForGapFunc) (GESTrack *track);
+
+/**
+ * GESTrack:
+ * @type: a #GESTrackType indicting the basic type of the track.
+ */
+struct _GESTrack
+{
+ GstBin parent;
+
+ /*< public >*/
+ /* READ-ONLY */
+ GESTrackType type;
+
+ /*< private >*/
+ GESTrackPrivate* priv;
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTrackClass:
+ */
+struct _GESTrackClass
+{
+ /*< private >*/
+ GstBinClass parent_class;
+
+ GstElement * (*get_mixing_element) (GESTrack *track);
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+const GstCaps* ges_track_get_caps (GESTrack *track);
+GES_API
+GList* ges_track_get_elements (GESTrack *track);
+GES_API
+const GESTimeline* ges_track_get_timeline (GESTrack *track);
+GES_API
+gboolean ges_track_commit (GESTrack *track);
+GES_API
+void ges_track_set_timeline (GESTrack *track, GESTimeline *timeline);
+GES_API
+gboolean ges_track_add_element (GESTrack *track, GESTrackElement *object);
+GES_API
+gboolean ges_track_remove_element (GESTrack *track, GESTrackElement *object);
+GES_API
+void ges_track_set_create_element_for_gap_func (GESTrack *track, GESCreateElementForGapFunc func);
+GES_API
+void ges_track_set_mixing (GESTrack *track, gboolean mixing);
+GES_API
+gboolean ges_track_get_mixing (GESTrack *track);
+GES_API
+void ges_track_set_restriction_caps (GESTrack *track, const GstCaps *caps);
+GES_API
+void ges_track_update_restriction_caps (GESTrack *track, const GstCaps *caps);
+
+/* standard methods */
+GES_API
+GType ges_track_get_type (void);
+GES_API
+GESTrack* ges_track_new (GESTrackType type, GstCaps * caps);
+
+G_END_DECLS
+
+#endif /* _GES_TRACK */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gestransitionclip
+ * @title: GESTransitionClip
+ * @short_description: Transition from one clip to another in a GESLayer
+ *
+ * Creates an object that mixes together the two underlying objects, A and B.
+ * The A object is assumed to have a higher prioirity (lower number) than the
+ * B object. At the transition in point, only A will be visible, and by the
+ * end only B will be visible.
+ *
+ * The shape of the video transition depends on the value of the "vtype"
+ * property. The default value is "crossfade". For audio, only "crossfade" is
+ * supported.
+ *
+ * The ID of the ExtractableType is the nickname of the vtype property value. Note
+ * that this value can be changed after creation and the GESExtractable.asset value
+ * will be updated when needed.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+#include "ges-internal.h"
+
+struct _GESTransitionClipPrivate
+{
+ GSList *video_transitions;
+
+ const gchar *vtype_name;
+};
+
+enum
+{
+ PROP_VTYPE = 5,
+};
+
+static GESTrackElement *_create_track_element (GESClip
+ * self, GESTrackType type);
+static void _child_added (GESContainer * container,
+ GESTimelineElement * element);
+static void _child_removed (GESContainer * container,
+ GESTimelineElement * element);
+
+/* Internal methods */
+static void
+ges_transition_clip_update_vtype_internal (GESClip *
+ self, GESVideoStandardTransitionType value, gboolean set_asset)
+{
+ GSList *tmp;
+ guint index;
+ GEnumClass *enum_class;
+ const gchar *asset_id = NULL;
+ GESTransitionClip *trself = GES_TRANSITION_CLIP (self);
+
+ enum_class = g_type_class_peek (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+ for (index = 0; index < enum_class->n_values; index++) {
+ if (enum_class->values[index].value == value) {
+ asset_id = enum_class->values[index].value_nick;
+ break;
+ }
+ }
+
+ if (asset_id == NULL) {
+ GST_WARNING_OBJECT (self, "Wrong transition type value: %i can not set it",
+ value);
+
+ return;
+ }
+
+ for (tmp = trself->priv->video_transitions; tmp; tmp = tmp->next) {
+ if (!ges_video_transition_set_transition_type
+ (GES_VIDEO_TRANSITION (tmp->data), value))
+ return;
+ }
+
+ trself->vtype = value;
+ trself->priv->vtype_name = asset_id;
+
+ if (set_asset) {
+ GESAsset *asset =
+ ges_asset_request (GES_TYPE_TRANSITION_CLIP, asset_id, NULL);
+
+ /* We already checked the value, so we can be sure no error occurred */
+ ges_extractable_set_asset (GES_EXTRACTABLE (self), asset);
+ gst_object_unref (asset);
+ }
+}
+
+/* GESExtractable interface overrides */
+static GParameter *
+extractable_get_parameters_from_id (const gchar * id, guint * n_params)
+{
+ GEnumClass *enum_class =
+ g_type_class_peek (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+ GParameter *params = g_new0 (GParameter, 1);
+ GEnumValue *value = g_enum_get_value_by_nick (enum_class, id);
+
+ params[0].name = "vtype";
+ g_value_init (¶ms[0].value, GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+ g_value_set_enum (¶ms[0].value, value->value);
+ *n_params = 1;
+
+ return params;
+}
+
+static gchar *
+extractable_check_id (GType type, const gchar * id)
+{
+ guint index;
+ GEnumClass *enum_class;
+ enum_class = g_type_class_peek (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+
+ for (index = 0; index < enum_class->n_values; index++) {
+ if (g_strcmp0 (enum_class->values[index].value_nick, id) == 0)
+ return g_strdup (id);
+ }
+
+ return NULL;
+}
+
+static gchar *
+extractable_get_id (GESExtractable * self)
+{
+ guint index;
+ GEnumClass *enum_class;
+ guint value = GES_TRANSITION_CLIP (self)->vtype;
+
+ enum_class = g_type_class_peek (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+ for (index = 0; index < enum_class->n_values; index++) {
+ if (enum_class->values[index].value == value)
+ return g_strdup (enum_class->values[index].value_nick);
+ }
+
+ return NULL;
+}
+
+static gboolean
+extractable_set_asset (GESExtractable * self, GESAsset * asset)
+{
+ GEnumClass *enum_class;
+ GESVideoStandardTransitionType value;
+ GESTransitionClip *trans = GES_TRANSITION_CLIP (self);
+ const gchar *vtype = ges_asset_get_id (asset);
+
+ if (!(ges_clip_get_supported_formats (GES_CLIP (self)) &
+ GES_TRACK_TYPE_VIDEO)) {
+ return FALSE;
+ }
+
+ /* Update the transition type if we actually changed it */
+ if (g_strcmp0 (vtype, trans->priv->vtype_name)) {
+ guint index;
+
+ value = GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE;
+ enum_class = g_type_class_peek (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+
+ /* Find the in value in use */
+ for (index = 0; index < enum_class->n_values; index++) {
+ if (g_strcmp0 (enum_class->values[index].value_nick, vtype) == 0) {
+ value = enum_class->values[index].value;
+ break;
+ }
+ }
+ ges_transition_clip_update_vtype_internal (GES_CLIP (self), value, FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->check_id = (GESExtractableCheckId) extractable_check_id;
+ iface->get_id = extractable_get_id;
+ iface->get_parameters_from_id = extractable_get_parameters_from_id;
+ iface->can_update_asset = TRUE;
+ iface->set_asset_full = extractable_set_asset;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESTransitionClip,
+ ges_transition_clip, GES_TYPE_BASE_TRANSITION_CLIP,
+ G_ADD_PRIVATE (GESTransitionClip)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+static void
+ges_transition_clip_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ GESTransitionClip *self = GES_TRANSITION_CLIP (object);
+ switch (property_id) {
+ case PROP_VTYPE:
+ g_value_set_enum (value, self->vtype);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_transition_clip_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ GESClip *self = GES_CLIP (object);
+
+ switch (property_id) {
+ case PROP_VTYPE:
+ ges_transition_clip_update_vtype_internal (self,
+ g_value_get_enum (value), TRUE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static gboolean
+_lookup_child (GESTimelineElement * self, const gchar * prop_name,
+ GObject ** child, GParamSpec ** pspec)
+{
+ GESTimelineElementClass *element_klass =
+ g_type_class_peek (GES_TYPE_TIMELINE_ELEMENT);
+
+ /* Bypass the container implementation as we handle children properties directly */
+ /* FIXME Implement a syntax to precisely get properties by path */
+ if (element_klass->lookup_child (self, prop_name, child, pspec))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static void
+ges_transition_clip_class_init (GESTransitionClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESClipClass *timobj_class = GES_CLIP_CLASS (klass);
+ GESContainerClass *container_class = GES_CONTAINER_CLASS (klass);
+
+ object_class->get_property = ges_transition_clip_get_property;
+ object_class->set_property = ges_transition_clip_set_property;
+
+ /**
+ * GESTransitionClip:vtype:
+ *
+ * a #GESVideoStandardTransitionType representing the wipe to use
+ */
+ g_object_class_install_property (object_class, PROP_VTYPE,
+ g_param_spec_enum ("vtype", "VType",
+ "The SMPTE video wipe to use, or 0 for crossfade",
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ GES_TIMELINE_ELEMENT_CLASS (klass)->lookup_child = _lookup_child;
+ container_class->child_added = _child_added;
+ container_class->child_removed = _child_removed;
+
+ timobj_class->create_track_element = _create_track_element;
+}
+
+static void
+ges_transition_clip_init (GESTransitionClip * self)
+{
+
+ self->priv = ges_transition_clip_get_instance_private (self);
+
+ self->vtype = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;
+ self->priv->vtype_name = NULL;
+}
+
+static void
+_child_removed (GESContainer * container, GESTimelineElement * element)
+{
+ GESTransitionClipPrivate *priv = GES_TRANSITION_CLIP (container)->priv;
+
+ /* If this is called, we should be sure the trackelement exists */
+ if (GES_IS_VIDEO_TRANSITION (element)) {
+ GST_DEBUG_OBJECT (container, "%" GST_PTR_FORMAT " removed", element);
+ priv->video_transitions = g_slist_remove (priv->video_transitions, element);
+ gst_object_unref (element);
+ }
+}
+
+static void
+_child_added (GESContainer * container, GESTimelineElement * element)
+{
+ GESTransitionClipPrivate *priv = GES_TRANSITION_CLIP (container)->priv;
+
+ if (GES_IS_VIDEO_TRANSITION (element)) {
+ GObjectClass *eklass = G_OBJECT_GET_CLASS (element);
+
+ GST_DEBUG_OBJECT (container, "%" GST_PTR_FORMAT " added", element);
+ priv->video_transitions =
+ g_slist_prepend (priv->video_transitions, gst_object_ref (element));
+
+ ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (container),
+ g_object_class_find_property (eklass, "invert"), G_OBJECT (element));
+ ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (container),
+ g_object_class_find_property (eklass, "border"), G_OBJECT (element));
+ }
+}
+
+static GESTrackElement *
+_create_track_element (GESClip * clip, GESTrackType type)
+{
+ GESTransitionClip *transition = (GESTransitionClip *) clip;
+ GESTrackElement *res = NULL;
+ GESTrackType supportedformats;
+
+ GST_DEBUG ("Creating a GESTransition");
+
+ supportedformats = ges_clip_get_supported_formats (clip);
+ if (type == GES_TRACK_TYPE_VIDEO) {
+ if (supportedformats == GES_TRACK_TYPE_UNKNOWN ||
+ supportedformats & GES_TRACK_TYPE_VIDEO) {
+ GESVideoTransition *trans;
+
+ trans = ges_video_transition_new ();
+ ges_video_transition_set_transition_type (trans, transition->vtype);
+
+ res = GES_TRACK_ELEMENT (trans);
+ } else {
+ GST_DEBUG ("Not creating transition as video track not on"
+ " supportedformats");
+ }
+
+ } else if (type == GES_TRACK_TYPE_AUDIO) {
+
+ if (supportedformats == GES_TRACK_TYPE_UNKNOWN ||
+ supportedformats & GES_TRACK_TYPE_AUDIO)
+ res = GES_TRACK_ELEMENT (ges_audio_transition_new ());
+ else
+ GST_DEBUG ("Not creating transition as audio track"
+ " not on supportedformats");
+
+ } else
+ GST_WARNING ("Transitions don't handle this track type");
+
+ return res;
+}
+
+/**
+ * ges_transition_clip_new:
+ * @vtype: the type of transition to create
+ *
+ * Creates a new #GESTransitionClip.
+ *
+ * Returns: (transfer floating) (nullable): a newly created #GESTransitionClip,
+ * or %NULL if something went wrong.
+ */
+GESTransitionClip *
+ges_transition_clip_new (GESVideoStandardTransitionType vtype)
+{
+ GEnumValue *value;
+ GEnumClass *klass;
+ GESTransitionClip *ret = NULL;
+
+ klass =
+ G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE));
+ if (!klass) {
+ GST_ERROR ("Could not find the StandarTransitionType enum class");
+ return NULL;
+ }
+
+ value = g_enum_get_value (klass, vtype);
+ if (!value) {
+ GST_ERROR ("Could not find enum value for %i", vtype);
+ return NULL;
+ }
+
+ ret = ges_transition_clip_new_for_nick (((gchar *) value->value_nick));
+ g_type_class_unref (klass);
+
+ return ret;
+}
+
+/**
+ * ges_transition_clip_new_for_nick:
+ * @nick: a string representing the type of transition to create
+ *
+ * Creates a new #GESTransitionClip for the provided @nick.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESTransitionClip,
+ * or %NULL if something went wrong
+ */
+
+GESTransitionClip *
+ges_transition_clip_new_for_nick (gchar * nick)
+{
+ GESTransitionClip *ret = NULL;
+ GESAsset *asset = ges_asset_request (GES_TYPE_TRANSITION_CLIP, nick, NULL);
+
+ if (asset != NULL) {
+ ret = GES_TRANSITION_CLIP (ges_asset_extract (asset, NULL));
+
+ gst_object_unref (asset);
+ } else
+ GST_WARNING ("No asset found for nick: %s", nick);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_TRANSITION_CLIP
+#define _GES_TRANSITION_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-base-transition-clip.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TRANSITION_CLIP ges_transition_clip_get_type()
+
+#define GES_TRANSITION_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TRANSITION_CLIP, GESTransitionClip))
+
+#define GES_TRANSITION_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TRANSITION_CLIP, GESTransitionClipClass))
+
+#define GES_IS_TRANSITION_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TRANSITION_CLIP))
+
+#define GES_IS_TRANSITION_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TRANSITION_CLIP))
+
+#define GES_TRANSITION_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TRANSITION_CLIP, GESTransitionClipClass))
+
+typedef struct _GESTransitionClipPrivate GESTransitionClipPrivate;
+
+/**
+ * GESTransitionClip:
+ * @vtype: a #GESVideoStandardTransitionType indicating the type of video transition
+ * to apply.
+ */
+struct _GESTransitionClip {
+ /*< private >*/
+ GESBaseTransitionClip parent;
+
+ /*< public >*/
+ GESVideoStandardTransitionType vtype;
+
+ /*< private >*/
+ GESTransitionClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTransitionClipClass:
+ *
+ */
+
+struct _GESTransitionClipClass {
+ /*< private >*/
+ GESBaseTransitionClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_transition_clip_get_type (void);
+
+GES_API
+GESTransitionClip *ges_transition_clip_new (GESVideoStandardTransitionType vtype);
+GES_API
+GESTransitionClip *ges_transition_clip_new_for_nick (char *nick);
+
+G_END_DECLS
+
+#endif /* _GES_TRANSITION_CLIP */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gestransition
+ * @title: GESTransition
+ * @short_description: base class for audio and video transitions
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+#include "ges-internal.h"
+
+struct _GESTransitionPrivate
+{
+ /* Dummy variable */
+ void *nothing;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESTransition, ges_transition,
+ GES_TYPE_OPERATION);
+
+
+static void
+ges_transition_class_init (GESTransitionClass * klass)
+{
+}
+
+static void
+ges_transition_init (GESTransition * self)
+{
+ self->priv = ges_transition_get_instance_private (self);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_TRANSITION
+#define _GES_TRANSITION
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <gst/controller/gstinterpolationcontrolsource.h>
+#include <ges/ges-types.h>
+#include <ges/ges-operation.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_TRANSITION ges_transition_get_type()
+
+#define GES_TRANSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_TRANSITION,\
+ GESTransition))
+
+#define GES_TRANSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_TRANSITION,\
+ GESTransitionClass))
+
+#define GES_IS_TRANSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_TRANSITION))
+
+#define GES_IS_TRANSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_TRANSITION))
+
+#define GES_TRANSITION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_TRANSITION,\
+ GESTransitionClass))
+
+typedef struct _GESTransitionPrivate GESTransitionPrivate;
+
+/**
+ * GESTransition:
+ *
+ * Base class for media transitions.
+ */
+
+struct _GESTransition
+{
+ /*< private >*/
+ GESOperation parent;
+
+ GESTransitionPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESTransitionClass:
+ */
+
+struct _GESTransitionClass {
+ /*< private >*/
+ GESOperationClass parent_class;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_transition_get_type (void);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 __GES_TYPES_H__
+#define __GES_TYPES_H__
+
+#include <glib.h>
+#include <ges/ges-prelude.h>
+
+G_BEGIN_DECLS
+
+/* Padding */
+#define GES_PADDING 4
+
+/* padding for very extensible base classes */
+#define GES_PADDING_LARGE 20
+
+/* Type definitions */
+
+typedef struct _GESTimeline GESTimeline;
+typedef struct _GESTimelineClass GESTimelineClass;
+
+typedef struct _GESLayer GESLayer;
+typedef struct _GESLayerClass GESLayerClass;
+
+typedef struct _GESTimelineElementClass GESTimelineElementClass;
+typedef struct _GESTimelineElement GESTimelineElement;
+
+typedef struct _GESContainer GESContainer;
+typedef struct _GESContainerClass GESContainerClass;
+
+typedef struct _GESClip GESClip;
+typedef struct _GESClipClass GESClipClass;
+
+typedef struct _GESOperationClip GESOperationClip;
+typedef struct _GESOperationClipClass GESOperationClipClass;
+
+typedef struct _GESPipeline GESPipeline;
+typedef struct _GESPipelineClass GESPipelineClass;
+
+typedef struct _GESSourceClip GESSourceClip;
+typedef struct _GESSourceClipClass GESSourceClipClass;
+
+typedef struct _GESBaseEffectClip GESBaseEffectClip;
+typedef struct _GESBaseEffectClipClass GESBaseEffectClipClass;
+
+typedef struct _GESUriClip GESUriClip;
+typedef struct _GESUriClipClass GESUriClipClass;
+
+typedef struct _GESBaseTransitionClip GESBaseTransitionClip;
+typedef struct _GESBaseTransitionClipClass GESBaseTransitionClipClass;
+
+typedef struct _GESTransitionClip GESTransitionClip;
+typedef struct _GESTransitionClipClass GESTransitionClipClass;
+
+typedef struct _GESTestClip GESTestClip;
+typedef struct _GESTestClipClass GESTestClipClass;
+
+typedef struct _GESTitleClip GESTitleClip;
+typedef struct _GESTitleClipClass GESTitleClipClass;
+
+typedef struct _GESOverlayClip GESOverlayClip;
+typedef struct _GESOverlayClipClass GESOverlayClipClass;
+
+typedef struct _GESTextOverlayClip GESTextOverlayClip;
+typedef struct _GESTextOverlayClipClass GESTextOverlayClipClass;
+
+typedef struct _GESEffectClip GESEffectClip;
+typedef struct _GESEffectClipClass GESEffectClipClass;
+
+typedef struct _GESGroup GESGroup;
+typedef struct _GESGroupClass GESGroupClass;
+
+typedef struct _GESTrack GESTrack;
+typedef struct _GESTrackClass GESTrackClass;
+
+typedef struct _GESTrackElement GESTrackElement;
+typedef struct _GESTrackElementClass GESTrackElementClass;
+
+typedef struct _GESSource GESSource;
+typedef struct _GESSourceClass GESSourceClass;
+
+typedef struct _GESOperation GESOperation;
+typedef struct _GESOperationClass GESOperationClass;
+
+typedef struct _GESBaseEffect GESBaseEffect;
+typedef struct _GESBaseEffectClass GESBaseEffectClass;
+
+typedef struct _GESEffect GESEffect;
+typedef struct _GESEffectClass GESEffectClass;
+
+typedef struct _GESVideoSource GESVideoSource;
+typedef struct _GESVideoSourceClass GESVideoSourceClass;
+
+typedef struct _GESAudioSource GESAudioSource;
+typedef struct _GESAudioSourceClass GESAudioSourceClass;
+
+typedef struct _GESVideoUriSource GESVideoUriSource;
+typedef struct _GESVideoUriSourceClass GESVideoUriSourceClass;
+
+typedef struct _GESAudioUriSource GESAudioUriSource;
+typedef struct _GESAudioUriSourceClass GESAudioUriSourceClass;
+
+typedef struct _GESImageSource GESImageSource;
+typedef struct _GESImageSourceClass GESImageSourceClass;
+
+typedef struct _GESMultiFileSource GESMultiFileSource;
+typedef struct _GESMultiFileSourceClass GESMultiFileSourceClass;
+
+typedef struct _GESTransition GESTransition;
+typedef struct _GESTransitionClass GESTransitionClass;
+
+typedef struct _GESAudioTransition GESAudioTransition;
+typedef struct _GESAudioTransitionClass
+ GESAudioTransitionClass;
+
+typedef struct _GESVideoTransition GESVideoTransition;
+typedef struct _GESVideoTransitionClass
+ GESVideoTransitionClass;
+
+typedef struct _GESVideoTestSource GESVideoTestSource;
+typedef struct _GESVideoTestSourceClass
+ GESVideoTestSourceClass;
+
+typedef struct _GESAudioTestSource GESAudioTestSource;
+typedef struct _GESAudioTestSourceClass
+ GESAudioTestSourceClass;
+
+typedef struct _GESTitleSource GESTitleSource;
+typedef struct _GESTitleSourceClass
+ GESTitleSourceClass;
+
+typedef struct _GESTextOverlay GESTextOverlay;
+typedef struct _GESTextOverlayClass
+ GESTextOverlayClass;
+
+typedef struct _GESFormatter GESFormatter;
+typedef struct _GESFormatterClass GESFormatterClass;
+
+typedef struct _GESPitiviFormatter GESPitiviFormatter;
+typedef struct _GESPitiviFormatterClass GESPitiviFormatterClass;
+
+typedef struct _GESAsset GESAsset;
+typedef struct _GESAssetClass GESAssetClass;
+
+typedef struct _GESClipAsset GESClipAsset;
+typedef struct _GESClipAssetClass GESClipAssetClass;
+
+typedef struct _GESUriClipAsset GESUriClipAsset;
+typedef struct _GESUriClipAssetClass GESUriClipAssetClass;
+
+typedef struct _GESTrackElementAsset GESTrackElementAsset;
+typedef struct _GESTrackElementAssetClass GESTrackElementAssetClass;
+
+typedef struct _GESUriSourceAsset GESUriSourceAsset;
+typedef struct _GESUriSourceAssetClass GESUriSourceAssetClass;
+
+typedef struct _GESProject GESProject;
+typedef struct _GESProjectClass GESProjectClass;
+
+typedef struct _GESExtractable GESExtractable;
+typedef struct _GESExtractableInterface GESExtractableInterface;
+
+typedef struct _GESVideoTrackClass GESVideoTrackClass;
+typedef struct _GESVideoTrack GESVideoTrack;
+
+typedef struct _GESAudioTrackClass GESAudioTrackClass;
+typedef struct _GESAudioTrack GESAudioTrack;
+
+G_END_DECLS
+
+#endif /* __GES_TYPES_H__ */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION: gesuriclipasset
+ * @title: GESUriClipAsset
+ * @short_description: A GESAsset subclass specialized in GESUriClip extraction
+ *
+ * The #GESUriClipAsset is a special #GESAsset that lets you handle
+ * the media file to use inside the GStreamer Editing Services. It has APIs that
+ * let you get information about the medias. Also, the tags found in the media file are
+ * set as Metadata of the Asset.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <gst/pbutils/pbutils.h>
+#include "ges.h"
+#include "ges-internal.h"
+#include "ges-track-element-asset.h"
+
+#define DEFAULT_DISCOVERY_TIMEOUT (60 * GST_SECOND)
+
+static GHashTable *parent_newparent_table = NULL;
+
+static GstDiscoverer *discoverer = NULL;
+
+static void
+initable_iface_init (GInitableIface * initable_iface)
+{
+ /* We can not iniate synchronously */
+ initable_iface->init = NULL;
+}
+
+/* TODO: We should monitor files here, and add some way of reporting changes
+ * to user
+ */
+enum
+{
+ PROP_0,
+ PROP_DURATION,
+ PROP_LAST
+};
+static GParamSpec *properties[PROP_LAST];
+
+static void discoverer_discovered_cb (GstDiscoverer * discoverer,
+ GstDiscovererInfo * info, GError * err, gpointer user_data);
+
+struct _GESUriClipAssetPrivate
+{
+ GstDiscovererInfo *info;
+ GstClockTime duration;
+ gboolean is_image;
+
+ GList *asset_trackfilesources;
+};
+
+typedef struct
+{
+ GMainLoop *ml;
+ GESAsset *asset;
+ GError *error;
+} RequestSyncData;
+
+struct _GESUriSourceAssetPrivate
+{
+ GstDiscovererStreamInfo *sinfo;
+ GESUriClipAsset *parent_asset;
+
+ const gchar *uri;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GESUriClipAsset, ges_uri_clip_asset,
+ GES_TYPE_CLIP_ASSET, G_ADD_PRIVATE (GESUriClipAsset)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
+
+static void
+ges_uri_clip_asset_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
+
+ switch (property_id) {
+ case PROP_DURATION:
+ g_value_set_uint64 (value, priv->duration);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_uri_clip_asset_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
+
+ switch (property_id) {
+ case PROP_DURATION:
+ priv->duration = g_value_get_uint64 (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GESAssetLoadingReturn
+_start_loading (GESAsset * asset, GError ** error)
+{
+ gboolean ret;
+ const gchar *uri;
+ GESUriClipAssetClass *class = GES_URI_CLIP_ASSET_GET_CLASS (asset);
+
+ GST_DEBUG ("Started loading %p", asset);
+
+ uri = ges_asset_get_id (asset);
+
+ ret = gst_discoverer_discover_uri_async (class->discoverer, uri);
+ if (ret)
+ return GES_ASSET_LOADING_ASYNC;
+
+ return GES_ASSET_LOADING_ERROR;
+}
+
+static gboolean
+_request_id_update (GESAsset * self, gchar ** proposed_new_id, GError * error)
+{
+ if (error->domain == GST_RESOURCE_ERROR &&
+ (error->code == GST_RESOURCE_ERROR_NOT_FOUND ||
+ error->code == GST_RESOURCE_ERROR_OPEN_READ)) {
+ const gchar *uri = ges_asset_get_id (self);
+ GFile *parent, *file = g_file_new_for_uri (uri);
+
+ /* Check if we have the new parent in cache */
+ parent = g_file_get_parent (file);
+ if (parent) {
+ GFile *new_parent = g_hash_table_lookup (parent_newparent_table, parent);
+
+ if (new_parent) {
+ gchar *basename = g_file_get_basename (file);
+ GFile *new_file = g_file_get_child (new_parent, basename);
+
+ /* FIXME Handle the GCancellable */
+ if (g_file_query_exists (new_file, NULL)) {
+ *proposed_new_id = g_file_get_uri (new_file);
+ GST_DEBUG_OBJECT (self, "Proposing path: %s as proxy",
+ *proposed_new_id);
+ }
+
+ gst_object_unref (new_file);
+ g_free (basename);
+ }
+ gst_object_unref (parent);
+ }
+
+ gst_object_unref (file);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+_asset_proxied (GESAsset * self, const gchar * new_uri)
+{
+ const gchar *uri = ges_asset_get_id (self);
+ GFile *parent, *new_parent, *new_file = g_file_new_for_uri (new_uri),
+ *file = g_file_new_for_uri (uri);
+
+ parent = g_file_get_parent (file);
+ new_parent = g_file_get_parent (new_file);
+ g_hash_table_insert (parent_newparent_table, parent, new_parent);
+ gst_object_unref (file);
+ gst_object_unref (new_file);
+}
+
+static void
+ges_uri_clip_asset_dispose (GObject * object)
+{
+ GESUriClipAsset *self = GES_URI_CLIP_ASSET (object);
+ GESUriClipAssetPrivate *prif = self->priv;
+
+ if (prif->asset_trackfilesources) {
+ g_list_free_full (prif->asset_trackfilesources,
+ (GDestroyNotify) gst_object_unref);
+ prif->asset_trackfilesources = NULL;
+ }
+
+ gst_clear_object (&prif->info);
+
+ G_OBJECT_CLASS (ges_uri_clip_asset_parent_class)->dispose (object);
+}
+
+static void
+ges_uri_clip_asset_class_init (GESUriClipAssetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ges_uri_clip_asset_get_property;
+ object_class->set_property = ges_uri_clip_asset_set_property;
+ object_class->dispose = ges_uri_clip_asset_dispose;
+
+ GES_ASSET_CLASS (klass)->start_loading = _start_loading;
+ GES_ASSET_CLASS (klass)->request_id_update = _request_id_update;
+ GES_ASSET_CLASS (klass)->inform_proxy = _asset_proxied;
+
+ klass->discovered = discoverer_discovered_cb;
+
+
+ /**
+ * GESUriClipAsset:duration:
+ *
+ * The duration (in nanoseconds) of the media file
+ */
+ properties[PROP_DURATION] =
+ g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
+ G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_DURATION,
+ properties[PROP_DURATION]);
+
+ _ges_uri_asset_ensure_setup (klass);
+}
+
+static void
+ges_uri_clip_asset_init (GESUriClipAsset * self)
+{
+ GESUriClipAssetPrivate *priv;
+
+ priv = self->priv = ges_uri_clip_asset_get_instance_private (self);
+
+ priv->info = NULL;
+ priv->duration = GST_CLOCK_TIME_NONE;
+ priv->is_image = FALSE;
+}
+
+static void
+_create_uri_source_asset (GESUriClipAsset * asset,
+ GstDiscovererStreamInfo * sinfo, GESTrackType type)
+{
+ GESAsset *tck_filesource_asset;
+ GESUriSourceAssetPrivate *priv_tckasset;
+ GESUriClipAssetPrivate *priv = asset->priv;
+ gchar *stream_id =
+ g_strdup (gst_discoverer_stream_info_get_stream_id (sinfo));
+
+ if (stream_id == NULL) {
+ GST_WARNING ("No stream ID found, using the pointer instead");
+
+ stream_id = g_strdup_printf ("%i", GPOINTER_TO_INT (sinfo));
+ }
+
+ if (type == GES_TRACK_TYPE_VIDEO)
+ tck_filesource_asset = ges_asset_request (GES_TYPE_VIDEO_URI_SOURCE,
+ stream_id, NULL);
+ else
+ tck_filesource_asset = ges_asset_request (GES_TYPE_AUDIO_URI_SOURCE,
+ stream_id, NULL);
+ g_free (stream_id);
+
+ priv_tckasset = GES_URI_SOURCE_ASSET (tck_filesource_asset)->priv;
+ priv_tckasset->uri = ges_asset_get_id (GES_ASSET (asset));
+ priv_tckasset->sinfo = gst_object_ref (sinfo);
+ priv_tckasset->parent_asset = asset;
+ ges_track_element_asset_set_track_type (GES_TRACK_ELEMENT_ASSET
+ (tck_filesource_asset), type);
+
+ priv->asset_trackfilesources = g_list_append (priv->asset_trackfilesources,
+ tck_filesource_asset);
+}
+
+static void
+ges_uri_clip_asset_set_info (GESUriClipAsset * self, GstDiscovererInfo * info)
+{
+ GList *tmp, *stream_list;
+
+ GESTrackType supportedformats = GES_TRACK_TYPE_UNKNOWN;
+ GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (self)->priv;
+
+ /* Extract infos from the GstDiscovererInfo */
+ stream_list = gst_discoverer_info_get_stream_list (info);
+ for (tmp = stream_list; tmp; tmp = tmp->next) {
+ GESTrackType type = GES_TRACK_TYPE_UNKNOWN;
+ GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
+
+ if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
+ if (supportedformats == GES_TRACK_TYPE_UNKNOWN)
+ supportedformats = GES_TRACK_TYPE_AUDIO;
+ else
+ supportedformats |= GES_TRACK_TYPE_AUDIO;
+
+ type = GES_TRACK_TYPE_AUDIO;
+ } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
+ if (supportedformats == GES_TRACK_TYPE_UNKNOWN)
+ supportedformats = GES_TRACK_TYPE_VIDEO;
+ else
+ supportedformats |= GES_TRACK_TYPE_VIDEO;
+ if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
+ sinf))
+ priv->is_image = TRUE;
+ type = GES_TRACK_TYPE_VIDEO;
+ }
+
+ GST_DEBUG_OBJECT (self, "Creating GESUriSourceAsset for stream: %s",
+ gst_discoverer_stream_info_get_stream_id (sinf));
+ _create_uri_source_asset (self, sinf, type);
+ }
+ ges_clip_asset_set_supported_formats (GES_CLIP_ASSET
+ (self), supportedformats);
+
+ if (stream_list)
+ gst_discoverer_stream_info_list_free (stream_list);
+
+ if (priv->is_image == FALSE)
+ priv->duration = gst_discoverer_info_get_duration (info);
+ /* else we keep #GST_CLOCK_TIME_NONE */
+
+ priv->info = gst_object_ref (info);
+}
+
+static void
+_set_meta_file_size (const gchar * uri, GESUriClipAsset * asset)
+{
+ GError *error = NULL;
+ GFileInfo *file_info = NULL;
+ guint64 file_size;
+ GFile *gfile = NULL;
+
+ GESMetaContainer *container = GES_META_CONTAINER (asset);
+
+ gfile = g_file_new_for_uri (uri);
+ file_info = g_file_query_info (gfile, "standard::size",
+ G_FILE_QUERY_INFO_NONE, NULL, &error);
+ if (!error) {
+ file_size = g_file_info_get_attribute_uint64 (file_info, "standard::size");
+ ges_meta_container_register_meta_uint64 (container, GES_META_READ_WRITE,
+ "file-size", file_size);
+ } else {
+ g_error_free (error);
+ }
+ if (gfile)
+ g_object_unref (gfile);
+ if (file_info)
+ g_object_unref (file_info);
+}
+
+static void
+_set_meta_foreach (const GstTagList * tags, const gchar * tag,
+ GESMetaContainer * container)
+{
+ GValue value = { 0 };
+
+ if (gst_tag_list_copy_value (&value, tags, tag)) {
+ ges_meta_container_set_meta (container, tag, &value);
+ g_value_unset (&value);
+ } else {
+ GST_INFO ("Could not set metadata: %s", tag);
+ }
+}
+
+static void
+discoverer_discovered_cb (GstDiscoverer * discoverer,
+ GstDiscovererInfo * info, GError * err, gpointer user_data)
+{
+ GError *error = NULL;
+ const GstTagList *tags;
+
+ const gchar *uri = gst_discoverer_info_get_uri (info);
+ GESUriClipAsset *mfs =
+ GES_URI_CLIP_ASSET (ges_asset_cache_lookup (GES_TYPE_URI_CLIP, uri));
+
+ tags = gst_discoverer_info_get_tags (info);
+ if (tags)
+ gst_tag_list_foreach (tags, (GstTagForeachFunc) _set_meta_foreach, mfs);
+
+ _set_meta_file_size (uri, mfs);
+
+ if (gst_discoverer_info_get_result (info) == GST_DISCOVERER_OK) {
+ ges_uri_clip_asset_set_info (mfs, info);
+ } else {
+ if (err) {
+ error = g_error_copy (err);
+ } else {
+ error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
+ "Stream %s discovering failed (error code: %d)",
+ uri, gst_discoverer_info_get_result (info));
+ }
+ }
+
+ ges_asset_cache_set_loaded (GES_TYPE_URI_CLIP, uri, error);
+
+ if (error)
+ g_error_free (error);
+}
+
+static void
+asset_ready_cb (GESAsset * source, GAsyncResult * res, RequestSyncData * data)
+{
+ data->asset = ges_asset_request_finish (res, &data->error);
+
+ if (data->error) {
+ gchar *possible_uri = ges_uri_asset_try_update_id (data->error, source);
+
+ if (possible_uri) {
+ g_clear_error (&data->error);
+ ges_asset_request_async (GES_TYPE_URI_CLIP, possible_uri, NULL,
+ (GAsyncReadyCallback) asset_ready_cb, data);
+ g_free (possible_uri);
+
+ return;
+ }
+ }
+ g_main_loop_quit (data->ml);
+}
+
+/* API implementation */
+/**
+ * ges_uri_clip_asset_get_info:
+ * @self: Target asset
+ *
+ * Gets #GstDiscovererInfo about the file
+ *
+ * Returns: (transfer none): #GstDiscovererInfo of specified asset
+ */
+GstDiscovererInfo *
+ges_uri_clip_asset_get_info (const GESUriClipAsset * self)
+{
+ g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), NULL);
+
+ return self->priv->info;
+}
+
+/**
+ * ges_uri_clip_asset_get_duration:
+ * @self: a #GESUriClipAsset
+ *
+ * Gets duration of the file represented by @self
+ *
+ * Returns: The duration of @self
+ */
+GstClockTime
+ges_uri_clip_asset_get_duration (GESUriClipAsset * self)
+{
+ g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), GST_CLOCK_TIME_NONE);
+
+ return self->priv->duration;
+}
+
+/**
+ * ges_uri_clip_asset_is_image:
+ * @self: a #indent: Standard input:311: Error:Unexpected end of file
+GESUriClipAsset
+ *
+ * Gets Whether the file represented by @self is an image or not
+ *
+ * Returns: Whether the file represented by @self is an image or not
+ */
+gboolean
+ges_uri_clip_asset_is_image (GESUriClipAsset * self)
+{
+ g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
+
+ return self->priv->is_image;
+}
+
+/**
+ * ges_uri_clip_asset_new:
+ * @uri: The URI of the file for which to create a #GESUriClipAsset
+ * @cancellable: optional %GCancellable object, %NULL to ignore.
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the initialization is finished
+ * @user_data: The user data to pass when @callback is called
+ *
+ * Creates a #GESUriClipAsset for @uri
+ *
+ * Example of request of a GESUriClipAsset:
+ * |[
+ * // The request callback
+ * static void
+ * filesource_asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
+ * {
+ * GError *error = NULL;
+ * GESUriClipAsset *filesource_asset;
+ *
+ * filesource_asset = ges_uri_clip_asset_finish (res, &error);
+ * if (filesource_asset) {
+ * g_print ("The file: %s is usable as a FileSource, it is%s an image and lasts %" GST_TIME_FORMAT,
+ * ges_asset_get_id (GES_ASSET (filesource_asset))
+ * ges_uri_clip_asset_is_image (filesource_asset) ? "" : " not",
+ * GST_TIME_ARGS (ges_uri_clip_asset_get_duration (filesource_asset));
+ * } else {
+ * g_print ("The file: %s is *not* usable as a FileSource because: %s",
+ * ges_asset_get_id (source), error->message);
+ * }
+ *
+ * gst_object_unref (mfs);
+ * }
+ *
+ * // The request:
+ * ges_uri_clip_asset_new (uri, (GAsyncReadyCallback) filesource_asset_loaded_cb, user_data);
+ * ]|
+ */
+void
+ges_uri_clip_asset_new (const gchar * uri, GCancellable * cancellable,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ ges_asset_request_async (GES_TYPE_URI_CLIP, uri, cancellable,
+ callback, user_data);
+}
+
+/**
+ * ges_uri_clip_asset_finish:
+ * @res: The #GAsyncResult from which to get the newly created #GESUriClipAsset
+ * @error: An error to be set in case something wrong happens or %NULL
+ *
+ * Finalize the request of an async #GESUriClipAsset
+ *
+ * Returns: (transfer full): The #GESUriClipAsset previously requested
+ *
+ * Since: 1.16
+ */
+GESUriClipAsset *
+ges_uri_clip_asset_finish (GAsyncResult * res, GError ** error)
+{
+ GESAsset *asset;
+
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+ asset = ges_asset_request_finish (res, error);
+ if (asset != NULL) {
+ return GES_URI_CLIP_ASSET (asset);
+ }
+
+ return NULL;
+}
+
+/**
+ * ges_uri_clip_asset_request_sync:
+ * @uri: The URI of the file for which to create a #GESUriClipAsset.
+ * You can also use multi file uris for #GESMultiFileSource.
+ * @error: An error to be set in case something wrong happens or %NULL
+ *
+ * Creates a #GESUriClipAsset for @uri syncronously. You should avoid
+ * to use it in application, and rather create #GESUriClipAsset asynchronously
+ *
+ * Returns: (transfer full): A reference to the requested asset or %NULL if
+ * an error happened
+ */
+GESUriClipAsset *
+ges_uri_clip_asset_request_sync (const gchar * uri, GError ** error)
+{
+ GError *lerror = NULL;
+ GESUriClipAsset *asset;
+ RequestSyncData data = { 0, };
+ GESUriClipAssetClass *klass = g_type_class_peek (GES_TYPE_URI_CLIP_ASSET);
+ GstClockTime timeout;
+ GstDiscoverer *previous_discoverer = klass->discoverer;
+
+ asset = GES_URI_CLIP_ASSET (ges_asset_request (GES_TYPE_URI_CLIP, uri,
+ &lerror));
+
+ if (asset)
+ return asset;
+
+ data.ml = g_main_loop_new (NULL, TRUE);
+ g_object_get (previous_discoverer, "timeout", &timeout, NULL);
+ klass->discoverer = gst_discoverer_new (timeout, error);
+ if (!klass->discoverer) {
+ klass->discoverer = previous_discoverer;
+
+ return NULL;
+ }
+
+ g_signal_connect (klass->discoverer, "discovered",
+ G_CALLBACK (klass->discovered), NULL);
+ gst_discoverer_start (klass->discoverer);
+ ges_asset_request_async (GES_TYPE_URI_CLIP, uri, NULL,
+ (GAsyncReadyCallback) asset_ready_cb, &data);
+ g_main_loop_run (data.ml);
+ g_main_loop_unref (data.ml);
+
+ gst_object_unref (klass->discoverer);
+ klass->discoverer = previous_discoverer;
+
+ if (data.error) {
+ GST_ERROR ("Got an error requesting asset: %s", data.error->message);
+ if (error != NULL)
+ g_propagate_error (error, data.error);
+
+ return NULL;
+ }
+
+ return GES_URI_CLIP_ASSET (data.asset);
+}
+
+/**
+ * ges_uri_clip_asset_class_set_timeout:
+ * @klass: The #GESUriClipAssetClass on which to set the discoverer timeout
+ * @timeout: The timeout to set
+ *
+ * Sets the timeout of #GESUriClipAsset loading
+ */
+void
+ges_uri_clip_asset_class_set_timeout (GESUriClipAssetClass * klass,
+ GstClockTime timeout)
+{
+ g_return_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (klass));
+
+ g_object_set (klass->discoverer, "timeout", timeout, NULL);
+}
+
+/**
+ * ges_uri_clip_asset_get_stream_assets:
+ * @self: A #GESUriClipAsset
+ *
+ * Get the GESUriSourceAsset @self containes
+ *
+ * Returns: (transfer none) (element-type GESUriSourceAsset): a
+ * #GList of #GESUriSourceAsset
+ */
+const GList *
+ges_uri_clip_asset_get_stream_assets (GESUriClipAsset * self)
+{
+ g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
+
+ return self->priv->asset_trackfilesources;
+}
+
+/*****************************************************************
+ * GESUriSourceAsset implementation *
+ *****************************************************************/
+/**
+ * SECTION: gesurisourceasset
+ * @title: GESUriClipAsset
+ * @short_description: A GESAsset subclass specialized in GESUriSource extraction
+ *
+ * NOTE: You should never request such a #GESAsset as they will be created automatically
+ * by #GESUriClipAsset-s.
+ */
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESUriSourceAsset, ges_uri_source_asset,
+ GES_TYPE_TRACK_ELEMENT_ASSET);
+
+static GESExtractable *
+_extract (GESAsset * asset, GError ** error)
+{
+ gchar *uri = NULL;
+ GESTrackElement *trackelement;
+ GESUriSourceAssetPrivate *priv = GES_URI_SOURCE_ASSET (asset)->priv;
+
+ if (GST_IS_DISCOVERER_STREAM_INFO (priv->sinfo) == FALSE) {
+ GST_WARNING_OBJECT (asset, "Can not extract as no strean info set");
+
+ return NULL;
+ }
+
+ if (priv->uri == NULL) {
+ GST_WARNING_OBJECT (asset, "Can not extract as no uri set");
+
+ return NULL;
+ }
+
+ uri = g_strdup (priv->uri);
+
+ if (g_str_has_prefix (priv->uri, GES_MULTI_FILE_URI_PREFIX)) {
+ trackelement = GES_TRACK_ELEMENT (ges_multi_file_source_new (uri));
+ } else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo)
+ && gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
+ priv->sinfo))
+ trackelement = GES_TRACK_ELEMENT (ges_image_source_new (uri));
+ else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo))
+ trackelement = GES_TRACK_ELEMENT (ges_video_uri_source_new (uri));
+ else
+ trackelement = GES_TRACK_ELEMENT (ges_audio_uri_source_new (uri));
+
+ ges_track_element_set_track_type (trackelement,
+ ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET (asset)));
+
+ g_free (uri);
+
+ return GES_EXTRACTABLE (trackelement);
+}
+
+static void
+ges_uri_source_asset_dispose (GObject * object)
+{
+ GESUriSourceAsset *self = GES_URI_SOURCE_ASSET (object);
+ GESUriSourceAssetPrivate *priv = self->priv;
+
+ gst_clear_object (&priv->sinfo);
+
+ G_OBJECT_CLASS (ges_uri_source_asset_parent_class)->dispose (object);
+}
+
+static void
+ges_uri_source_asset_class_init (GESUriSourceAssetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ges_uri_source_asset_dispose;
+
+ GES_ASSET_CLASS (klass)->extract = _extract;
+}
+
+static void
+ges_uri_source_asset_init (GESUriSourceAsset * self)
+{
+ GESUriSourceAssetPrivate *priv;
+
+ priv = self->priv = ges_uri_source_asset_get_instance_private (self);
+
+ priv->sinfo = NULL;
+ priv->parent_asset = NULL;
+ priv->uri = NULL;
+}
+
+/**
+ * ges_uri_source_asset_get_stream_info:
+ * @asset: A #GESUriClipAsset
+ *
+ * Get the #GstDiscovererStreamInfo user by @asset
+ *
+ * Returns: (transfer none): a #GESUriClipAsset
+ */
+GstDiscovererStreamInfo *
+ges_uri_source_asset_get_stream_info (GESUriSourceAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
+
+ return asset->priv->sinfo;
+}
+
+const gchar *
+ges_uri_source_asset_get_stream_uri (GESUriSourceAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
+
+ return asset->priv->uri;
+}
+
+/**
+ * ges_uri_source_asset_get_filesource_asset:
+ * @asset: A #GESUriClipAsset
+ *
+ * Get the #GESUriClipAsset @self is contained in
+ *
+ * Returns: a #GESUriClipAsset
+ */
+const GESUriClipAsset *
+ges_uri_source_asset_get_filesource_asset (GESUriSourceAsset * asset)
+{
+ g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
+
+ return asset->priv->parent_asset;
+}
+
+void
+_ges_uri_asset_cleanup (void)
+{
+ if (discoverer)
+ gst_discoverer_stop (discoverer);
+ g_clear_object (&discoverer);
+ if (parent_newparent_table) {
+ g_hash_table_destroy (parent_newparent_table);
+ parent_newparent_table = NULL;
+ }
+}
+
+gboolean
+_ges_uri_asset_ensure_setup (gpointer uriasset_class)
+{
+ GESUriClipAssetClass *klass;
+ GError *err;
+ GstClockTime timeout;
+ const gchar *timeout_str;
+
+ g_return_val_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (uriasset_class), FALSE);
+
+ klass = GES_URI_CLIP_ASSET_CLASS (uriasset_class);
+
+ timeout = DEFAULT_DISCOVERY_TIMEOUT;
+ errno = 0;
+ timeout_str = g_getenv ("GES_DISCOVERY_TIMEOUT");
+ if (timeout_str)
+ timeout = g_ascii_strtod (timeout_str, NULL) * GST_SECOND;
+ else
+ errno = 10;
+
+ if (errno)
+ timeout = DEFAULT_DISCOVERY_TIMEOUT;
+
+ if (!discoverer) {
+ discoverer = gst_discoverer_new (timeout, &err);
+ if (!discoverer) {
+ GST_ERROR ("Could not create discoverer: %s", err->message);
+ g_error_free (err);
+ return FALSE;
+ }
+ }
+
+ /* The class structure keeps weak pointers on the discoverers so they
+ * can be properly cleaned up in _ges_uri_asset_cleanup(). */
+ if (!klass->discoverer) {
+ klass->discoverer = klass->sync_discoverer = discoverer;
+ g_object_add_weak_pointer (G_OBJECT (discoverer),
+ (gpointer *) & klass->discoverer);
+ g_object_add_weak_pointer (G_OBJECT (discoverer),
+ (gpointer *) & klass->sync_discoverer);
+
+ g_signal_connect (klass->discoverer, "discovered",
+ G_CALLBACK (klass->discovered), NULL);
+ }
+
+ /* We just start the discoverer and let it live */
+ gst_discoverer_start (klass->discoverer);
+ if (parent_newparent_table == NULL) {
+ parent_newparent_table = g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
+ }
+
+ return TRUE;
+}
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef _GES_URI_CLIP_ASSET_
+#define _GES_URI_CLIP_ASSET_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <ges/ges-types.h>
+#include <ges/ges-asset.h>
+#include <ges/ges-clip-asset.h>
+#include <ges/ges-track-element-asset.h>
+
+G_BEGIN_DECLS
+#define GES_TYPE_URI_CLIP_ASSET ges_uri_clip_asset_get_type()
+#define GES_URI_CLIP_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_URI_CLIP_ASSET, GESUriClipAsset))
+#define GES_URI_CLIP_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_URI_CLIP_ASSET, GESUriClipAssetClass))
+#define GES_IS_URI_CLIP_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_URI_CLIP_ASSET))
+#define GES_IS_URI_CLIP_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_URI_CLIP_ASSET))
+#define GES_URI_CLIP_ASSET_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_URI_CLIP_ASSET, GESUriClipAssetClass))
+
+typedef struct _GESUriClipAssetPrivate GESUriClipAssetPrivate;
+
+GES_API
+GType ges_uri_clip_asset_get_type (void);
+
+struct _GESUriClipAsset
+{
+ GESClipAsset parent;
+
+ /* <private> */
+ GESUriClipAssetPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer __ges_reserved[GES_PADDING];
+};
+
+struct _GESUriClipAssetClass
+{
+ GESClipAssetClass parent_class;
+
+ /* <private> */
+ GstDiscoverer *discoverer;
+ GstDiscoverer *sync_discoverer;
+
+ void (*discovered) (GstDiscoverer * discoverer, GstDiscovererInfo * info,
+ GError * err, gpointer user_data);
+
+ gpointer _ges_reserved[GES_PADDING -1];
+};
+
+GES_API
+GstDiscovererInfo *ges_uri_clip_asset_get_info (const GESUriClipAsset * self);
+GES_API
+GstClockTime ges_uri_clip_asset_get_duration (GESUriClipAsset *self);
+GES_API
+gboolean ges_uri_clip_asset_is_image (GESUriClipAsset *self);
+GES_API
+void ges_uri_clip_asset_new (const gchar *uri,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GES_API
+GESUriClipAsset * ges_uri_clip_asset_finish (GAsyncResult * res, GError ** error);
+GES_API
+GESUriClipAsset* ges_uri_clip_asset_request_sync (const gchar *uri, GError **error);
+GES_API
+void ges_uri_clip_asset_class_set_timeout (GESUriClipAssetClass *klass,
+ GstClockTime timeout);
+GES_API
+const GList * ges_uri_clip_asset_get_stream_assets (GESUriClipAsset *self);
+
+#define GES_TYPE_URI_SOURCE_ASSET ges_uri_source_asset_get_type()
+#define GES_URI_SOURCE_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_URI_SOURCE_ASSET, GESUriSourceAsset))
+#define GES_URI_SOURCE_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_URI_SOURCE_ASSET, GESUriSourceAssetClass))
+#define GES_IS_URI_SOURCE_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_URI_SOURCE_ASSET))
+#define GES_IS_URI_SOURCE_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_URI_SOURCE_ASSET))
+#define GES_URI_SOURCE_ASSET_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_URI_SOURCE_ASSET, GESUriSourceAssetClass))
+
+typedef struct _GESUriSourceAssetPrivate GESUriSourceAssetPrivate;
+
+GES_API
+GType ges_uri_source_asset_get_type (void);
+
+struct _GESUriSourceAsset
+{
+ GESTrackElementAsset parent;
+
+ /* <private> */
+ GESUriSourceAssetPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer __ges_reserved[GES_PADDING];
+};
+
+struct _GESUriSourceAssetClass
+{
+ GESTrackElementAssetClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+GES_API
+GstDiscovererStreamInfo * ges_uri_source_asset_get_stream_info (GESUriSourceAsset *asset);
+GES_API
+const gchar * ges_uri_source_asset_get_stream_uri (GESUriSourceAsset *asset);
+GES_API
+const GESUriClipAsset *ges_uri_source_asset_get_filesource_asset (GESUriSourceAsset *asset);
+
+G_END_DECLS
+#endif /* _GES_URI_CLIP_ASSET */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gesuriclip
+ * @title: GESUriClip
+ * @short_description: An object for manipulating media files in a GESTimeline
+ *
+ * Represents all the output streams from a particular uri. It is assumed that
+ * the URI points to a file of some type.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-uri-clip.h"
+#include "ges-source-clip.h"
+#include "ges-video-uri-source.h"
+#include "ges-audio-uri-source.h"
+#include "ges-uri-asset.h"
+#include "ges-track-element-asset.h"
+#include "ges-extractable.h"
+#include "ges-image-source.h"
+#include "ges-audio-test-source.h"
+#include "ges-multi-file-source.h"
+#include "ges-layer.h"
+
+static void ges_extractable_interface_init (GESExtractableInterface * iface);
+
+#define parent_class ges_uri_clip_parent_class
+
+GESExtractableInterface *parent_extractable_iface;
+
+struct _GESUriClipPrivate
+{
+ gchar *uri;
+
+ gboolean mute;
+ gboolean is_image;
+};
+
+enum
+{
+ PROP_0,
+ PROP_URI,
+ PROP_MUTE,
+ PROP_IS_IMAGE,
+ PROP_SUPPORTED_FORMATS,
+};
+
+G_DEFINE_TYPE_WITH_CODE (GESUriClip, ges_uri_clip,
+ GES_TYPE_SOURCE_CLIP, G_ADD_PRIVATE (GESUriClip)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+static GList *ges_uri_clip_create_track_elements (GESClip *
+ clip, GESTrackType type);
+static GESTrackElement
+ * ges_uri_clip_create_track_element (GESClip * clip, GESTrackType type);
+static void ges_uri_clip_set_uri (GESUriClip * self, gchar * uri);
+
+gboolean
+uri_clip_set_max_duration (GESTimelineElement * element,
+ GstClockTime maxduration);
+
+static void
+ges_uri_clip_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESUriClipPrivate *priv = GES_URI_CLIP (object)->priv;
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, priv->uri);
+ break;
+ case PROP_MUTE:
+ g_value_set_boolean (value, priv->mute);
+ break;
+ case PROP_IS_IMAGE:
+ g_value_set_boolean (value, priv->is_image);
+ break;
+ case PROP_SUPPORTED_FORMATS:
+ g_value_set_flags (value,
+ ges_clip_get_supported_formats (GES_CLIP (object)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_uri_clip_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESUriClip *uriclip = GES_URI_CLIP (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ ges_uri_clip_set_uri (uriclip, g_value_dup_string (value));
+ break;
+ case PROP_MUTE:
+ ges_uri_clip_set_mute (uriclip, g_value_get_boolean (value));
+ break;
+ case PROP_IS_IMAGE:
+ ges_uri_clip_set_is_image (uriclip, g_value_get_boolean (value));
+ break;
+ case PROP_SUPPORTED_FORMATS:
+ ges_clip_set_supported_formats (GES_CLIP (uriclip),
+ g_value_get_flags (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_uri_clip_finalize (GObject * object)
+{
+ GESUriClipPrivate *priv = GES_URI_CLIP (object)->priv;
+
+ if (priv->uri)
+ g_free (priv->uri);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+ges_uri_clip_class_init (GESUriClipClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESClipClass *timobj_class = GES_CLIP_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->get_property = ges_uri_clip_get_property;
+ object_class->set_property = ges_uri_clip_set_property;
+ object_class->finalize = ges_uri_clip_finalize;
+
+
+ /**
+ * GESUriClip:uri:
+ *
+ * The location of the file/resource to use.
+ */
+ g_object_class_install_property (object_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "uri of the resource", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GESUriClip:mute:
+ *
+ * Whether the sound will be played or not.
+ */
+ g_object_class_install_property (object_class, PROP_MUTE,
+ g_param_spec_boolean ("mute", "Mute", "Mute audio track",
+ FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * GESUriClip:is-image:
+ *
+ * Whether this uri clip represents a still image or not. This must be set
+ * before create_track_elements is called.
+ */
+ g_object_class_install_property (object_class, PROP_IS_IMAGE,
+ g_param_spec_boolean ("is-image", "Is still image",
+ "Whether the clip represents a still image or not",
+ FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /* Redefine the supported formats property so the default value is UNKNOWN
+ * and not AUDIO | VIDEO */
+ g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
+ g_param_spec_flags ("supported-formats",
+ "Supported formats", "Formats supported by the file",
+ GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ element_class->set_max_duration = uri_clip_set_max_duration;
+
+ timobj_class->create_track_elements = ges_uri_clip_create_track_elements;
+ timobj_class->create_track_element = ges_uri_clip_create_track_element;
+}
+
+static gchar *
+extractable_check_id (GType type, const gchar * id)
+{
+ const gchar *testing_directory;
+
+ testing_directory = g_getenv ("GES_TESTING_ASSETS_DIRECTORY");
+
+ /* Testing purposes, user can specify a directory to look up for script */
+ if (testing_directory != NULL) {
+ gchar **tokens;
+ gchar *location = NULL;
+ guint i;
+
+ GST_DEBUG ("Checking if the testing directory contains needed media");
+
+ tokens = g_strsplit (id, "media", 2);
+ for (i = 0; tokens[i]; i++)
+ if (i == 1)
+ location = tokens[1];
+
+ if (location == NULL)
+ GST_WARNING ("The provided id doesn't have a media subdirectory");
+ else {
+ gchar *actual_id =
+ g_strconcat ("file://", testing_directory, "/media/", location, NULL);
+
+ if (gst_uri_is_valid (actual_id)) {
+ GST_DEBUG ("Returning new id %s instead of id %s", actual_id, id);
+ g_strfreev (tokens);
+ return (actual_id);
+ } else
+ GST_WARNING ("The constructed id %s was not valid, trying %s anyway",
+ actual_id, id);
+
+ g_free (actual_id);
+ }
+
+ g_strfreev (tokens);
+ }
+
+ if (gst_uri_is_valid (id))
+ return g_strdup (id);
+
+ return NULL;
+}
+
+static GParameter *
+extractable_get_parameters_from_id (const gchar * id, guint * n_params)
+{
+ GParameter *params = g_new0 (GParameter, 2);
+
+ params[0].name = "uri";
+ g_value_init (¶ms[0].value, G_TYPE_STRING);
+ g_value_set_string (¶ms[0].value, id);
+
+ *n_params = 1;
+
+ return params;
+}
+
+static gchar *
+extractable_get_id (GESExtractable * self)
+{
+ return g_strdup (GES_URI_CLIP (self)->priv->uri);
+}
+
+static gboolean
+extractable_set_asset (GESExtractable * self, GESAsset * asset)
+{
+ gboolean res = TRUE;
+ GESUriClip *uriclip = GES_URI_CLIP (self);
+ GESUriClipAsset *uri_clip_asset;
+ GESClip *clip = GES_CLIP (self);
+ GESLayer *layer = ges_clip_get_layer (clip);
+ GList *tmp;
+ GESTimelineElement *audio_source = NULL, *video_source = NULL;
+
+ g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (asset), FALSE);
+
+ uri_clip_asset = GES_URI_CLIP_ASSET (asset);
+ if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (self)) &&
+ ges_uri_clip_asset_get_duration (uri_clip_asset) <
+ GES_TIMELINE_ELEMENT_INPOINT (self) +
+ GES_TIMELINE_ELEMENT_DURATION (self)) {
+ GST_INFO_OBJECT (self,
+ "Can not set asset to %p as its duration is %" GST_TIME_FORMAT
+ " < to inpoint %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT " = %"
+ GST_TIME_FORMAT, asset,
+ GST_TIME_ARGS (ges_uri_clip_asset_get_duration (uri_clip_asset)),
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self)),
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self) +
+ GES_TIMELINE_ELEMENT_DURATION (self)));
+
+
+ return FALSE;
+ }
+
+ if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (clip)) == FALSE)
+ _set_duration0 (GES_TIMELINE_ELEMENT (uriclip),
+ ges_uri_clip_asset_get_duration (uri_clip_asset));
+
+ ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (uriclip),
+ ges_uri_clip_asset_get_duration (uri_clip_asset));
+ ges_uri_clip_set_is_image (uriclip,
+ ges_uri_clip_asset_is_image (uri_clip_asset));
+
+ if (ges_clip_get_supported_formats (clip) == GES_TRACK_TYPE_UNKNOWN) {
+ ges_clip_set_supported_formats (clip,
+ ges_clip_asset_get_supported_formats (GES_CLIP_ASSET (uri_clip_asset)));
+ }
+
+ GES_TIMELINE_ELEMENT (uriclip)->asset = asset;
+
+ if (layer) {
+ GList *children = ges_container_get_children (GES_CONTAINER (self), TRUE);
+
+ for (tmp = children; tmp; tmp = tmp->next) {
+ if (GES_IS_SOURCE (tmp->data)) {
+ GESTrack *track = ges_track_element_get_track (tmp->data);
+
+ if (track->type == GES_TRACK_TYPE_AUDIO)
+ audio_source = gst_object_ref (tmp->data);
+ else if (track->type == GES_TRACK_TYPE_VIDEO)
+ video_source = gst_object_ref (tmp->data);
+
+ ges_track_remove_element (track, tmp->data);
+ ges_container_remove (GES_CONTAINER (self), tmp->data);
+ }
+ }
+ g_list_free_full (children, g_object_unref);
+
+ gst_object_ref (clip);
+
+ ges_layer_remove_clip (layer, clip);
+ res = ges_layer_add_clip (layer, clip);
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ if (GES_IS_SOURCE (tmp->data)) {
+ GESTrack *track = ges_track_element_get_track (tmp->data);
+
+ if (track->type == GES_TRACK_TYPE_AUDIO && audio_source) {
+ ges_track_element_copy_properties (audio_source, tmp->data);
+ ges_track_element_copy_bindings (GES_TRACK_ELEMENT (audio_source),
+ tmp->data, GST_CLOCK_TIME_NONE);
+ } else if (track->type == GES_TRACK_TYPE_VIDEO && video_source) {
+ ges_track_element_copy_properties (video_source, tmp->data);
+ ges_track_element_copy_bindings (GES_TRACK_ELEMENT (video_source),
+ tmp->data, GST_CLOCK_TIME_NONE);
+ }
+ }
+ }
+
+ g_clear_object (&audio_source);
+ g_clear_object (&video_source);
+ gst_object_unref (clip);
+ gst_object_unref (layer);
+ }
+
+ if (res) {
+ g_free (uriclip->priv->uri);
+ uriclip->priv->uri = g_strdup (ges_asset_get_id (asset));
+ }
+
+ return res;
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_URI_CLIP_ASSET;
+ iface->check_id = (GESExtractableCheckId) extractable_check_id;
+ iface->get_parameters_from_id = extractable_get_parameters_from_id;
+ iface->get_id = extractable_get_id;
+ iface->get_id = extractable_get_id;
+ iface->can_update_asset = TRUE;
+ iface->set_asset_full = extractable_set_asset;
+}
+
+static void
+ges_uri_clip_init (GESUriClip * self)
+{
+ self->priv = ges_uri_clip_get_instance_private (self);
+
+ /* Setting the duration to -1 by default. */
+ GES_TIMELINE_ELEMENT (self)->duration = GST_CLOCK_TIME_NONE;
+}
+
+/**
+ * ges_uri_clip_set_mute:
+ * @self: the #GESUriClip on which to mute or unmute the audio track
+ * @mute: %TRUE to mute @self audio track, %FALSE to unmute it
+ *
+ * Sets whether the audio track of this clip is muted or not.
+ *
+ */
+void
+ges_uri_clip_set_mute (GESUriClip * self, gboolean mute)
+{
+ GList *tmp;
+
+ GST_DEBUG ("self:%p, mute:%d", self, mute);
+
+ self->priv->mute = mute;
+
+ /* Go over tracked objects, and update 'active' status on all audio objects */
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = g_list_next (tmp)) {
+ GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
+
+ if (ges_track_element_get_track (trackelement)->type ==
+ GES_TRACK_TYPE_AUDIO)
+ ges_track_element_set_active (trackelement, !mute);
+ }
+}
+
+gboolean
+uri_clip_set_max_duration (GESTimelineElement * element,
+ GstClockTime maxduration)
+{
+ if (_DURATION (element) == GST_CLOCK_TIME_NONE || _DURATION (element) == 0)
+ /* If we don't have a valid duration, use the max duration */
+ _set_duration0 (element, maxduration - _INPOINT (element));
+
+ return
+ GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_max_duration (element,
+ maxduration);
+}
+
+/**
+ * ges_uri_clip_set_is_image:
+ * @self: the #GESUriClip
+ * @is_image: %TRUE if @self is a still image, %FALSE otherwise
+ *
+ * Sets whether the clip is a still image or not.
+ */
+void
+ges_uri_clip_set_is_image (GESUriClip * self, gboolean is_image)
+{
+ self->priv->is_image = is_image;
+}
+
+/**
+ * ges_uri_clip_is_muted:
+ * @self: the #GESUriClip
+ *
+ * Lets you know if the audio track of @self is muted or not.
+ *
+ * Returns: %TRUE if the audio track of @self is muted, %FALSE otherwise.
+ */
+gboolean
+ges_uri_clip_is_muted (GESUriClip * self)
+{
+ return self->priv->mute;
+}
+
+/**
+ * ges_uri_clip_is_image:
+ * @self: the #GESUriClip
+ *
+ * Lets you know if @self is an image or not.
+ *
+ * Returns: %TRUE if @self is a still image %FALSE otherwise.
+ */
+gboolean
+ges_uri_clip_is_image (GESUriClip * self)
+{
+ return self->priv->is_image;
+}
+
+/**
+ * ges_uri_clip_get_uri:
+ * @self: the #GESUriClip
+ *
+ * Get the location of the resource.
+ *
+ * Returns: The location of the resource.
+ */
+const gchar *
+ges_uri_clip_get_uri (GESUriClip * self)
+{
+ return self->priv->uri;
+}
+
+static GList *
+ges_uri_clip_create_track_elements (GESClip * clip, GESTrackType type)
+{
+ GList *res = NULL;
+ const GList *tmp, *stream_assets;
+
+ g_return_val_if_fail (GES_TIMELINE_ELEMENT (clip)->asset, NULL);
+
+ stream_assets =
+ ges_uri_clip_asset_get_stream_assets (GES_URI_CLIP_ASSET
+ (GES_TIMELINE_ELEMENT (clip)->asset));
+ for (tmp = stream_assets; tmp; tmp = tmp->next) {
+ GESTrackElementAsset *asset = GES_TRACK_ELEMENT_ASSET (tmp->data);
+
+ if (ges_track_element_asset_get_track_type (asset) == type)
+ res = g_list_prepend (res, ges_asset_extract (GES_ASSET (asset), NULL));
+ }
+
+ return res;
+}
+
+static GESTrackElement *
+ges_uri_clip_create_track_element (GESClip * clip, GESTrackType type)
+{
+ GESUriClipPrivate *priv = GES_URI_CLIP (clip)->priv;
+ GESTrackElement *res = NULL;
+
+ if (g_str_has_prefix (priv->uri, GES_MULTI_FILE_URI_PREFIX)) {
+ GST_DEBUG ("Creating a GESMultiFileSource for %s", priv->uri);
+ res = (GESTrackElement *) ges_multi_file_source_new (priv->uri);
+ } else if (priv->is_image) {
+ if (type != GES_TRACK_TYPE_VIDEO) {
+ GST_DEBUG ("Object is still image, not adding any audio source");
+ return NULL;
+ } else {
+ GST_DEBUG ("Creating a GESImageSource");
+ res = (GESTrackElement *) ges_image_source_new (priv->uri);
+ }
+
+ } else {
+ GST_DEBUG ("Creating a GESUriSource");
+
+ /* FIXME : Implement properly ! */
+ if (type == GES_TRACK_TYPE_VIDEO)
+ res = (GESTrackElement *) ges_video_uri_source_new (priv->uri);
+ else if (type == GES_TRACK_TYPE_AUDIO)
+ res = (GESTrackElement *) ges_audio_uri_source_new (priv->uri);
+
+ /* If mute and track is audio, deactivate the track element */
+ if (type == GES_TRACK_TYPE_AUDIO && priv->mute)
+ ges_track_element_set_active (res, FALSE);
+ }
+
+ if (res)
+ ges_track_element_set_track_type (res, type);
+
+ return res;
+}
+
+/**
+ * ges_uri_clip_new:
+ * @uri: the URI the source should control
+ *
+ * Creates a new #GESUriClip for the provided @uri.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESUriClip, or
+ * %NULL if there was an error.
+ */
+GESUriClip *
+ges_uri_clip_new (const gchar * uri)
+{
+ GESAsset *asset = GES_ASSET (ges_uri_clip_asset_request_sync (uri, NULL));
+ GESUriClip *res = NULL;
+
+ if (asset) {
+ res = GES_URI_CLIP (ges_asset_extract (asset, NULL));
+ gst_object_unref (asset);
+ } else
+ GST_ERROR ("Could not create asset for uri: %s", uri);
+
+ return res;
+}
+
+void
+ges_uri_clip_set_uri (GESUriClip * self, gchar * uri)
+{
+ if (GES_CONTAINER_CHILDREN (self)) {
+ /* FIXME handle this case properly */
+ GST_WARNING_OBJECT (self, "Can not change uri when already"
+ "containing TrackElements");
+
+ return;
+ }
+
+ self->priv->uri = uri;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_URI_CLIP
+#define _GES_URI_CLIP
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-source-clip.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_URI_CLIP ges_uri_clip_get_type()
+
+#define GES_URI_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_URI_CLIP, GESUriClip))
+
+#define GES_URI_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_URI_CLIP, GESUriClipClass))
+
+#define GES_IS_URI_CLIP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_URI_CLIP))
+
+#define GES_IS_URI_CLIP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_URI_CLIP))
+
+#define GES_URI_CLIP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_URI_CLIP, GESUriClipClass))
+
+typedef struct _GESUriClipPrivate GESUriClipPrivate;
+
+struct _GESUriClip {
+ GESSourceClip parent;
+
+ /*< private >*/
+ GESUriClipPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESUriClipClass:
+ */
+
+struct _GESUriClipClass {
+ /*< private >*/
+ GESSourceClipClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_uri_clip_get_type (void);
+
+GES_API void
+ges_uri_clip_set_mute (GESUriClip * self, gboolean mute);
+
+GES_API void
+ges_uri_clip_set_is_image (GESUriClip * self,
+ gboolean is_image);
+
+GES_API
+gboolean ges_uri_clip_is_muted (GESUriClip * self);
+GES_API
+gboolean ges_uri_clip_is_image (GESUriClip * self);
+GES_API
+const gchar *ges_uri_clip_get_uri (GESUriClip * self);
+
+GES_API
+GESUriClip* ges_uri_clip_new (const gchar *uri);
+
+G_END_DECLS
+
+#endif /* _GES_URI_CLIP */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:ges-utils
+ * @title: GES utilities
+ * @short_description: Convenience methods
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "ges-internal.h"
+#include "ges-timeline.h"
+#include "ges-track.h"
+#include "ges-layer.h"
+#include "ges.h"
+
+static GstElementFactory *compositor_factory = NULL;
+
+/**
+ * ges_timeline_new_audio_video:
+ *
+ * Creates a new #GESTimeline containing a raw audio and a
+ * raw video track.
+ *
+ * Returns: (transfer floating): The newly created #GESTimeline.
+ */
+
+GESTimeline *
+ges_timeline_new_audio_video (void)
+{
+ GESTrack *tracka, *trackv;
+ GESTimeline *timeline;
+
+ /* This is our main GESTimeline */
+ timeline = ges_timeline_new ();
+
+ tracka = GES_TRACK (ges_audio_track_new ());
+ trackv = GES_TRACK (ges_video_track_new ());
+
+ if (!ges_timeline_add_track (timeline, trackv) ||
+ !ges_timeline_add_track (timeline, tracka)) {
+ gst_object_unref (timeline);
+ timeline = NULL;
+ }
+
+ return timeline;
+}
+
+/* Internal utilities */
+gint
+element_start_compare (GESTimelineElement * a, GESTimelineElement * b)
+{
+ if (a->start == b->start) {
+ if (a->priority < b->priority)
+ return -1;
+ if (a->priority > b->priority)
+ return 1;
+ if (a->duration < b->duration)
+ return -1;
+ if (a->duration > b->duration)
+ return 1;
+ return 0;
+ } else if (a->start < b->start)
+ return -1;
+
+ return 1;
+}
+
+gint
+element_end_compare (GESTimelineElement * a, GESTimelineElement * b)
+{
+ if (GES_TIMELINE_ELEMENT_END (a) == GES_TIMELINE_ELEMENT_END (b)) {
+ if (a->priority < b->priority)
+ return -1;
+ if (a->priority > b->priority)
+ return 1;
+ if (a->duration < b->duration)
+ return -1;
+ if (a->duration > b->duration)
+ return 1;
+ return 0;
+ } else if (GES_TIMELINE_ELEMENT_END (a) < (GES_TIMELINE_ELEMENT_END (b)))
+ return -1;
+
+ return 1;
+}
+
+gboolean
+ges_pspec_equal (gconstpointer key_spec_1, gconstpointer key_spec_2)
+{
+ const GParamSpec *key1 = key_spec_1;
+ const GParamSpec *key2 = key_spec_2;
+
+ return (key1->owner_type == key2->owner_type &&
+ strcmp (key1->name, key2->name) == 0);
+}
+
+guint
+ges_pspec_hash (gconstpointer key_spec)
+{
+ const GParamSpec *key = key_spec;
+ const gchar *p;
+ guint h = key->owner_type;
+
+ for (p = key->name; *p; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+static gboolean
+find_compositor (GstPluginFeatureFilter * feature, gpointer udata)
+{
+ const gchar *klass;
+
+ if (G_UNLIKELY (!GST_IS_ELEMENT_FACTORY (feature)))
+ return FALSE;
+
+ klass = gst_element_factory_get_metadata (GST_ELEMENT_FACTORY_CAST (feature),
+ GST_ELEMENT_METADATA_KLASS);
+
+ return (strstr (klass, "Compositor") != NULL);
+}
+
+
+
+GstElementFactory *
+ges_get_compositor_factory (void)
+{
+ GList *result;
+
+ if (compositor_factory)
+ return compositor_factory;
+
+ result = gst_registry_feature_filter (gst_registry_get (),
+ (GstPluginFeatureFilter) find_compositor, FALSE, NULL);
+
+ /* sort on rank and name */
+ result = g_list_sort (result, gst_plugin_feature_rank_compare_func);
+ g_assert (result);
+
+ compositor_factory = result->data;
+ gst_plugin_feature_list_free (result);
+
+ return compositor_factory;
+}
+
+gboolean
+ges_nle_composition_add_object (GstElement * comp, GstElement * object)
+{
+ return gst_bin_add (GST_BIN (comp), object);
+}
+
+gboolean
+ges_nle_composition_remove_object (GstElement * comp, GstElement * object)
+{
+ return gst_bin_remove (GST_BIN (comp), object);
+}
+
+gboolean
+ges_nle_object_commit (GstElement * nlesource, gboolean recurse)
+{
+ gboolean ret;
+
+ g_signal_emit_by_name (nlesource, "commit", recurse, &ret);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_UTILS
+#define _GES_UTILS
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+
+G_BEGIN_DECLS
+
+GES_API
+GESTimeline * ges_timeline_new_audio_video (void);
+GES_API
+gboolean ges_pspec_equal (gconstpointer key_spec_1, gconstpointer key_spec_2);
+GES_API
+guint ges_pspec_hash (gconstpointer key_spec);
+
+
+G_END_DECLS
+
+#endif /* _GES_UTILS */
+
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2014> 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+
+#ifdef HAVE_GST_VALIDATE
+#include <gst/validate/validate.h>
+#include <gst/validate/gst-validate-scenario.h>
+#include <gst/validate/gst-validate-utils.h>
+#include "ges-internal.h"
+#include "ges-structured-interface.h"
+
+#define MONITOR_ON_PIPELINE "validate-monitor"
+#define RUNNER_ON_PIPELINE "runner-monitor"
+
+#define DECLARE_AND_GET_TIMELINE_AND_PIPELINE(scenario, action) \
+ GESTimeline *timeline; \
+ GstElement * pipeline = gst_validate_scenario_get_pipeline (scenario); \
+ if (pipeline == NULL) { \
+ GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,\
+ "Can't execute a '%s' action after the pipeline "\
+ "has been destroyed.", action->type);\
+ return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;\
+ }\
+ g_object_get (pipeline, "timeline", &timeline, NULL);
+
+#define DECLARE_AND_GET_TIMELINE(scenario, action) \
+ DECLARE_AND_GET_TIMELINE_AND_PIPELINE(scenario, action);\
+ gst_object_unref(pipeline);
+
+static gboolean
+_serialize_project (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ const gchar *uri = gst_structure_get_string (action->structure, "uri");
+ gboolean res;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ gst_validate_printf (action, "Saving project to %s", uri);
+
+ res = ges_timeline_save_to_uri (timeline, uri, NULL, TRUE, NULL);
+
+ g_object_unref (timeline);
+ return res;
+}
+
+static gboolean
+_remove_asset (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ const gchar *id = NULL;
+ const gchar *type_string = NULL;
+ GType type;
+ GESAsset *asset;
+ gboolean res = FALSE;
+ GESProject *project;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ project = ges_timeline_get_project (timeline);
+
+ id = gst_structure_get_string (action->structure, "id");
+ type_string = gst_structure_get_string (action->structure, "type");
+
+ if (!type_string || !id) {
+ GST_ERROR ("Missing parameters, we got type %s and id %s", type_string, id);
+ goto beach;
+ }
+
+ if (!(type = g_type_from_name (type_string))) {
+ GST_ERROR ("This type doesn't exist : %s", type_string);
+ goto beach;
+ }
+
+ asset = ges_project_get_asset (project, id, type);
+
+ if (!asset) {
+ GST_ERROR ("No asset with id %s and type %s", id, type_string);
+ goto beach;
+ }
+
+ res = ges_project_remove_asset (project, asset);
+
+beach:
+ g_object_unref (timeline);
+ return res;
+}
+
+static gboolean
+_add_asset (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ const gchar *id = NULL;
+ const gchar *type_string = NULL;
+ GType type;
+ GESAsset *asset;
+ gboolean res = FALSE;
+ GESProject *project;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ project = ges_timeline_get_project (timeline);
+
+ id = gst_structure_get_string (action->structure, "id");
+ type_string = gst_structure_get_string (action->structure, "type");
+
+ gst_validate_printf (action, "Adding asset of type %s with ID %s\n",
+ id, type_string);
+
+ if (!type_string || !id) {
+ GST_ERROR ("Missing parameters, we got type %s and id %s", type_string, id);
+ goto beach;
+ }
+
+ if (!(type = g_type_from_name (type_string))) {
+ GST_ERROR ("This type doesn't exist : %s", type_string);
+ goto beach;
+ }
+
+ asset = _ges_get_asset_from_timeline (timeline, type, id, NULL);
+
+ if (!asset) {
+ res = FALSE;
+
+ goto beach;
+ }
+
+ res = ges_project_add_asset (project, asset);
+
+beach:
+ g_object_unref (timeline);
+ return res;
+}
+
+static gboolean
+_add_layer (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESLayer *layer;
+ gint priority;
+ gboolean res = TRUE, auto_transition = FALSE;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ if (!gst_structure_get_int (action->structure, "priority", &priority)) {
+ GST_ERROR ("priority is needed when adding a layer");
+ goto failed;
+ }
+
+ gst_validate_printf (action, "Adding layer with priority %d\n", priority);
+ layer = _ges_get_layer_by_priority (timeline, priority);
+
+ gst_structure_get_boolean (action->structure, "auto-transition",
+ &auto_transition);
+ g_object_set (layer, "priority", priority, "auto-transition", auto_transition,
+ NULL);
+
+
+beach:
+ g_object_unref (timeline);
+ return res;
+
+failed:
+ goto beach;
+}
+
+static gboolean
+_remove_layer (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESLayer *layer;
+ gint priority;
+ gboolean res = FALSE;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ if (!gst_structure_get_int (action->structure, "priority", &priority)) {
+ GST_ERROR ("priority is needed when removing a layer");
+ goto beach;
+ }
+
+ layer = _ges_get_layer_by_priority (timeline, priority);
+
+ if (layer) {
+ res = ges_timeline_remove_layer (timeline, layer);
+ gst_object_unref (layer);
+ } else {
+ GST_ERROR ("No layer with priority %d", priority);
+ }
+
+beach:
+ g_object_unref (timeline);
+ return res;
+}
+
+static gboolean
+_remove_clip (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESTimelineElement *clip;
+ GESLayer *layer;
+ const gchar *name;
+ gboolean res = FALSE;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ name = gst_structure_get_string (action->structure, "name");
+ clip = ges_timeline_get_element (timeline, name);
+ g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+
+ gst_validate_printf (action, "removing clip with ID %s\n", name);
+ layer = ges_clip_get_layer (GES_CLIP (clip));
+
+ if (layer) {
+ res = ges_layer_remove_clip (layer, GES_CLIP (clip));
+ gst_object_unref (layer);
+ } else {
+ GST_ERROR ("No layer for clip %s", ges_timeline_element_get_name (clip));
+ }
+
+ g_object_unref (timeline);
+ return res;
+}
+
+
+static gboolean
+_edit_container (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GList *layers = NULL;
+ GESTimelineElement *container;
+ GstClockTime position;
+ gboolean res = FALSE;
+
+ gint new_layer_priority = -1;
+ guint edge = GES_EDGE_NONE;
+ guint mode = GES_EDIT_MODE_NORMAL;
+
+ const gchar *edit_mode_str = NULL, *edge_str = NULL;
+ const gchar *clip_name;
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ clip_name = gst_structure_get_string (action->structure, "container-name");
+
+ container = ges_timeline_get_element (timeline, clip_name);
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+
+ if (!gst_validate_action_get_clocktime (scenario, action,
+ "position", &position)) {
+ GST_WARNING ("Could not get position");
+ goto beach;
+ }
+
+ if ((edit_mode_str =
+ gst_structure_get_string (action->structure, "edit-mode")))
+ g_return_val_if_fail (gst_validate_utils_enum_from_str (GES_TYPE_EDIT_MODE,
+ edit_mode_str, &mode), FALSE);
+
+ if ((edge_str = gst_structure_get_string (action->structure, "edge")))
+ g_return_val_if_fail (gst_validate_utils_enum_from_str (GES_TYPE_EDGE,
+ edge_str, &edge), FALSE);
+
+ gst_structure_get_int (action->structure, "new-layer-priority",
+ &new_layer_priority);
+
+ gst_validate_printf (action, "Editing %s to %" GST_TIME_FORMAT
+ " in %s mode, edge: %s "
+ "with new layer prio: %d \n\n",
+ clip_name, GST_TIME_ARGS (position),
+ edit_mode_str ? edit_mode_str : "normal",
+ edge_str ? edge_str : "None", new_layer_priority);
+
+ if (!(res = ges_container_edit (GES_CONTAINER (container), layers,
+ new_layer_priority, mode, edge, position))) {
+ gst_object_unref (container);
+ GST_ERROR ("HERE");
+ goto beach;
+ }
+ gst_object_unref (container);
+
+beach:
+ g_object_unref (timeline);
+ return res;
+}
+
+
+static void
+_commit_done_cb (GstBus * bus, GstMessage * message, GstValidateAction * action)
+{
+ gst_validate_action_set_done (action);
+
+ g_signal_handlers_disconnect_by_func (bus, _commit_done_cb, action);
+}
+
+static gboolean
+_commit (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GstBus *bus;
+ GstState state;
+
+ DECLARE_AND_GET_TIMELINE_AND_PIPELINE (scenario, action);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+ gst_validate_printf (action, "Commiting timeline %s\n",
+ GST_OBJECT_NAME (timeline));
+
+ g_signal_connect (bus, "message::async-done", G_CALLBACK (_commit_done_cb),
+ action);
+
+ gst_element_get_state (pipeline, &state, NULL, 0);
+ if (!ges_timeline_commit (timeline) || state < GST_STATE_PAUSED) {
+ g_signal_handlers_disconnect_by_func (bus, G_CALLBACK (_commit_done_cb),
+ action);
+ gst_object_unref (timeline);
+ gst_object_unref (bus);
+
+ return TRUE;
+ }
+ gst_object_unref (bus);
+ gst_object_unref (timeline);
+
+ return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
+}
+
+static gboolean
+_split_clip (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ const gchar *clip_name;
+ GESTimelineElement *element;
+ GstClockTime position;
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ clip_name = gst_structure_get_string (action->structure, "clip-name");
+
+ element = ges_timeline_get_element (timeline, clip_name);
+ g_return_val_if_fail (GES_IS_CLIP (element), FALSE);
+ g_object_unref (timeline);
+
+ g_return_val_if_fail (gst_validate_action_get_clocktime (scenario, action,
+ "position", &position), FALSE);
+
+ return (ges_clip_split (GES_CLIP (element), position) != NULL);
+}
+
+static gboolean
+_set_track_restriction_caps (GstValidateScenario * scenario,
+ GstValidateAction * action)
+{
+ GList *tmp;
+ GstCaps *caps;
+ gboolean res = FALSE;
+ GESTrackType track_types;
+
+ const gchar *track_type_str =
+ gst_structure_get_string (action->structure, "track-type");
+ const gchar *caps_str = gst_structure_get_string (action->structure, "caps");
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ g_return_val_if_fail ((track_types =
+ gst_validate_utils_flags_from_str (GES_TYPE_TRACK_TYPE,
+ track_type_str)), FALSE);
+
+ g_return_val_if_fail ((caps =
+ gst_caps_from_string (caps_str)) != NULL, FALSE);
+
+ for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
+ GESTrack *track = tmp->data;
+
+ if (track->type & track_types) {
+ gchar *str;
+
+ str = gst_caps_to_string (caps);
+ gst_validate_printf (action, "Setting restriction caps %s on track: %s\n",
+ str, GST_ELEMENT_NAME (track));
+ g_free (str);
+
+ ges_track_set_restriction_caps (track, caps);
+
+ res = TRUE;
+ }
+ }
+ gst_caps_unref (caps);
+ gst_object_unref (timeline);
+
+ return res;
+}
+
+static gboolean
+_set_asset_on_element (GstValidateScenario * scenario,
+ GstValidateAction * action)
+{
+ GESAsset *asset;
+ GESTimelineElement *element;
+ const gchar *element_name, *id;
+
+ gboolean res = TRUE;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ element_name = gst_structure_get_string (action->structure, "element-name");
+ element = ges_timeline_get_element (timeline, element_name);
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (element), FALSE);
+
+ id = gst_structure_get_string (action->structure, "asset-id");
+
+ gst_validate_printf (action, "Setting asset %s on element %s\n",
+ id, element_name);
+
+ asset = _ges_get_asset_from_timeline (timeline, G_OBJECT_TYPE (element), id,
+ NULL);
+ if (asset == NULL) {
+ res = FALSE;
+ GST_ERROR ("Could not find asset: %s", id);
+ goto beach;
+ }
+
+ res = ges_extractable_set_asset (GES_EXTRACTABLE (element), asset);
+
+beach:
+ gst_object_unref (timeline);
+
+ return res;
+}
+
+static gboolean
+_container_remove_child (GstValidateScenario * scenario,
+ GstValidateAction * action)
+{
+ GESContainer *container;
+ GESTimelineElement *child;
+ const gchar *container_name, *child_name;
+
+ gboolean res = TRUE;
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ container_name =
+ gst_structure_get_string (action->structure, "container-name");
+ container =
+ GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+
+ child_name = gst_structure_get_string (action->structure, "child-name");
+ child = ges_timeline_get_element (timeline, child_name);
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
+
+ gst_validate_printf (action, "Remove child %s from container %s\n",
+ child_name, GES_TIMELINE_ELEMENT_NAME (container));
+
+ res = ges_container_remove (container, child);
+
+ gst_object_unref (timeline);
+
+ return res;
+}
+
+static gboolean
+_ungroup (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESContainer *container;
+ gboolean recursive = FALSE;
+ const gchar *container_name;
+
+ gboolean res = TRUE;
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ container_name =
+ gst_structure_get_string (action->structure, "container-name");
+ container =
+ GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
+ g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
+
+ gst_validate_printf (action, "Ungrouping children from container %s\n",
+ GES_TIMELINE_ELEMENT_NAME (container));
+
+ gst_structure_get_boolean (action->structure, "recursive", &recursive);
+
+ g_list_free (ges_container_ungroup (container, recursive));
+
+ gst_object_unref (timeline);
+
+ return res;
+}
+
+static GstValidateExecuteActionReturn
+_copy_element (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESTimelineElement *element, *copied, *pasted;
+ gboolean recursive = FALSE;
+ const gchar *element_name, *paste_name;
+ GstClockTime position;
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+
+ element_name = gst_structure_get_string (action->structure, "element-name");
+ element = ges_timeline_get_element (timeline, element_name);
+
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (element),
+ GST_VALIDATE_EXECUTE_ACTION_ERROR);
+
+ gst_validate_printf (action, "Copying element %s\n",
+ GES_TIMELINE_ELEMENT_NAME (element));
+
+ if (!gst_structure_get_boolean (action->structure, "recursive", &recursive))
+ recursive = TRUE;
+
+ g_return_val_if_fail (gst_validate_action_get_clocktime (scenario, action,
+ "position", &position), FALSE);
+
+ copied = ges_timeline_element_copy (element, recursive);
+ pasted = ges_timeline_element_paste (copied, position);
+ gst_object_unref (timeline);
+
+ if (!pasted) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Could not paste clip %s", element_name);
+ return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+ }
+
+ paste_name = gst_structure_get_string (action->structure, "paste-name");
+ if (paste_name) {
+ if (!ges_timeline_element_set_name (pasted, paste_name)) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Could not set element name %s", paste_name);
+
+ return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+ }
+ }
+
+ return GST_VALIDATE_EXECUTE_ACTION_OK;
+}
+
+static gboolean
+_set_control_source (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESTrackElement *element;
+
+ gboolean ret = FALSE;
+ GstControlSource *source = NULL;
+ gchar *element_name, *property_name, *binding_type = NULL,
+ *source_type = NULL, *interpolation_mode = NULL;
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ g_return_val_if_fail (gst_structure_get (action->structure,
+ "element-name", G_TYPE_STRING, &element_name,
+ "property-name", G_TYPE_STRING, &property_name,
+ "binding-type", G_TYPE_STRING, &binding_type,
+ "source-type", G_TYPE_STRING, &source_type,
+ "interpolation-mode", G_TYPE_STRING, &interpolation_mode, NULL),
+ FALSE);
+
+ element =
+ GES_TRACK_ELEMENT (ges_timeline_get_element (timeline, element_name));
+
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (element), FALSE);
+
+ if (!binding_type)
+ binding_type = g_strdup ("direct");
+
+ if (source_type == NULL || !g_strcmp0 (source_type, "interpolation")) {
+ guint mode;
+
+ source = gst_interpolation_control_source_new ();
+
+ if (interpolation_mode)
+ g_return_val_if_fail (gst_validate_utils_enum_from_str
+ (GST_TYPE_INTERPOLATION_MODE, interpolation_mode, &mode), FALSE);
+ else
+ mode = GST_INTERPOLATION_MODE_LINEAR;
+
+ g_object_set (source, "mode", mode, NULL);
+
+ } else {
+ GST_ERROR_OBJECT (scenario, "Interpolation type %s not supported",
+ source_type);
+
+ goto done;
+ }
+
+ gst_validate_printf (action, "Setting control source on %s:%s\n",
+ element_name, property_name);
+ ret = ges_track_element_set_control_source (element,
+ source, property_name, binding_type);
+
+
+done:
+ gst_object_unref (timeline);
+ g_free (element_name);
+ g_free (binding_type);
+ g_free (source_type);
+ g_free (interpolation_mode);
+
+ return ret;
+}
+
+static gboolean
+_validate_action_execute (GstValidateScenario * scenario,
+ GstValidateAction * action)
+{
+ GError *err = NULL;
+ ActionFromStructureFunc func;
+
+ DECLARE_AND_GET_TIMELINE (scenario, action);
+
+ gst_structure_remove_field (action->structure, "playback-time");
+ if (gst_structure_has_name (action->structure, "add-keyframe") ||
+ gst_structure_has_name (action->structure, "remove-keyframe")) {
+ func = _ges_add_remove_keyframe_from_struct;
+ } else if (gst_structure_has_name (action->structure, "add-clip")) {
+ func = _ges_add_clip_from_struct;
+ } else if (gst_structure_has_name (action->structure, "container-add-child")) {
+ func = _ges_container_add_child_from_struct;
+ } else if (gst_structure_has_name (action->structure, "set-child-property")) {
+ func = _ges_set_child_property_from_struct;
+ } else {
+ g_assert_not_reached ();
+ }
+
+ if (!func (timeline, action->structure, &err)) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Could not execute %s (error: %s)",
+ gst_structure_get_name (action->structure),
+ err ? err->message : "None");
+
+ g_clear_error (&err);
+
+ return TRUE;
+ }
+
+ gst_object_unref (timeline);
+
+ return TRUE;
+}
+
+static void
+_project_loaded_cb (GESProject * project, GESTimeline * timeline,
+ GstValidateAction * action)
+{
+ gst_validate_action_set_done (action);
+}
+
+static gboolean
+_load_project (GstValidateScenario * scenario, GstValidateAction * action)
+{
+ GESProject *project;
+ GList *tmp, *tmp_full;
+
+ gchar *uri = NULL;
+ GError *error = NULL;
+ const gchar *content = NULL;
+
+ gchar *tmpfile = g_strdup_printf ("%s%s%s", g_get_tmp_dir (),
+ G_DIR_SEPARATOR_S, "tmpxgesload.xges");
+
+ GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
+ DECLARE_AND_GET_TIMELINE_AND_PIPELINE (scenario, action);
+
+ gst_validate_printf (action, "Loading project from serialized content\n");
+
+ if (!GES_IS_PIPELINE (pipeline)) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Not a GES pipeline, can't work with it");
+
+ goto fail;
+ }
+
+ content = gst_structure_get_string (action->structure, "serialized-content");
+
+ g_file_set_contents (tmpfile, content, -1, &error);
+ if (error) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Could not set XML content: %s", error->message);
+
+ goto fail;
+ }
+
+ uri = gst_filename_to_uri (tmpfile, &error);
+ if (error) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Could not set filename to URI: %s", error->message);
+
+ goto fail;
+ }
+
+ tmp_full = ges_timeline_get_layers (timeline);
+ for (tmp = tmp_full; tmp; tmp = tmp->next)
+ ges_timeline_remove_layer (timeline, tmp->data);
+ g_list_free_full (tmp_full, gst_object_unref);
+
+ tmp_full = ges_timeline_get_tracks (timeline);
+ for (tmp = tmp_full; tmp; tmp = tmp->next)
+ ges_timeline_remove_track (timeline, tmp->data);
+ g_list_free_full (tmp_full, gst_object_unref);
+
+ project = ges_project_new (uri);
+ g_signal_connect (project, "loaded", G_CALLBACK (_project_loaded_cb), action);
+ ges_project_load (project, timeline, &error);
+ if (error) {
+ GST_VALIDATE_REPORT (scenario,
+ g_quark_from_string ("scenario::execution-error"),
+ "Could not load timeline: %s", error->message);
+
+ goto fail;
+ }
+
+done:
+ if (error)
+ g_error_free (error);
+
+ if (uri)
+ g_free (uri);
+
+ g_free (tmpfile);
+
+ return res;
+
+fail:
+
+ /* We reported the issue ourself, so do not ask GstValidate
+ * to do that for us */
+ res = GST_VALIDATE_EXECUTE_ACTION_OK;
+
+ goto done;
+
+}
+
+#endif
+
+gboolean
+ges_validate_register_action_types (void)
+{
+#ifdef HAVE_GST_VALIDATE
+ gst_validate_init ();
+
+ /* *INDENT-OFF* */
+ gst_validate_register_action_type ("edit-container", "ges", _edit_container,
+ (GstValidateActionParameter []) {
+ {
+ .name = "container-name",
+ .description = "The name of the GESContainer to edit",
+ .mandatory = TRUE,
+ .types = "string",
+ },
+ {
+ .name = "position",
+ .description = "The new position of the GESContainer",
+ .mandatory = TRUE,
+ .types = "double or string",
+ .possible_variables = "position: The current position in the stream\n"
+ "duration: The duration of the stream",
+ NULL
+ },
+ {
+ .name = "edit-mode",
+ .description = "The GESEditMode to use to edit @container-name",
+ .mandatory = FALSE,
+ .types = "string",
+ .def = "normal",
+ },
+ {
+ .name = "edge",
+ .description = "The GESEdge to use to edit @container-name\n"
+ "should be in [ edge_start, edge_end, edge_none ] ",
+ .mandatory = FALSE,
+ .types = "string",
+ .def = "edge_none",
+ },
+ {
+ .name = "new-layer-priority",
+ .description = "The priority of the layer @container should land in.\n"
+ "If the layer you're trying to move the container to doesn't exist, it will\n"
+ "be created automatically. -1 means no move.",
+ .mandatory = FALSE,
+ .types = "int",
+ .def = "-1",
+ },
+ {NULL}
+ },
+ "Allows to edit a container (like a GESClip), for more details, have a look at:\n"
+ "ges_container_edit documentation, Note that the timeline will\n"
+ "be commited, and flushed so that the edition is taken into account",
+ GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("add-asset", "ges", _add_asset,
+ (GstValidateActionParameter []) {
+ {
+ .name = "id",
+ .description = "Adds an asset to a project.",
+ .mandatory = TRUE,
+ NULL
+ },
+ {
+ .name = "type",
+ .description = "The type of asset to add",
+ .mandatory = TRUE,
+ NULL
+ },
+ {NULL}
+ },
+ "Allows to add an asset to the current project", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("remove-asset", "ges", _remove_asset,
+ (GstValidateActionParameter []) {
+ {
+ .name = "id",
+ .description = "The ID of the clip to remove",
+ .mandatory = TRUE,
+ NULL
+ },
+ {
+ .name = "type",
+ .description = "The type of asset to remove",
+ .mandatory = TRUE,
+ NULL
+ },
+ { NULL }
+ },
+ "Allows to remove an asset from the current project", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("add-layer", "ges", _add_layer,
+ (GstValidateActionParameter []) {
+ {
+ .name = "priority",
+ .description = "The priority of the new layer to add,"
+ "if not specified, the new layer will be"
+ " appended to the timeline",
+ .mandatory = FALSE,
+ NULL
+ },
+ { NULL }
+ },
+ "Allows to add a layer to the current timeline", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("remove-layer", "ges", _remove_layer,
+ (GstValidateActionParameter []) {
+ {
+ .name = "priority",
+ .description = "The priority of the layer to remove",
+ .mandatory = TRUE,
+ NULL
+ },
+ {
+ .name = "auto-transition",
+ .description = "Wheter auto-transition is activated on the new layer.",
+ .mandatory = FALSE,
+ .types="boolean",
+ .def = "False"
+ },
+ { NULL }
+ },
+ "Allows to remove a layer from the current timeline", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("add-clip", "ges", _validate_action_execute,
+ (GstValidateActionParameter []) {
+ {
+ .name = "name",
+ .description = "The name of the clip to add",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "layer-priority",
+ .description = "The priority of the clip to add",
+ .types = "int",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "asset-id",
+ .description = "The id of the asset from which to extract the clip",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "type",
+ .description = "The type of the clip to create",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "start",
+ .description = "The start value to set on the new GESClip.",
+ .types = "double or string",
+ .mandatory = FALSE,
+ },
+ {
+ .name = "inpoint",
+ .description = "The inpoint value to set on the new GESClip",
+ .types = "double or string",
+ .mandatory = FALSE,
+ },
+ {
+ .name = "duration",
+ .description = "The duration value to set on the new GESClip",
+ .types = "double or string",
+ .mandatory = FALSE,
+ },
+ {NULL}
+ }, "Allows to add a clip to a given layer", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("remove-clip", "ges", _remove_clip,
+ (GstValidateActionParameter []) {
+ {
+ .name = "name",
+ .description = "The name of the clip to remove",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Allows to remove a clip from a given layer", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("serialize-project", "ges", _serialize_project,
+ (GstValidateActionParameter []) {
+ {
+ .name = "uri",
+ .description = "The uri where to store the serialized project",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "serializes a project", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("set-child-property", "ges", _validate_action_execute,
+ (GstValidateActionParameter []) {
+ {
+ .name = "element-name",
+ .description = "The name of the element on which to modify the property",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "property",
+ .description = "The name of the property to modify",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "value",
+ .description = "The value of the property",
+ .types = "gvalue",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Allows to change child property of an object", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("split-clip", "ges", _split_clip,
+ (GstValidateActionParameter []) {
+ {
+ .name = "clip-name",
+ .description = "The name of the clip to split",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "position",
+ .description = "The position at which to split the clip",
+ .types = "double or string",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Split a clip at a specified position.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("set-track-restriction-caps", "ges", _set_track_restriction_caps,
+ (GstValidateActionParameter []) {
+ {
+ .name = "track-type",
+ .description = "The type of track to set restriction caps on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "caps",
+ .description = "The caps to set on the track",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Sets restriction caps on tracks of a specific type.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("element-set-asset", "ges", _set_asset_on_element,
+ (GstValidateActionParameter []) {
+ {
+ .name = "element-name",
+ .description = "The name of the TimelineElement to set an asset on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "asset-id",
+ .description = "The id of the asset from which to extract the clip",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Sets restriction caps on tracks of a specific type.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+
+ gst_validate_register_action_type ("container-add-child", "ges", _validate_action_execute,
+ (GstValidateActionParameter []) {
+ {
+ .name = "container-name",
+ .description = "The name of the GESContainer to add a child to",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "child-name",
+ .description = "The name of the child to add to @container-name",
+ .types = "string",
+ .mandatory = FALSE,
+ .def = "NULL"
+ },
+ {
+ .name = "asset-id",
+ .description = "The id of the asset from which to extract the child",
+ .types = "string",
+ .mandatory = TRUE,
+ .def = "NULL"
+ },
+ {
+ .name = "child-type",
+ .description = "The type of the child to create",
+ .types = "string",
+ .mandatory = FALSE,
+ .def = "NULL"
+ },
+ {NULL}
+ }, "Add a child to @container-name. If asset-id and child-type are specified,"
+ " the child will be created and added. Otherwize @child-name has to be specified"
+ " and will be added to the container.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("container-remove-child", "ges", _container_remove_child,
+ (GstValidateActionParameter []) {
+ {
+ .name = "container-name",
+ .description = "The name of the GESContainer to remove a child from",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "child-name",
+ .description = "The name of the child to reomve from @container-name",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Remove a child from @container-name.", FALSE);
+
+ gst_validate_register_action_type ("ungroup-container", "ges", _ungroup,
+ (GstValidateActionParameter []) {
+ {
+ .name = "container-name",
+ .description = "The name of the GESContainer to ungroup children from",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "recursive",
+ .description = "Wether to recurse ungrouping or not.",
+ .types = "boolean",
+ .mandatory = FALSE,
+ },
+ {NULL}
+ }, "Ungroup children of @container-name.", FALSE);
+
+ gst_validate_register_action_type ("set-control-source", "ges", _set_control_source,
+ (GstValidateActionParameter []) {
+ {
+ .name = "element-name",
+ .description = "The name of the GESTrackElement to set the control source on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "property-name",
+ .description = "The name of the property for which to set a control source",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "binding-type",
+ .description = "The name of the type of binding to use",
+ .types = "string",
+ .mandatory = FALSE,
+ .def = "direct",
+ },
+ {
+ .name = "source-type",
+ .description = "The name of the type of ControlSource to use",
+ .types = "string",
+ .mandatory = FALSE,
+ .def = "interpolation",
+ },
+ {
+ .name = "interpolation-mode",
+ .description = "The name of the GstInterpolationMode to on the source",
+ .types = "string",
+ .mandatory = FALSE,
+ .def = "linear",
+ },
+ {NULL}
+ }, "Adds a GstControlSource on @element-name::@property-name"
+ " allowing you to then add keyframes on that property.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("add-keyframe", "ges", _validate_action_execute,
+ (GstValidateActionParameter []) {
+ {
+ .name = "element-name",
+ .description = "The name of the GESTrackElement to add a keyframe on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "property-name",
+ .description = "The name of the property for which to add a keyframe on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "timestamp",
+ .description = "The timestamp of the keyframe",
+ .types = "string or float",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "value",
+ .description = "The value of the keyframe",
+ .types = "float",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Remove a child from @container-name.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("copy-element", "ges", _copy_element,
+ (GstValidateActionParameter []) {
+ {
+ .name = "element-name",
+ .description = "The name of the GESTtimelineElement to copy",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "recurse",
+ .description = "Copy recursively or not",
+ .types = "boolean",
+ .def = "true",
+ .mandatory = FALSE,
+ },
+ {
+ .name = "position",
+ .description = "The time where to paste the element",
+ .types = "string or float",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "paste-name",
+ .description = "The name of the copied element",
+ .types = "string",
+ .mandatory = FALSE,
+ },
+ {NULL}
+ }, "Remove a child from @container-name.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("remove-keyframe", "ges", _validate_action_execute,
+ (GstValidateActionParameter []) {
+ {
+ .name = "element-name",
+ .description = "The name of the GESTrackElement to add a keyframe on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "property-name",
+ .description = "The name of the property for which to add a keyframe on",
+ .types = "string",
+ .mandatory = TRUE,
+ },
+ {
+ .name = "timestamp",
+ .description = "The timestamp of the keyframe",
+ .types = "string or float",
+ .mandatory = TRUE,
+ },
+ {NULL}
+ }, "Remove a child from @container-name.", GST_VALIDATE_ACTION_TYPE_NONE);
+
+ gst_validate_register_action_type ("load-project", "ges", _load_project,
+ (GstValidateActionParameter []) {
+ {
+ .name = "serialized-content",
+ .description = "The full content of the XML describing project in XGES formet.",
+ .mandatory = TRUE,
+ NULL
+ },
+ {NULL}
+ },
+ "Loads a project either from its content passed in the serialized-content field.\n"
+ "Note that it will completely clean the previous timeline",
+ GST_VALIDATE_ACTION_TYPE_NONE);
+
+
+ gst_validate_register_action_type ("commit", "ges", _commit, NULL,
+ "Commit the timeline.", GST_VALIDATE_ACTION_TYPE_ASYNC);
+ /* *INDENT-ON* */
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GES_VERSION_H__
+#define __GES_VERSION_H__
+
+G_BEGIN_DECLS
+
+#define GES_VERSION_MAJOR (@GES_VERSION_MAJOR@)
+#define GES_VERSION_MINOR (@GES_VERSION_MINOR@)
+#define GES_VERSION_MICRO (@GES_VERSION_MICRO@)
+#define GES_VERSION_NANO (@GES_VERSION_NANO@)
+
+G_END_DECLS
+
+#endif /* __GES_H__ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesvideosource
+ * @title: GESVideoSource
+ * @short_description: Base Class for video sources
+ *
+ * # Children Properties:
+ * You can use the following children properties through the
+ * #ges_track_element_set_child_property and alike set of methods:
+ *
+ * <informaltable frame="none">
+ * <tgroup cols="3">
+ * <colspec colname="properties_type" colwidth="150px"/>
+ * <colspec colname="properties_name" colwidth="200px"/>
+ * <colspec colname="properties_flags" colwidth="400px"/>
+ * <tbody>
+ * <row>
+ * <entry role="property_type"><link linkend="gdouble"><type>double</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--alpha">alpha</link></entry>
+ * <entry>The desired alpha for the stream.</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gint"><type>gint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--posx">posx</link></entry>
+ * <entry>The desired x position for the stream.</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gint"><type>gint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--posy">posy</link></entry>
+ * <entry>The desired y position for the stream</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gint"><type>gint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--width">width</link></entry>
+ * <entry>The desired width for that source. Set to 0 if size is not mandatory, will be set to width of the current track.</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="gint"><type>gint</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--height">height</link></entry>
+ * <entry>The desired height for that source. Set to 0 if size is not mandatory, will be set to height of the current track.</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="GstDeinterlaceModes"><type>GstDeinterlaceModes</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--deinterlace-mode">deinterlace-mode</link></entry>
+ * <entry>Deinterlace Mode</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="GstDeinterlaceFields"><type>GstDeinterlaceFields</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--deinterlace-fields">deinterlace-fields</link></entry>
+ * <entry>Fields to use for deinterlacing</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="GstDeinterlaceFieldLayout"><type>GstDeinterlaceFieldLayout</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--deinterlace-tff">deinterlace-tff</link></entry>
+ * <entry>Deinterlace top field first</entry>
+ * </row>
+ * <row>
+ * <entry role="property_type"><link linkend="GstVideoOrientationMethod"><type>GstVideoOrientationMethod</type></link></entry>
+ * <entry role="property_name"><link linkend="GESVideoSource--video-direction">video-direction</link></entry>
+ * <entry>The desired video rotation and flipping.</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/pbutils/missing-plugins.h>
+#include <gst/video/video.h>
+
+#include "ges-internal.h"
+#include "ges/ges-meta-container.h"
+#include "ges-track-element.h"
+#include "ges-video-source.h"
+#include "ges-layer.h"
+#include "gstframepositioner.h"
+
+#define parent_class ges_video_source_parent_class
+
+struct _GESVideoSourcePrivate
+{
+ GstFramePositioner *positioner;
+ GstElement *capsfilter;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESVideoSource, ges_video_source,
+ GES_TYPE_SOURCE);
+
+/* TrackElement VMethods */
+
+static gboolean
+_set_priority (GESTimelineElement * element, guint32 priority)
+{
+ gboolean res;
+ GESVideoSource *self = GES_VIDEO_SOURCE (element);
+
+ res = GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_priority (element,
+ priority);
+
+ if (res && self->priv->positioner)
+ g_object_set (self->priv->positioner, "zorder", G_MAXUINT - priority, NULL);
+
+ return res;
+}
+
+static void
+post_missing_element_message (GstElement * element, const gchar * name)
+{
+ GstMessage *msg;
+
+ msg = gst_missing_element_message_new (element, name);
+ gst_element_post_message (element, msg);
+}
+
+static GstElement *
+ges_video_source_create_element (GESTrackElement * trksrc)
+{
+ GstElement *topbin;
+ GstElement *sub_element;
+ GstElement *queue = gst_element_factory_make ("queue", NULL);
+ GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_GET_CLASS (trksrc);
+ GESVideoSource *self;
+ GstElement *positioner, *videoflip, *videoscale, *videorate, *capsfilter,
+ *videoconvert, *deinterlace;
+ const gchar *positioner_props[] =
+ { "alpha", "posx", "posy", "width", "height", NULL };
+ const gchar *deinterlace_props[] = { "mode", "fields", "tff", NULL };
+ const gchar *videoflip_props[] = { "video-direction", NULL };
+
+ if (!source_class->create_source)
+ return NULL;
+
+ sub_element = source_class->create_source (trksrc);
+
+ self = (GESVideoSource *) trksrc;
+
+ /* That positioner will add metadata to buffers according to its
+ properties, acting like a proxy for our smart-mixer dynamic pads. */
+ positioner = gst_element_factory_make ("framepositioner", "frame_tagger");
+ g_object_set (positioner, "zorder",
+ G_MAXUINT - GES_TIMELINE_ELEMENT_PRIORITY (self), NULL);
+
+ /* If there's image-orientation tag, make sure the image is correctly oriented
+ * before we scale it. */
+ videoflip = gst_element_factory_make ("videoflip", "track-element-videoflip");
+ g_object_set (videoflip, "video-direction", GST_VIDEO_ORIENTATION_AUTO, NULL);
+
+ videoscale =
+ gst_element_factory_make ("videoscale", "track-element-videoscale");
+ videoconvert =
+ gst_element_factory_make ("videoconvert", "track-element-videoconvert");
+ videorate = gst_element_factory_make ("videorate", "track-element-videorate");
+ deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
+ capsfilter =
+ gst_element_factory_make ("capsfilter", "track-element-capsfilter");
+
+ ges_frame_positioner_set_source_and_filter (GST_FRAME_POSITIONNER
+ (positioner), trksrc, capsfilter);
+
+ ges_track_element_add_children_props (trksrc, positioner, NULL, NULL,
+ positioner_props);
+ ges_track_element_add_children_props (trksrc, videoflip, NULL, NULL,
+ videoflip_props);
+
+ if (deinterlace == NULL) {
+ post_missing_element_message (sub_element, "deinterlace");
+
+ GST_ELEMENT_WARNING (sub_element, CORE, MISSING_PLUGIN,
+ ("Missing element '%s' - check your GStreamer installation.",
+ "deinterlace"), ("deinterlacing won't work"));
+ topbin =
+ ges_source_create_topbin ("videosrcbin", sub_element, queue,
+ videoconvert, positioner, videoflip, videoscale, videorate, capsfilter,
+ NULL);
+ } else {
+ ges_track_element_add_children_props (trksrc, deinterlace, NULL, NULL,
+ deinterlace_props);
+ topbin =
+ ges_source_create_topbin ("videosrcbin", sub_element, queue,
+ videoconvert, deinterlace, positioner, videoflip, videoscale, videorate,
+ capsfilter, NULL);
+ }
+
+ self->priv->positioner = GST_FRAME_POSITIONNER (positioner);
+ self->priv->positioner->scale_in_compositor =
+ !GES_VIDEO_SOURCE_GET_CLASS (self)->ABI.abi.disable_scale_in_compositor;
+ self->priv->capsfilter = capsfilter;
+
+ return topbin;
+}
+
+static gboolean
+_lookup_child (GESTimelineElement * object,
+ const gchar * prop_name, GObject ** element, GParamSpec ** pspec)
+{
+ gboolean res;
+
+ gchar *clean_name;
+
+ if (!g_strcmp0 (prop_name, "deinterlace-fields"))
+ clean_name = g_strdup ("GstDeinterlace::fields");
+ else if (!g_strcmp0 (prop_name, "deinterlace-mode"))
+ clean_name = g_strdup ("GstDeinterlace::mode");
+ else if (!g_strcmp0 (prop_name, "deinterlace-tff"))
+ clean_name = g_strdup ("GstDeinterlace::tff");
+ else if (!g_strcmp0 (prop_name, "tff") ||
+ !g_strcmp0 (prop_name, "fields") || !g_strcmp0 (prop_name, "mode")) {
+ GST_DEBUG_OBJECT (object, "Not allowed to use GstDeinterlace %s"
+ " property without prefixing its name", prop_name);
+ return FALSE;
+ } else
+ clean_name = g_strdup (prop_name);
+
+ res =
+ GES_TIMELINE_ELEMENT_CLASS (ges_video_source_parent_class)->lookup_child
+ (object, clean_name, element, pspec);
+
+ g_free (clean_name);
+
+ return res;
+}
+
+static void
+ges_video_source_class_init (GESVideoSourceClass * klass)
+{
+ GESTrackElementClass *track_element_class = GES_TRACK_ELEMENT_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+ GESVideoSourceClass *video_source_class = GES_VIDEO_SOURCE_CLASS (klass);
+
+ element_class->set_priority = _set_priority;
+ element_class->lookup_child = _lookup_child;
+
+ track_element_class->nleobject_factorytype = "nlesource";
+ track_element_class->create_element = ges_video_source_create_element;
+ video_source_class->create_source = NULL;
+}
+
+static void
+ges_video_source_init (GESVideoSource * self)
+{
+ self->priv = ges_video_source_get_instance_private (self);
+ self->priv->positioner = NULL;
+ self->priv->capsfilter = NULL;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_VIDEO_SOURCE
+#define _GES_VIDEO_SOURCE
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include <ges/ges-types.h>
+#include <ges/ges-track-element.h>
+#include <ges/ges-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_VIDEO_SOURCE ges_video_source_get_type()
+
+#define GES_VIDEO_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_VIDEO_SOURCE, GESVideoSource))
+
+#define GES_VIDEO_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_VIDEO_SOURCE, GESVideoSourceClass))
+
+#define GES_IS_VIDEO_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_VIDEO_SOURCE))
+
+#define GES_IS_VIDEO_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_VIDEO_SOURCE))
+
+#define GES_VIDEO_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_VIDEO_SOURCE, GESVideoSourceClass))
+
+typedef struct _GESVideoSourcePrivate GESVideoSourcePrivate;
+
+/**
+ * GESVideoSource:
+ *
+ * Base class for video sources
+ */
+
+struct _GESVideoSource {
+ /*< private >*/
+ GESSource parent;
+
+ GESVideoSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESVideoSourceClass:
+ * @create_source: method to return the GstElement to put in the source topbin.
+ * Other elements will be queued, like a videoscale.
+ * In the case of a VideoUriSource, for example, the subclass will return a decodebin,
+ * and we will append a videoscale.
+ */
+struct _GESVideoSourceClass {
+ /*< private >*/
+ GESSourceClass parent_class;
+
+ /*< public >*/
+ GstElement* (*create_source) (GESTrackElement * object);
+
+ /*< private >*/
+ /* Padding for API extension */
+ union {
+ gpointer _ges_reserved[GES_PADDING];
+ struct {
+ gboolean disable_scale_in_compositor;
+ } abi;
+ } ABI;
+};
+
+GES_API
+GType ges_video_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_VIDEO_SOURCE */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesvideotestsource
+ * @title: GESVideoTestSource
+ * @short_description: produce solid colors and patterns
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-video-test-source.h"
+
+#define DEFAULT_VPATTERN GES_VIDEO_TEST_PATTERN_SMPTE
+
+struct _GESVideoTestSourcePrivate
+{
+ GESVideoTestPattern pattern;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTestSource, ges_video_test_source,
+ GES_TYPE_VIDEO_SOURCE);
+
+static GstElement *ges_video_test_source_create_source (GESTrackElement * self);
+
+static void
+ges_video_test_source_class_init (GESVideoTestSourceClass * klass)
+{
+ GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass);
+
+ source_class->create_source = ges_video_test_source_create_source;
+}
+
+static void
+ges_video_test_source_init (GESVideoTestSource * self)
+{
+ self->priv = ges_video_test_source_get_instance_private (self);
+
+ self->priv->pattern = DEFAULT_VPATTERN;
+}
+
+static GstElement *
+ges_video_test_source_create_source (GESTrackElement * self)
+{
+ GstCaps *caps;
+ gint pattern;
+ GstElement *testsrc, *capsfilter;
+ const gchar *props[] = { "pattern", NULL };
+
+ testsrc = gst_element_factory_make ("videotestsrc", NULL);
+ capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ pattern = ((GESVideoTestSource *) self)->priv->pattern;
+
+ g_object_set (testsrc, "pattern", pattern, NULL);
+
+ caps = gst_caps_new_empty_simple ("video/x-raw");
+ g_object_set (capsfilter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ ges_track_element_add_children_props (self, testsrc, NULL, NULL, props);
+
+ return ges_source_create_topbin ("videotestsrc", testsrc, capsfilter, NULL);
+}
+
+/**
+ * ges_video_test_source_set_pattern:
+ * @self: a #GESVideoTestSource
+ * @pattern: a #GESVideoTestPattern
+ *
+ * Sets the source to use the given @pattern.
+ */
+void
+ges_video_test_source_set_pattern (GESVideoTestSource
+ * self, GESVideoTestPattern pattern)
+{
+ GstElement *element =
+ ges_track_element_get_element (GES_TRACK_ELEMENT (self));
+
+ self->priv->pattern = pattern;
+
+ if (element) {
+ GValue val = { 0 };
+
+ g_value_init (&val, GES_VIDEO_TEST_PATTERN_TYPE);
+ g_value_set_enum (&val, pattern);
+ ges_track_element_set_child_property (GES_TRACK_ELEMENT (self), "pattern",
+ &val);
+ }
+}
+
+/**
+ * ges_video_test_source_get_pattern:
+ * @source: a #GESVideoTestPattern
+ *
+ * Get the video pattern used by the @source.
+ *
+ * Returns: The video pattern used by the @source.
+ */
+GESVideoTestPattern
+ges_video_test_source_get_pattern (GESVideoTestSource * source)
+{
+ GValue val = { 0 };
+
+ ges_track_element_get_child_property (GES_TRACK_ELEMENT (source), "pattern",
+ &val);
+ return g_value_get_enum (&val);
+}
+
+/**
+ * ges_video_test_source_new:
+ *
+ * Creates a new #GESVideoTestSource.
+ *
+ * Returns: (transfer floating) (nullable): The newly created
+ * #GESVideoTestSource, or %NULL if there was an error.
+ */
+GESVideoTestSource *
+ges_video_test_source_new (void)
+{
+ return g_object_new (GES_TYPE_VIDEO_TEST_SOURCE, "track-type",
+ GES_TRACK_TYPE_VIDEO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_VIDEO_TEST_SOURCE
+#define _GES_VIDEO_TEST_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-enums.h>
+#include <ges/ges-types.h>
+#include <ges/ges-video-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_VIDEO_TEST_SOURCE ges_video_test_source_get_type()
+
+#define GES_VIDEO_TEST_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_VIDEO_TEST_SOURCE, GESVideoTestSource))
+
+#define GES_VIDEO_TEST_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_VIDEO_TEST_SOURCE, GESVideoTestSourceClass))
+
+#define GES_IS_VIDEO_TEST_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_VIDEO_TEST_SOURCE))
+
+#define GES_IS_VIDEO_TEST_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_VIDEO_TEST_SOURCE))
+
+#define GES_VIDEO_TEST_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_VIDEO_TEST_SOURCE, GESVideoTestSourceClass))
+
+typedef struct _GESVideoTestSourcePrivate GESVideoTestSourcePrivate;
+
+/**
+ * GESVideoTestSource:
+ */
+struct _GESVideoTestSource {
+ /*< private >*/
+ GESVideoSource parent;
+
+ GESVideoTestSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESVideoTestSourceClass {
+ GESVideoSourceClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_video_test_source_get_type (void);
+
+GES_API void
+ges_video_test_source_set_pattern(GESVideoTestSource *self,
+ GESVideoTestPattern pattern);
+GES_API GESVideoTestPattern
+ges_video_test_source_get_pattern (GESVideoTestSource *source);
+
+G_END_DECLS
+
+#endif /* _GES_VIDEO_TEST_SOURCE */
--- /dev/null
+/* GStreamer Editing Services
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION: gesvideotrack
+ * @title: GESVideoTrack
+ * @short_description: A standard GESTrack for raw video
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-video-track.h"
+#include "ges-smart-video-mixer.h"
+
+struct _GESVideoTrackPrivate
+{
+ gpointer nothing;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTrack, ges_video_track, GES_TYPE_TRACK);
+
+static void
+_sync_capsfilter_with_track (GESTrack * track, GstElement * capsfilter)
+{
+ GstCaps *restriction, *caps;
+ gint fps_n, fps_d;
+ GstStructure *structure;
+
+ g_object_get (track, "restriction-caps", &restriction, NULL);
+ if (restriction == NULL)
+ return;
+
+ if (gst_caps_get_size (restriction) == 0)
+ goto done;
+
+ structure = gst_caps_get_structure (restriction, 0);
+ if (!gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d))
+ goto done;
+
+ caps =
+ gst_caps_new_simple ("video/x-raw", "framerate", GST_TYPE_FRACTION, fps_n,
+ fps_d, NULL);
+
+ g_object_set (capsfilter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+done:
+ gst_caps_unref (restriction);
+}
+
+static void
+_track_restriction_changed_cb (GESTrack * track, GParamSpec * arg G_GNUC_UNUSED,
+ GstElement * capsfilter)
+{
+ _sync_capsfilter_with_track (track, capsfilter);
+}
+
+static void
+_weak_notify_cb (GESTrack * track, GstElement * capsfilter)
+{
+ g_signal_handlers_disconnect_by_func (track,
+ (GCallback) _track_restriction_changed_cb, capsfilter);
+}
+
+static GstElement *
+create_element_for_raw_video_gap (GESTrack * track)
+{
+ GstElement *bin;
+ GstElement *capsfilter;
+
+ bin = gst_parse_bin_from_description
+ ("videotestsrc pattern=2 name=src ! videorate ! capsfilter name=gapfilter caps=video/x-raw",
+ TRUE, NULL);
+
+ capsfilter = gst_bin_get_by_name (GST_BIN (bin), "gapfilter");
+ g_object_weak_ref (G_OBJECT (capsfilter), (GWeakNotify) _weak_notify_cb,
+ track);
+ g_signal_connect (track, "notify::restriction-caps",
+ (GCallback) _track_restriction_changed_cb, capsfilter);
+
+ _sync_capsfilter_with_track (track, capsfilter);
+
+ gst_object_unref (capsfilter);
+
+ return bin;
+}
+
+static void
+ges_video_track_init (GESVideoTrack * ges_video_track)
+{
+/* GESVideoTrackPrivate *priv = GES_VIDEO_TRACK_GET_PRIVATE (ges_video_track);
+ */
+
+ /* TODO: Add initialization code here */
+}
+
+static void
+ges_video_track_finalize (GObject * object)
+{
+ /* TODO: Add deinitalization code here */
+
+ G_OBJECT_CLASS (ges_video_track_parent_class)->finalize (object);
+}
+
+static void
+ges_video_track_class_init (GESVideoTrackClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+/* GESTrackClass *parent_class = GES_TRACK_CLASS (klass);
+ */
+
+ object_class->finalize = ges_video_track_finalize;
+
+ GES_TRACK_CLASS (klass)->get_mixing_element = ges_smart_mixer_new;
+}
+
+/**
+ * ges_video_track_new:
+ *
+ * Creates a new #GESVideoTrack of type #GES_TRACK_TYPE_VIDEO and with generic
+ * raw video caps ("video/x-raw");
+ *
+ * Returns: (transfer floating): A new #GESTrack.
+ */
+GESVideoTrack *
+ges_video_track_new (void)
+{
+ GESVideoTrack *ret;
+ GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
+
+ ret = g_object_new (GES_TYPE_VIDEO_TRACK, "track-type", GES_TRACK_TYPE_VIDEO,
+ "caps", caps, NULL);
+
+ ges_track_set_create_element_for_gap_func (GES_TRACK (ret),
+ create_element_for_raw_video_gap);
+
+ gst_caps_unref (caps);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_VIDEO_TRACK_H_
+#define _GES_VIDEO_TRACK_H_
+
+#include <glib-object.h>
+
+#include "ges-track.h"
+#include "ges-types.h"
+
+G_BEGIN_DECLS
+#define GES_TYPE_VIDEO_TRACK (ges_video_track_get_type ())
+#define GES_VIDEO_TRACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_VIDEO_TRACK, GESVideoTrack))
+#define GES_VIDEO_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_VIDEO_TRACK, GESVideoTrackClass))
+#define GES_IS_VIDEO_TRACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_VIDEO_TRACK))
+#define GES_IS_VIDEO_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_VIDEO_TRACK))
+#define GES_VIDEO_TRACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_VIDEO_TRACK, GESVideoTrackClass))
+
+typedef struct _GESVideoTrackPrivate GESVideoTrackPrivate;
+
+struct _GESVideoTrackClass
+{
+ GESTrackClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESVideoTrack
+{
+ GESTrack parent_instance;
+
+ /*< private >*/
+ GESVideoTrackPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_video_track_get_type (void) G_GNUC_CONST;
+
+GES_API
+GESVideoTrack * ges_video_track_new (void);
+
+G_END_DECLS
+#endif /* _GES_VIDEO_TRACK_H_ */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesvideotransition
+ * @title: GESVideoTransition
+ * @short_description: implements video crossfade transition
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ges/ges.h>
+#include "ges-internal.h"
+#include "ges-smart-video-mixer.h"
+
+#include <gst/controller/gstdirectcontrolbinding.h>
+
+#define parent_class ges_video_transition_parent_class
+
+static inline void
+ges_video_transition_set_border_internal (GESVideoTransition * self,
+ guint border);
+static inline void
+ges_video_transition_set_inverted_internal (GESVideoTransition *
+ self, gboolean inverted);
+static inline gboolean
+ges_video_transition_set_transition_type_internal (GESVideoTransition
+ * self, GESVideoStandardTransitionType type);
+struct _GESVideoTransitionPrivate
+{
+ GESVideoStandardTransitionType type;
+
+ /* prevents cases where the transitions have not been created yet */
+ GESVideoStandardTransitionType pending_type;
+
+ /* these enable video interpolation */
+ GstTimedValueControlSource *fade_in_control_source;
+ GstTimedValueControlSource *fade_out_control_source;
+ GstTimedValueControlSource *smpte_control_source;
+
+ /* so we can support changing between wipes */
+ GstElement *smpte;
+
+ GstPad *mixer_sink;
+
+ GstElement *mixer;
+ GstPad *mixer_sinka;
+ GstPad *mixer_sinkb;
+
+ GstPad *mixer_ghosta;
+ GstPad *mixer_ghostb;
+
+ /* This is in case the smpte doesn't exist yet */
+ gint pending_border_value;
+ gboolean pending_inverted;
+
+ GstElement *positioner;
+};
+
+enum
+{
+ PROP_0,
+ PROP_BORDER,
+ PROP_TRANSITION_TYPE,
+ PROP_INVERT,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTransition, ges_video_transition,
+ GES_TYPE_TRANSITION);
+
+#define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING)
+
+static GObject *link_element_to_mixer_with_smpte (GstBin * bin,
+ GstElement * element, GstElement * mixer, gint type,
+ GstElement ** smpteref, GESVideoTransitionPrivate * priv, GstPad ** ghost);
+
+static void
+ges_video_transition_duration_changed (GESTrackElement * self,
+ guint64 duration);
+
+static GstElement *ges_video_transition_create_element (GESTrackElement * self);
+
+static void ges_video_transition_dispose (GObject * object);
+
+static void ges_video_transition_finalize (GObject * object);
+
+static void ges_video_transition_get_property (GObject * object, guint
+ property_id, GValue * value, GParamSpec * pspec);
+
+static void ges_video_transition_set_property (GObject * object, guint
+ property_id, const GValue * value, GParamSpec * pspec);
+
+static void
+duration_changed_cb (GESTrackElement * self, GParamSpec * arg G_GNUC_UNUSED)
+{
+ ges_video_transition_duration_changed (self,
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (self)));
+}
+
+static gboolean
+_set_priority (GESTimelineElement * element, guint32 priority)
+{
+ gboolean res;
+ GESVideoTransition *self = GES_VIDEO_TRANSITION (element);
+
+ res = GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_priority (element,
+ priority);
+
+ if (res && self->priv->positioner)
+ g_object_set (self->priv->positioner, "zorder", G_MAXUINT - priority, NULL);
+
+ return res;
+}
+
+static void
+ges_video_transition_class_init (GESVideoTransitionClass * klass)
+{
+ GObjectClass *object_class;
+ GESTrackElementClass *toclass;
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ges_video_transition_get_property;
+ object_class->set_property = ges_video_transition_set_property;
+ object_class->dispose = ges_video_transition_dispose;
+ object_class->finalize = ges_video_transition_finalize;
+
+ /**
+ * GESVideoTransition:border:
+ *
+ * This value represents the border width of the transition.
+ *
+ */
+ properties[PROP_BORDER] =
+ g_param_spec_uint ("border", "Border", "The border width", 0,
+ G_MAXUINT, 0, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_BORDER,
+ properties[PROP_BORDER]);
+
+ /**
+ * GESVideoTransition:type:
+ *
+ * The #GESVideoStandardTransitionType currently applied on the object
+ *
+ */
+ properties[PROP_TRANSITION_TYPE] =
+ g_param_spec_enum ("transition-type", "Transition type",
+ "The type of the transition", GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_TRANSITION_TYPE,
+ properties[PROP_TRANSITION_TYPE]);
+
+ /**
+ * GESVideoTransition:invert:
+ *
+ * This value represents the direction of the transition.
+ *
+ */
+ properties[PROP_INVERT] =
+ g_param_spec_boolean ("invert", "Invert",
+ "Whether the transition is inverted", FALSE, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_INVERT,
+ properties[PROP_INVERT]);
+
+ toclass = GES_TRACK_ELEMENT_CLASS (klass);
+ toclass->create_element = ges_video_transition_create_element;
+
+ element_class->set_priority = _set_priority;
+}
+
+static void
+ges_video_transition_init (GESVideoTransition * self)
+{
+ self->priv = ges_video_transition_get_instance_private (self);
+
+ self->priv->fade_in_control_source = NULL;
+ self->priv->fade_out_control_source = NULL;
+ self->priv->smpte_control_source = NULL;
+ self->priv->smpte = NULL;
+ self->priv->mixer_sink = NULL;
+ self->priv->mixer = NULL;
+ self->priv->mixer_sinka = NULL;
+ self->priv->mixer_sinkb = NULL;
+ self->priv->pending_type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;
+ self->priv->pending_border_value = 0;
+ self->priv->pending_inverted = TRUE;
+}
+
+static void
+ges_video_transition_release_mixer (GESVideoTransition * self)
+{
+ GESVideoTransitionPrivate *priv = self->priv;
+
+ if (priv->mixer_ghosta && priv->mixer_ghostb) {
+ gst_element_release_request_pad (priv->mixer, priv->mixer_ghosta);
+ gst_element_release_request_pad (priv->mixer, priv->mixer_ghostb);
+ gst_clear_object (&priv->mixer_ghosta);
+ gst_clear_object (&priv->mixer_ghostb);
+ }
+
+ gst_clear_object (&priv->mixer_sinka);
+ gst_clear_object (&priv->mixer_sinkb);
+ gst_clear_object (&priv->mixer);
+}
+
+static void
+ges_video_transition_dispose (GObject * object)
+{
+ GESVideoTransition *self = GES_VIDEO_TRANSITION (object);
+ GESVideoTransitionPrivate *priv = self->priv;
+
+ GST_DEBUG ("disposing");
+
+ if (priv->fade_in_control_source) {
+ gst_object_unref (priv->fade_in_control_source);
+ priv->fade_in_control_source = NULL;
+ }
+
+ if (priv->fade_out_control_source) {
+ gst_object_unref (priv->fade_out_control_source);
+ priv->fade_out_control_source = NULL;
+ }
+
+ if (priv->smpte_control_source) {
+ gst_object_unref (priv->smpte_control_source);
+ priv->smpte_control_source = NULL;
+ }
+
+ ges_video_transition_release_mixer (self);
+
+ g_signal_handlers_disconnect_by_func (GES_TRACK_ELEMENT (self),
+ duration_changed_cb, NULL);
+
+ G_OBJECT_CLASS (ges_video_transition_parent_class)->dispose (object);
+}
+
+static void
+ges_video_transition_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ges_video_transition_parent_class)->finalize (object);
+}
+
+static void
+ges_video_transition_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ GESVideoTransition *tr = GES_VIDEO_TRANSITION (object);
+
+ switch (property_id) {
+ case PROP_BORDER:
+ g_value_set_uint (value, ges_video_transition_get_border (tr));
+ break;
+ case PROP_TRANSITION_TYPE:
+ g_value_set_enum (value, ges_video_transition_get_transition_type (tr));
+ break;
+ case PROP_INVERT:
+ g_value_set_boolean (value, ges_video_transition_is_inverted (tr));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_video_transition_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec)
+{
+ GESVideoTransition *tr = GES_VIDEO_TRANSITION (object);
+
+ switch (property_id) {
+ case PROP_BORDER:
+ ges_video_transition_set_border_internal (tr, g_value_get_uint (value));
+ break;
+ case PROP_TRANSITION_TYPE:
+ ges_video_transition_set_transition_type_internal (tr,
+ g_value_get_enum (value));
+ break;
+ case PROP_INVERT:
+ ges_video_transition_set_inverted_internal (tr,
+ g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GstTimedValueControlSource *
+set_interpolation (GstObject * element, GESVideoTransitionPrivate * priv,
+ const gchar * propname)
+{
+ GstControlSource *control_source;
+
+ g_object_set (element, propname, (gfloat) 0.0, NULL);
+
+ control_source = gst_interpolation_control_source_new ();
+ gst_object_add_control_binding (GST_OBJECT (element),
+ gst_direct_control_binding_new (GST_OBJECT (element), propname,
+ control_source));
+ g_object_set (control_source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+
+ return GST_TIMED_VALUE_CONTROL_SOURCE (control_source);
+}
+
+static GstElement *
+ges_video_transition_create_element (GESTrackElement * object)
+{
+ GstElement *topbin, *iconva, *iconvb, *oconv;
+ GstElement *mixer = NULL;
+ GstPad *sinka_target, *sinkb_target, *src_target, *sinka, *sinkb, *src;
+ GESVideoTransition *self;
+ GESVideoTransitionPrivate *priv;
+
+ self = GES_VIDEO_TRANSITION (object);
+ priv = self->priv;
+
+ GST_LOG ("creating a video bin");
+
+ topbin = gst_bin_new ("transition-bin");
+
+ iconva = gst_element_factory_make ("videoconvert", "tr-csp-a");
+ iconvb = gst_element_factory_make ("videoconvert", "tr-csp-b");
+ priv->positioner =
+ gst_element_factory_make ("framepositioner", "frame_tagger");
+ g_object_set (priv->positioner, "zorder",
+ G_MAXUINT - GES_TIMELINE_ELEMENT_PRIORITY (self), NULL);
+ oconv = gst_element_factory_make ("videoconvert", "tr-csp-output");
+
+ gst_bin_add_many (GST_BIN (topbin), iconva, iconvb, priv->positioner,
+ oconv, NULL);
+
+ mixer = ges_smart_mixer_new (NULL);
+ GES_SMART_MIXER (mixer)->disable_zorder_alpha = TRUE;
+ g_object_set (GES_SMART_MIXER (mixer)->mixer, "background", 3, NULL); /* transparent */
+ gst_bin_add (GST_BIN (topbin), mixer);
+
+ priv->mixer_sinka =
+ (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconva,
+ mixer, GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR, NULL, priv,
+ &priv->mixer_ghosta);
+ priv->mixer_sinkb =
+ (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconvb,
+ mixer, GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR, &priv->smpte,
+ priv, &priv->mixer_ghostb);
+ g_object_set (priv->mixer_sinka, "zorder", 0, NULL);
+ gst_util_set_object_arg (G_OBJECT (priv->mixer_sinka), "operator", "source");
+ g_object_set (priv->mixer_sinkb, "zorder", 1, NULL);
+ gst_util_set_object_arg (G_OBJECT (priv->mixer_sinkb), "operator", "add");
+
+ fast_element_link (mixer, priv->positioner);
+ fast_element_link (priv->positioner, oconv);
+
+ sinka_target = gst_element_get_static_pad (iconva, "sink");
+ sinkb_target = gst_element_get_static_pad (iconvb, "sink");
+ src_target = gst_element_get_static_pad (oconv, "src");
+
+ sinka = gst_ghost_pad_new ("sinka", sinka_target);
+ sinkb = gst_ghost_pad_new ("sinkb", sinkb_target);
+ src = gst_ghost_pad_new ("src", src_target);
+
+ gst_element_add_pad (topbin, src);
+ gst_element_add_pad (topbin, sinka);
+ gst_element_add_pad (topbin, sinkb);
+
+ gst_object_unref (sinka_target);
+ gst_object_unref (sinkb_target);
+ gst_object_unref (src_target);
+
+ /* set up interpolation */
+
+ priv->fade_out_control_source =
+ set_interpolation (GST_OBJECT (priv->mixer_ghosta), priv, "alpha");
+ priv->fade_in_control_source =
+ set_interpolation (GST_OBJECT (priv->mixer_ghostb), priv, "alpha");
+ priv->smpte_control_source =
+ set_interpolation (GST_OBJECT (priv->smpte), priv, "position");
+ priv->mixer = gst_object_ref (mixer);
+
+ if (priv->pending_type)
+ ges_video_transition_set_transition_type_internal (self,
+ priv->pending_type);
+ else
+ ges_video_transition_set_transition_type_internal (self, priv->type);
+
+ ges_video_transition_duration_changed (object,
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (object)));
+
+ g_signal_connect (object, "notify::duration",
+ G_CALLBACK (duration_changed_cb), NULL);
+
+ priv->pending_type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;
+
+ return topbin;
+}
+
+static GObject *
+link_element_to_mixer_with_smpte (GstBin * bin, GstElement * element,
+ GstElement * mixer, gint type, GstElement ** smpteref,
+ GESVideoTransitionPrivate * priv, GstPad ** ghost)
+{
+ GstPad *srcpad, *mixerpad;
+ GstElement *smptealpha = gst_element_factory_make ("smptealpha", NULL);
+
+ g_object_set (G_OBJECT (smptealpha),
+ "type", (gint) type, "invert", (gboolean) priv->pending_inverted,
+ "border", priv->pending_border_value, NULL);
+ gst_bin_add (bin, smptealpha);
+
+ fast_element_link (element, smptealpha);
+
+ /* crack */
+ if (smpteref) {
+ *smpteref = smptealpha;
+ }
+
+ srcpad = gst_element_get_static_pad (smptealpha, "src");
+ *ghost = ges_smart_mixer_get_mixer_pad (GES_SMART_MIXER (mixer), &mixerpad);
+ gst_pad_link_full (srcpad, *ghost, GST_PAD_LINK_CHECK_NOTHING);
+ gst_object_unref (srcpad);
+
+ return G_OBJECT (mixerpad);
+}
+
+static void
+ges_video_transition_update_control_source (GstTimedValueControlSource * ts,
+ guint64 duration, gdouble start_value, gdouble end_value)
+{
+ gst_timed_value_control_source_unset_all (ts);
+ gst_timed_value_control_source_set (ts, 0, start_value);
+ gst_timed_value_control_source_set (ts, duration, end_value);
+}
+
+static void
+ges_video_transition_update_control_sources (GESVideoTransition * self,
+ GESVideoStandardTransitionType type)
+{
+ GESVideoTransitionPrivate *priv = self->priv;
+ guint64 duration =
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (self));
+
+ GST_LOG ("updating controller");
+ if (type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) {
+ ges_video_transition_update_control_source
+ (priv->fade_in_control_source, duration, 0.0, 1.0);
+ ges_video_transition_update_control_source
+ (priv->fade_out_control_source, duration, 1.0, 0.0);
+ ges_video_transition_update_control_source (priv->smpte_control_source,
+ duration, 0.0, 0.0);
+ } else {
+ ges_video_transition_update_control_source
+ (priv->fade_in_control_source, duration, 1.0, 1.0);
+ ges_video_transition_update_control_source
+ (priv->fade_out_control_source, duration, 1.0, 1.0);
+ ges_video_transition_update_control_source (priv->smpte_control_source,
+ duration, 1.0, 0.0);
+ }
+ GST_LOG ("done updating controller");
+}
+
+static void
+ges_video_transition_duration_changed (GESTrackElement * object,
+ guint64 duration)
+{
+ GESVideoTransition *self = GES_VIDEO_TRANSITION (object);
+
+ ges_video_transition_update_control_sources (self, self->priv->type);
+}
+
+static inline void
+ges_video_transition_set_border_internal (GESVideoTransition * self,
+ guint value)
+{
+ GESVideoTransitionPrivate *priv = self->priv;
+
+ if (!priv->smpte) {
+ priv->pending_border_value = value;
+ return;
+ }
+ g_object_set (priv->smpte, "border", value, NULL);
+}
+
+static inline void
+ges_video_transition_set_inverted_internal (GESVideoTransition *
+ self, gboolean inverted)
+{
+ GESVideoTransitionPrivate *priv = self->priv;
+
+ if (!priv->smpte) {
+ priv->pending_inverted = !inverted;
+ return;
+ }
+ g_object_set (priv->smpte, "invert", !inverted, NULL);
+}
+
+static inline gboolean
+ges_video_transition_set_transition_type_internal (GESVideoTransition
+ * self, GESVideoStandardTransitionType type)
+{
+ GESVideoTransitionPrivate *priv = self->priv;
+
+ GST_DEBUG ("%p %d => %d", self, priv->type, type);
+
+ if (!priv->mixer) {
+ priv->pending_type = type;
+ return TRUE;
+ }
+
+ if (type == priv->type) {
+ GST_DEBUG ("%d type is already set on this transition\n", type);
+ return TRUE;
+ }
+
+ ges_video_transition_update_control_sources (self, type);
+
+ priv->type = type;
+
+ if (type != GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) {
+ g_object_set (priv->smpte, "type", (gint) type, NULL);
+ }
+
+ return TRUE;
+}
+
+/**
+ * ges_video_transition_set_border:
+ * @self: The #GESVideoTransition to set the border to
+ * @value: The value of the border to set on @object
+ *
+ * Set the border property of @self, this value represents
+ * the border width of the transition. In case this value does
+ * not make sense for the current transition type, it is cached
+ * for later use.
+ */
+void
+ges_video_transition_set_border (GESVideoTransition * self, guint value)
+{
+ ges_video_transition_set_border_internal (self, value);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BORDER]);
+}
+
+/**
+ * ges_video_transition_get_border:
+ * @self: The #GESVideoTransition to get the border from
+ *
+ * Get the border property of @self, this value represents
+ * the border width of the transition.
+ *
+ * Returns: The border values of @self or -1 if not meaningful
+ * (this will happen when not using a smpte transition).
+ */
+gint
+ges_video_transition_get_border (GESVideoTransition * self)
+{
+ gint value;
+
+ if (!self->priv->smpte) {
+ return -1;
+ }
+
+ g_object_get (self->priv->smpte, "border", &value, NULL);
+
+ return value;
+}
+
+/**
+ * ges_video_transition_set_inverted:
+ * @self: The #GESVideoTransition to set invert on
+ * @inverted: %TRUE if the transition should be inverted %FALSE otherwise
+ *
+ * Set the invert property of @self, this value represents
+ * the direction of the transition. In case this value does
+ * not make sense for the current transition type, it is cached
+ * for later use.
+ */
+void
+ges_video_transition_set_inverted (GESVideoTransition * self, gboolean inverted)
+{
+ ges_video_transition_set_inverted_internal (self, inverted);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INVERT]);
+}
+
+/**
+ * ges_video_transition_is_inverted:
+ * @self: The #GESVideoTransition to get the inversion from
+ *
+ * Get the invert property of @self, this value represents
+ * the direction of the transition.
+ *
+ * Returns: The invert value of @self
+ */
+gboolean
+ges_video_transition_is_inverted (GESVideoTransition * self)
+{
+ gboolean inverted;
+
+ if (!self->priv->smpte) {
+ return FALSE;
+ }
+
+ g_object_get (self->priv->smpte, "invert", &inverted, NULL);
+
+ return !inverted;
+}
+
+/**
+ * ges_video_transition_set_transition_type:
+ * @self: a #GESVideoTransition
+ * @type: a #GESVideoStandardTransitionType
+ *
+ * Sets the transition being used to @type.
+ *
+ * Returns: %TRUE if the transition type was properly changed, else %FALSE.
+ */
+gboolean
+ges_video_transition_set_transition_type (GESVideoTransition * self,
+ GESVideoStandardTransitionType type)
+{
+ gboolean ret = ges_video_transition_set_transition_type_internal (self, type);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TRANSITION_TYPE]);
+
+ return ret;
+}
+
+/**
+ * ges_video_transition_get_transition_type:
+ * @trans: a #GESVideoTransition
+ *
+ * Get the transition type used by @trans.
+ *
+ * Returns: The transition type used by @trans.
+ */
+GESVideoStandardTransitionType
+ges_video_transition_get_transition_type (GESVideoTransition * trans)
+{
+ if (trans->priv->pending_type)
+ return trans->priv->pending_type;
+ return trans->priv->type;
+}
+
+/**
+ * ges_video_transition_new:
+ *
+ * Creates a new #GESVideoTransition.
+ *
+ * Returns: (transfer floating) (nullable): The newly created
+ * #GESVideoTransition, or %NULL if there was an error.
+ */
+GESVideoTransition *
+ges_video_transition_new (void)
+{
+ return g_object_new (GES_TYPE_VIDEO_TRANSITION, "track-type",
+ GES_TRACK_TYPE_VIDEO, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ * 2010 Nokia Corporation
+ *
+ * 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 _GES_VIDEO_TRANSITION
+#define _GES_VIDEO_TRANSITION
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-transition.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_VIDEO_TRANSITION ges_video_transition_get_type()
+
+#define GES_VIDEO_TRANSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_VIDEO_TRANSITION, GESVideoTransition))
+
+#define GES_VIDEO_TRANSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_VIDEO_TRANSITION, GESVideoTransitionClass))
+
+#define GES_IS_VIDEO_TRANSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_VIDEO_TRANSITION))
+
+#define GES_IS_VIDEO_TRANSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_VIDEO_TRANSITION))
+
+#define GES_VIDEO_TRANSITION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_VIDEO_TRANSITION, GESVideoTransitionClass))
+
+typedef struct _GESVideoTransitionPrivate GESVideoTransitionPrivate;
+
+/**
+ * GESVideoTransition:
+ */
+
+struct _GESVideoTransition {
+ GESTransition parent;
+
+ /*< private >*/
+
+ GESVideoTransitionPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+/**
+ * GESVideoTransitionClass:
+ * @parent_class: parent class
+ *
+ */
+
+struct _GESVideoTransitionClass {
+ GESTransitionClass parent_class;
+
+ /*< private >*/
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_video_transition_get_type (void);
+GES_API
+GESVideoTransition* ges_video_transition_new (void);
+
+GES_API
+gboolean ges_video_transition_set_transition_type (GESVideoTransition * self,
+ GESVideoStandardTransitionType type);
+GES_API GESVideoStandardTransitionType
+ges_video_transition_get_transition_type (GESVideoTransition * trans);
+
+GES_API
+void ges_video_transition_set_border (GESVideoTransition * self,
+ guint value);
+GES_API
+gint ges_video_transition_get_border (GESVideoTransition * self);
+
+GES_API
+void ges_video_transition_set_inverted (GESVideoTransition * self,
+ gboolean inverted);
+GES_API
+gboolean ges_video_transition_is_inverted (GESVideoTransition * self);
+
+G_END_DECLS
+
+#endif /* _GES_TRACK_VIDEO_transition */
+
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gesvideourisource
+ * @title: GESVideoUriSource
+ * @short_description: outputs a single video stream from a given file
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/pbutils/missing-plugins.h>
+
+#include "ges-utils.h"
+#include "ges-internal.h"
+#include "ges-track-element.h"
+#include "ges-video-uri-source.h"
+#include "ges-uri-asset.h"
+#include "ges-extractable.h"
+
+struct _GESVideoUriSourcePrivate
+{
+ GstElement *decodebin; /* Reference owned by parent class */
+};
+
+enum
+{
+ PROP_0,
+ PROP_URI
+};
+
+static void
+ges_video_uri_source_track_set_cb (GESVideoUriSource * self,
+ GParamSpec * arg G_GNUC_UNUSED, gpointer nothing)
+{
+ GESTrack *track;
+ const GstCaps *caps = NULL;
+
+ if (!self->priv->decodebin)
+ return;
+
+ track = ges_track_element_get_track (GES_TRACK_ELEMENT (self));
+ if (!track)
+ return;
+
+ caps = ges_track_get_caps (track);
+
+ GST_INFO_OBJECT (self, "Setting caps to: %" GST_PTR_FORMAT, caps);
+ g_object_set (self->priv->decodebin, "caps", caps, NULL);
+}
+
+/* GESSource VMethod */
+static GstElement *
+ges_video_uri_source_create_source (GESTrackElement * trksrc)
+{
+ GESVideoUriSource *self;
+ GESTrack *track;
+ GstElement *decodebin;
+ const GstCaps *caps = NULL;
+
+ self = (GESVideoUriSource *) trksrc;
+
+ track = ges_track_element_get_track (trksrc);
+ if (track)
+ caps = ges_track_get_caps (track);
+
+ decodebin = self->priv->decodebin = gst_element_factory_make ("uridecodebin",
+ NULL);
+
+ g_object_set (decodebin, "caps", caps,
+ "expose-all-streams", FALSE, "uri", self->uri, NULL);
+
+ return decodebin;
+}
+
+/* Extractable interface implementation */
+
+static gchar *
+ges_extractable_check_id (GType type, const gchar * id, GError ** error)
+{
+ return g_strdup (id);
+}
+
+static void
+extractable_set_asset (GESExtractable * self, GESAsset * asset)
+{
+ /* FIXME That should go into #GESTrackElement, but
+ * some work is needed to make sure it works properly */
+
+ if (ges_track_element_get_track_type (GES_TRACK_ELEMENT (self)) ==
+ GES_TRACK_TYPE_UNKNOWN) {
+ ges_track_element_set_track_type (GES_TRACK_ELEMENT (self),
+ ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET
+ (asset)));
+ }
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_URI_SOURCE_ASSET;
+ iface->check_id = ges_extractable_check_id;
+ iface->set_asset = extractable_set_asset;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESVideoUriSource, ges_video_uri_source,
+ GES_TYPE_VIDEO_SOURCE, G_ADD_PRIVATE (GESVideoUriSource)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
+
+/* GObject VMethods */
+
+static void
+ges_video_uri_source_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESVideoUriSource *uriclip = GES_VIDEO_URI_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, uriclip->uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_video_uri_source_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESVideoUriSource *uriclip = GES_VIDEO_URI_SOURCE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ if (uriclip->uri) {
+ GST_WARNING_OBJECT (object, "Uri already set to %s", uriclip->uri);
+ return;
+ }
+ uriclip->uri = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_video_uri_source_dispose (GObject * object)
+{
+ GESVideoUriSource *uriclip = GES_VIDEO_URI_SOURCE (object);
+
+ if (uriclip->uri)
+ g_free (uriclip->uri);
+
+ G_OBJECT_CLASS (ges_video_uri_source_parent_class)->dispose (object);
+}
+
+static void
+ges_video_uri_source_class_init (GESVideoUriSourceClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass);
+
+ object_class->get_property = ges_video_uri_source_get_property;
+ object_class->set_property = ges_video_uri_source_set_property;
+ object_class->dispose = ges_video_uri_source_dispose;
+
+ /**
+ * GESVideoUriSource:uri:
+ *
+ * The location of the file/resource to use.
+ */
+ g_object_class_install_property (object_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "uri of the resource",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ source_class->create_source = ges_video_uri_source_create_source;
+}
+
+static void
+ges_video_uri_source_init (GESVideoUriSource * self)
+{
+ self->priv = ges_video_uri_source_get_instance_private (self);
+
+ g_signal_connect (self, "notify::track",
+ G_CALLBACK (ges_video_uri_source_track_set_cb), NULL);
+}
+
+/**
+ * ges_video_uri_source_new:
+ * @uri: the URI the source should control
+ *
+ * Creates a new #GESVideoUriSource for the provided @uri.
+ *
+ * Returns: (transfer floating) (nullable): The newly created #GESVideoUriSource,
+ * or %NULL if there was an error.
+ */
+GESVideoUriSource *
+ges_video_uri_source_new (gchar * uri)
+{
+ return g_object_new (GES_TYPE_VIDEO_URI_SOURCE, "uri", uri, NULL);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 _GES_VIDEO_URI_SOURCE
+#define _GES_VIDEO_URI_SOURCE
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-video-source.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_VIDEO_URI_SOURCE ges_video_uri_source_get_type()
+
+#define GES_VIDEO_URI_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_VIDEO_URI_SOURCE, GESVideoUriSource))
+
+#define GES_VIDEO_URI_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_VIDEO_URI_SOURCE, GESVideoUriSourceClass))
+
+#define GES_IS_VIDEO_URI_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_VIDEO_URI_SOURCE))
+
+#define GES_IS_VIDEO_URI_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_VIDEO_URI_SOURCE))
+
+#define GES_VIDEO_URI_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_VIDEO_URI_SOURCE, GESVideoUriSourceClass))
+
+typedef struct _GESVideoUriSourcePrivate GESVideoUriSourcePrivate;
+
+/**
+ * GESVideoUriSource:
+ */
+struct _GESVideoUriSource {
+ /*< private >*/
+ GESVideoSource parent;
+
+ gchar *uri;
+
+ GESVideoUriSourcePrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESVideoUriSourceClass {
+ /*< private >*/
+ GESVideoSourceClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GES_API
+GType ges_video_uri_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GES_VIDEO_URI_SOURCE */
+
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#undef VERSION
+#endif
+
+
+/* TODO Determine error codes numbers */
+
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+
+#include "ges.h"
+#include "ges-internal.h"
+
+#define parent_class ges_xml_formatter_parent_class
+#define API_VERSION 0
+#define MINOR_VERSION 5
+#define VERSION 0.5
+
+#define COLLECT_STR_OPT (G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL)
+
+#define _GET_PRIV(o) (((GESXmlFormatter*)o)->priv)
+
+struct _GESXmlFormatterPrivate
+{
+ gboolean ges_opened;
+ gboolean project_opened;
+
+ GString *str;
+
+ GHashTable *element_id;
+
+ guint nbelements;
+
+ guint min_version;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESXmlFormatter, ges_xml_formatter,
+ GES_TYPE_BASE_XML_FORMATTER);
+
+static inline void
+_parse_ges_element (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ guint api_version;
+ const gchar *version, *properties;
+
+ gchar **split_version = NULL;
+
+ if (g_strcmp0 (element_name, "ges")) {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Missing <ges> element'", element_name);
+ return;
+ }
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error, G_MARKUP_COLLECT_STRING, "version", &version,
+ COLLECT_STR_OPT, "properties", &properties,
+ G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+
+ split_version = g_strsplit (version, ".", 2);
+ if (split_version[1] == NULL)
+ goto failed;
+
+ errno = 0;
+ api_version = g_ascii_strtoull (split_version[0], NULL, 10);
+ if (errno || api_version != API_VERSION)
+ goto stroull_failed;
+
+ self->priv->min_version = g_ascii_strtoull (split_version[1], NULL, 10);
+ if (self->priv->min_version > MINOR_VERSION)
+ goto failed;
+
+ _GET_PRIV (self)->ges_opened = TRUE;
+ g_strfreev (split_version);
+ return;
+
+failed:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', %s wrong version'", element_name, version);
+ if (split_version)
+ g_strfreev (split_version);
+
+ return;
+
+stroull_failed:
+ GST_WARNING_OBJECT (self, "Error while strtoull: %s", g_strerror (errno));
+ goto failed;
+}
+
+static inline void
+_parse_project (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ const gchar *metadatas = NULL, *properties;
+ GESXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (g_strcmp0 (element_name, "project")) {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Found element '%s', Missing '<project>' element'", element_name);
+ } else {
+ priv->project_opened = TRUE;
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ COLLECT_STR_OPT, "properties", &properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (GES_FORMATTER (self)->project && metadatas)
+ ges_meta_container_add_metas_from_string (GES_META_CONTAINER
+ (GES_FORMATTER (self)->project), metadatas);
+
+ }
+}
+
+static inline void
+_parse_encoding_profile (GMarkupParseContext * context,
+ const gchar * element_name, const gchar ** attribute_names,
+ const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
+{
+ GstCaps *capsformat = NULL;
+ GstStructure *preset_properties = NULL;
+ const gchar *name, *description, *type, *preset = NULL,
+ *str_preset_properties = NULL, *preset_name = NULL, *format;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_STRING, "description", &description,
+ G_MARKUP_COLLECT_STRING, "type", &type,
+ COLLECT_STR_OPT, "preset", &preset,
+ COLLECT_STR_OPT, "preset-properties", &str_preset_properties,
+ COLLECT_STR_OPT, "preset-name", &preset_name,
+ COLLECT_STR_OPT, "format", &format, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (format)
+ capsformat = gst_caps_from_string (format);
+
+ if (str_preset_properties) {
+ preset_properties = gst_structure_from_string (str_preset_properties, NULL);
+ if (preset_properties == NULL) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Wrong preset-properties format.", element_name);
+ return;
+ }
+ }
+
+ ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self),
+ type, NULL, name, description, capsformat, preset, preset_properties,
+ preset_name, 0, 0, NULL, 0, FALSE, NULL, TRUE, error);
+}
+
+static inline void
+_parse_stream_profile (GMarkupParseContext * context,
+ const gchar * element_name, const gchar ** attribute_names,
+ const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
+{
+ gboolean variableframerate = FALSE, enabled = TRUE;
+ guint id = 0, presence = 0, pass = 0;
+ GstCaps *format_caps = NULL, *restriction_caps = NULL;
+ GstStructure *preset_properties = NULL;
+ const gchar *parent, *strid, *type, *strpresence, *format = NULL,
+ *name = NULL, *description = NULL, *preset,
+ *str_preset_properties = NULL, *preset_name = NULL, *restriction = NULL,
+ *strpass = NULL, *strvariableframerate = NULL, *strenabled = NULL;
+
+ /* FIXME Looks like there is a bug in that function, if we put the parent
+ * at the beginning it set %NULL and not the real value... :/ */
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &strid,
+ G_MARKUP_COLLECT_STRING, "type", &type,
+ G_MARKUP_COLLECT_STRING, "presence", &strpresence,
+ COLLECT_STR_OPT, "format", &format,
+ COLLECT_STR_OPT, "name", &name,
+ COLLECT_STR_OPT, "description", &description,
+ COLLECT_STR_OPT, "preset", &preset,
+ COLLECT_STR_OPT, "preset-properties", &str_preset_properties,
+ COLLECT_STR_OPT, "preset-name", &preset_name,
+ COLLECT_STR_OPT, "restriction", &restriction,
+ COLLECT_STR_OPT, "pass", &strpass,
+ COLLECT_STR_OPT, "variableframerate", &strvariableframerate,
+ COLLECT_STR_OPT, "enabled", &strenabled,
+ G_MARKUP_COLLECT_STRING, "parent", &parent, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ errno = 0;
+ id = g_ascii_strtoll (strid, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ if (strpresence) {
+ presence = g_ascii_strtoll (strpresence, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+ }
+
+ if (str_preset_properties) {
+ preset_properties = gst_structure_from_string (str_preset_properties, NULL);
+ if (preset_properties == NULL)
+ goto convertion_failed;
+ }
+
+ if (strpass) {
+ pass = g_ascii_strtoll (strpass, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+ }
+
+ if (strvariableframerate) {
+ variableframerate = g_ascii_strtoll (strvariableframerate, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+ }
+
+ if (strenabled) {
+ enabled = g_ascii_strtoll (strenabled, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+ }
+
+ if (format)
+ format_caps = gst_caps_from_string (format);
+
+ if (restriction)
+ restriction_caps = gst_caps_from_string (restriction);
+
+ ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self),
+ type, parent, name, description, format_caps, preset, preset_properties,
+ preset_name, id, presence, restriction_caps, pass, variableframerate,
+ NULL, enabled, error);
+
+ if (preset_properties)
+ gst_structure_free (preset_properties);
+
+ return;
+
+convertion_failed:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Wrong property type, error: %s'", element_name,
+ g_strerror (errno));
+ return;
+}
+
+static inline void
+_parse_timeline (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ const gchar *metadatas = NULL, *properties = NULL;
+ GESTimeline *timeline = GES_FORMATTER (self)->timeline;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ COLLECT_STR_OPT, "properties", &properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (timeline == NULL)
+ return;
+
+ ges_base_xml_formatter_set_timeline_properties (GES_BASE_XML_FORMATTER (self),
+ timeline, properties, metadatas);
+}
+
+static inline void
+_parse_asset (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ GType extractable_type;
+ const gchar *id, *extractable_type_name, *metadatas = NULL, *properties =
+ NULL, *proxy_id = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error, G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_STRING, "extractable-type-name",
+ &extractable_type_name,
+ COLLECT_STR_OPT, "properties", &properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas,
+ COLLECT_STR_OPT, "proxy-id", &proxy_id, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ extractable_type = g_type_from_name (extractable_type_name);
+ if (extractable_type == G_TYPE_NONE)
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s' invalid extractable_type %s'",
+ element_name, extractable_type_name);
+ else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE))
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', %s not an extractable_type'",
+ element_name, extractable_type_name);
+ else {
+ GstStructure *props = NULL;
+ if (properties)
+ props = gst_structure_from_string (properties, NULL);
+
+ ges_base_xml_formatter_add_asset (GES_BASE_XML_FORMATTER (self), id,
+ extractable_type, props, metadatas, proxy_id, error);
+ if (props)
+ gst_structure_free (props);
+ }
+}
+
+
+static inline void
+_parse_track (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ GstCaps *caps;
+ GESTrackType track_type;
+ GstStructure *props = NULL;
+ const gchar *strtrack_type, *strcaps, *strtrack_id, *metadatas =
+ NULL, *properties = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "track-type", &strtrack_type,
+ G_MARKUP_COLLECT_STRING, "track-id", &strtrack_id,
+ COLLECT_STR_OPT, "properties", &properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas,
+ G_MARKUP_COLLECT_STRING, "caps", &strcaps, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if ((caps = gst_caps_from_string (strcaps)) == NULL)
+ goto wrong_caps;
+
+ errno = 0;
+ track_type = g_ascii_strtoll (strtrack_type, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ if (properties) {
+ props = gst_structure_from_string (properties, NULL);
+ }
+
+ ges_base_xml_formatter_add_track (GES_BASE_XML_FORMATTER (self), track_type,
+ caps, strtrack_id, props, metadatas, error);
+
+ if (props)
+ gst_structure_free (props);
+
+ gst_caps_unref (caps);
+
+ return;
+
+wrong_caps:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Can not create caps: %s'", element_name, strcaps);
+ return;
+
+convertion_failed:
+ gst_caps_unref (caps);
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Wrong property type, error: %s'", element_name,
+ g_strerror (errno));
+ return;
+
+}
+
+static inline void
+_parse_layer (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ GstStructure *props = NULL;
+ guint priority;
+ GType extractable_type = G_TYPE_NONE;
+ const gchar *metadatas = NULL, *properties = NULL, *strprio = NULL,
+ *extractable_type_name;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "priority", &strprio,
+ COLLECT_STR_OPT, "extractable-type-name", &extractable_type_name,
+ COLLECT_STR_OPT, "properties", &properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (extractable_type_name) {
+ extractable_type = g_type_from_name (extractable_type_name);
+ if (extractable_type == G_TYPE_NONE) {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s' invalid extractable_type %s'",
+ element_name, extractable_type_name);
+
+ return;
+ } else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE)) {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', %s not an extractable_type'",
+ element_name, extractable_type_name);
+
+ return;
+ }
+ }
+
+ if (properties) {
+ props = gst_structure_from_string (properties, NULL);
+ if (props == NULL)
+ goto wrong_properties;
+ }
+
+ errno = 0;
+ priority = g_ascii_strtoll (strprio, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ ges_base_xml_formatter_add_layer (GES_BASE_XML_FORMATTER (self),
+ extractable_type, priority, props, metadatas, error);
+
+done:
+ if (props)
+ gst_structure_free (props);
+
+ return;
+
+convertion_failed:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Wrong property type, error: %s'", element_name,
+ g_strerror (errno));
+ goto done;
+
+wrong_properties:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', wrong layer properties '%s', could no be deserialized",
+ element_name, properties);
+}
+
+static inline void
+_parse_clip (GMarkupParseContext * context,
+ const gchar * element_name, const gchar ** attribute_names,
+ const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
+{
+ GType type;
+ GstStructure *props = NULL, *children_props = NULL;
+ GESTrackType track_types;
+ GstClockTime start, inpoint = 0, duration, layer_prio;
+
+ const gchar *strid, *asset_id, *strstart, *strin, *strduration, *strrate,
+ *strtrack_types, *strtype, *metadatas = NULL, *properties =
+ NULL, *children_properties = NULL, *strlayer_prio;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &strid,
+ G_MARKUP_COLLECT_STRING, "type-name", &strtype,
+ G_MARKUP_COLLECT_STRING, "start", &strstart,
+ G_MARKUP_COLLECT_STRING, "duration", &strduration,
+ G_MARKUP_COLLECT_STRING, "asset-id", &asset_id,
+ G_MARKUP_COLLECT_STRING, "track-types", &strtrack_types,
+ G_MARKUP_COLLECT_STRING, "layer-priority", &strlayer_prio,
+ COLLECT_STR_OPT, "properties", &properties,
+ COLLECT_STR_OPT, "children-properties", &children_properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas,
+ COLLECT_STR_OPT, "rate", &strrate,
+ COLLECT_STR_OPT, "inpoint", &strin, G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+ type = g_type_from_name (strtype);
+ if (!g_type_is_a (type, GES_TYPE_CLIP))
+ goto wrong_type;
+
+ errno = 0;
+ track_types = g_ascii_strtoll (strtrack_types, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ layer_prio = g_ascii_strtoll (strlayer_prio, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ if (strin) {
+ inpoint = g_ascii_strtoull (strin, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+ }
+
+ start = g_ascii_strtoull (strstart, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ duration = g_ascii_strtoull (strduration, NULL, 10);
+ if (errno)
+ goto convertion_failed;
+
+ if (properties) {
+ props = gst_structure_from_string (properties, NULL);
+ if (props == NULL)
+ goto wrong_properties;
+ }
+
+ if (children_properties) {
+ children_props = gst_structure_from_string (children_properties, NULL);
+ if (children_props == NULL)
+ goto wrong_children_properties;
+ }
+
+ ges_base_xml_formatter_add_clip (GES_BASE_XML_FORMATTER (self),
+ strid, asset_id, type, start, inpoint, duration, layer_prio,
+ track_types, props, children_props, metadatas, error);
+ if (props)
+ gst_structure_free (props);
+
+ return;
+
+wrong_properties:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Clip %s properties '%s', could no be deserialized",
+ element_name, asset_id, properties);
+ return;
+
+wrong_children_properties:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Clip %s children properties '%s', could no be deserialized",
+ element_name, asset_id, children_properties);
+ return;
+
+convertion_failed:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Wrong property type, error: %s'", element_name,
+ g_strerror (errno));
+ return;
+
+wrong_type:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', %s not a GESClip'", element_name, strtype);
+}
+
+static inline void
+_parse_binding (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ const gchar *type = NULL, *source_type = NULL, *timed_values =
+ NULL, *property_name = NULL, *mode = NULL, *track_id = NULL;
+ gchar **pairs, **tmp;
+ gchar *pair;
+ GSList *list = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "type", &type,
+ G_MARKUP_COLLECT_STRING, "source_type", &source_type,
+ G_MARKUP_COLLECT_STRING, "property", &property_name,
+ G_MARKUP_COLLECT_STRING, "mode", &mode,
+ G_MARKUP_COLLECT_STRING, "track_id", &track_id,
+ G_MARKUP_COLLECT_STRING, "values", &timed_values,
+ G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+
+ pairs = g_strsplit (timed_values, " ", 0);
+ for (tmp = pairs; tmp != NULL; tmp += 1) {
+ gchar **value_pair;
+
+ pair = *tmp;
+ if (pair == NULL)
+ break;
+ if (strlen (pair)) {
+ GstTimedValue *value;
+
+ value = g_slice_new (GstTimedValue);
+ value_pair = g_strsplit (pair, ":", 0);
+ value->timestamp = g_ascii_strtoull (value_pair[0], NULL, 10);
+ value->value = g_ascii_strtod (value_pair[1], NULL);
+ list = g_slist_append (list, value);
+ g_strfreev (value_pair);
+ }
+ }
+
+ g_strfreev (pairs);
+
+ ges_base_xml_formatter_add_control_binding (GES_BASE_XML_FORMATTER (self),
+ type,
+ source_type,
+ property_name, (gint) g_ascii_strtoll (mode, NULL, 10), track_id, list);
+}
+
+static inline void
+_parse_source (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ GstStructure *children_props = NULL;
+ const gchar *track_id = NULL, *children_properties = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "track-id", &track_id,
+ COLLECT_STR_OPT, "children-properties", &children_properties,
+ G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+
+ if (children_properties) {
+ children_props = gst_structure_from_string (children_properties, NULL);
+ if (children_props == NULL)
+ goto wrong_children_properties;
+ }
+
+ ges_base_xml_formatter_add_source (GES_BASE_XML_FORMATTER (self), track_id,
+ children_props);
+
+ if (children_props)
+ gst_structure_free (children_props);
+
+ return;
+
+wrong_children_properties:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', children properties '%s', could no be deserialized",
+ element_name, children_properties);
+}
+
+static inline void
+_parse_effect (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ GType type;
+
+ GstStructure *children_props = NULL, *props = NULL;
+ const gchar *asset_id = NULL, *strtype = NULL, *track_id =
+ NULL, *metadatas = NULL, *properties = NULL, *track_type = NULL,
+ *children_properties = NULL, *clip_id;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ COLLECT_STR_OPT, "metadatas", &metadatas,
+ G_MARKUP_COLLECT_STRING, "asset-id", &asset_id,
+ G_MARKUP_COLLECT_STRING, "clip-id", &clip_id,
+ G_MARKUP_COLLECT_STRING, "type-name", &strtype,
+ G_MARKUP_COLLECT_STRING, "track-id", &track_id,
+ COLLECT_STR_OPT, "children-properties", &children_properties,
+ COLLECT_STR_OPT, "track-type", &track_type,
+ COLLECT_STR_OPT, "properties", &properties,
+ G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+
+ type = g_type_from_name (strtype);
+ if (!g_type_is_a (type, GES_TYPE_BASE_EFFECT))
+ goto wrong_type;
+
+ if (children_properties) {
+ children_props = gst_structure_from_string (children_properties, NULL);
+ if (children_props == NULL)
+ goto wrong_children_properties;
+ }
+
+ if (properties) {
+ props = gst_structure_from_string (properties, NULL);
+ if (props == NULL)
+ goto wrong_properties;
+ }
+
+ ges_base_xml_formatter_add_track_element (GES_BASE_XML_FORMATTER (self),
+ type, asset_id, track_id, clip_id, children_props, props, metadatas,
+ error);
+
+out:
+
+ if (props)
+ gst_structure_free (props);
+ if (children_props)
+ gst_structure_free (children_props);
+
+ return;
+
+wrong_properties:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Effect %s properties '%s', could no be deserialized",
+ element_name, asset_id, properties);
+ goto out;
+
+wrong_children_properties:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', Effect %s children properties '%s', could no be deserialized",
+ element_name, asset_id, children_properties);
+ goto out;
+
+wrong_type:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "element '%s', %s not a GESBaseEffect'", element_name, strtype);
+}
+
+
+static inline void
+_parse_group (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ const gchar *id, *properties, *metadatas = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_STRING, "properties", &properties,
+ COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+
+ ges_base_xml_formatter_add_group (GES_BASE_XML_FORMATTER (self), id,
+ properties, metadatas);
+}
+
+static inline void
+_parse_group_child (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ GESXmlFormatter * self, GError ** error)
+{
+ const gchar *child_id, *name;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names,
+ attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &child_id,
+ G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_INVALID)) {
+ return;
+ }
+
+ ges_base_xml_formatter_last_group_add_child (GES_BASE_XML_FORMATTER (self),
+ child_id, name);
+}
+
+static void
+_parse_element_start (GMarkupParseContext * context, const gchar * element_name,
+ const gchar ** attribute_names, const gchar ** attribute_values,
+ gpointer self, GError ** error)
+{
+ GESXmlFormatterPrivate *priv = _GET_PRIV (self);
+
+ if (!G_UNLIKELY (priv->ges_opened))
+ _parse_ges_element (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (!G_UNLIKELY (priv->project_opened))
+ _parse_project (context, element_name, attribute_names, attribute_values,
+ self, error);
+ else if (g_strcmp0 (element_name, "encoding-profile") == 0)
+ _parse_encoding_profile (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "stream-profile") == 0)
+ _parse_stream_profile (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "timeline") == 0)
+ _parse_timeline (context, element_name, attribute_names, attribute_values,
+ self, error);
+ else if (g_strcmp0 (element_name, "asset") == 0)
+ _parse_asset (context, element_name, attribute_names, attribute_values,
+ self, error);
+ else if (g_strcmp0 (element_name, "track") == 0)
+ _parse_track (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "layer") == 0)
+ _parse_layer (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "clip") == 0)
+ _parse_clip (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "source") == 0)
+ _parse_source (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "effect") == 0)
+ _parse_effect (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "binding") == 0)
+ _parse_binding (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "group") == 0)
+ _parse_group (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else if (g_strcmp0 (element_name, "child") == 0)
+ _parse_group_child (context, element_name, attribute_names,
+ attribute_values, self, error);
+ else
+ GST_LOG_OBJECT (self, "Element %s not handled", element_name);
+}
+
+static void
+_parse_element_end (GMarkupParseContext * context,
+ const gchar * element_name, gpointer self, GError ** error)
+{
+ /*GESXmlFormatterPrivate *priv = _GET_PRIV (self); */
+ if (g_strcmp0 (element_name, "ges") == 0 && GES_FORMATTER (self)->project) {
+ gchar *version = g_strdup_printf ("%d.%d",
+ API_VERSION, GES_XML_FORMATTER (self)->priv->min_version);
+
+ ges_meta_container_set_string (GES_META_CONTAINER (GES_FORMATTER
+ (self)->project), GES_META_FORMAT_VERSION, version);
+
+ g_free (version);
+ }
+}
+
+static void
+_error_parsing (GMarkupParseContext * context, GError * error,
+ gpointer user_data)
+{
+ GST_WARNING ("Error occurred when parsing %s", error->message);
+}
+
+/***********************************************
+ * *
+ * Saving implementation *
+ * *
+ ***********************************************/
+
+/* XML writting utils */
+static inline void
+append_escaped (GString * str, gchar * tmpstr)
+{
+ g_string_append (str, tmpstr);
+ g_free (tmpstr);
+}
+
+static inline gboolean
+_can_serialize_spec (GParamSpec * spec)
+{
+ if (!(spec->flags & G_PARAM_WRITABLE)) {
+ GST_DEBUG ("%s from %s is not writable",
+ spec->name, g_type_name (spec->owner_type));
+
+ return FALSE;
+ } else if (spec->flags & G_PARAM_CONSTRUCT_ONLY) {
+ GST_DEBUG ("%s from %s is construct only",
+ spec->name, g_type_name (spec->owner_type));
+
+ return FALSE;
+ } else if (spec->flags & GES_PARAM_NO_SERIALIZATION &&
+ g_type_is_a (spec->owner_type, GES_TYPE_TIMELINE_ELEMENT)) {
+ GST_DEBUG ("%s from %s is set as GES_PARAM_NO_SERIALIZATION",
+ spec->name, g_type_name (spec->owner_type));
+
+ return FALSE;
+ } else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (spec), G_TYPE_OBJECT)) {
+ GST_DEBUG ("%s from %s contains GObject, can't serialize that.",
+ spec->name, g_type_name (spec->owner_type));
+
+ return FALSE;
+ } else if ((g_type_is_a (spec->owner_type, GST_TYPE_OBJECT) &&
+ !g_strcmp0 (spec->name, "name"))) {
+
+ GST_DEBUG ("We do not want to serialize the name of GstObjects.");
+ return FALSE;
+ } else if (G_PARAM_SPEC_VALUE_TYPE (spec) == G_TYPE_GTYPE) {
+ GST_DEBUG ("%s from %s contains a GType, can't serialize.",
+ spec->name, g_type_name (spec->owner_type));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static inline void
+_init_value_from_spec_for_serialization (GValue * value, GParamSpec * spec)
+{
+
+ if (g_type_is_a (spec->value_type, G_TYPE_ENUM) ||
+ g_type_is_a (spec->value_type, G_TYPE_FLAGS))
+ g_value_init (value, G_TYPE_INT);
+ else
+ g_value_init (value, spec->value_type);
+}
+
+static gchar *
+_serialize_properties (GObject * object, const gchar * fieldname, ...)
+{
+ gchar *ret;
+ guint n_props, j;
+ GParamSpec *spec, **pspecs;
+ GObjectClass *class = G_OBJECT_GET_CLASS (object);
+ GstStructure *structure = gst_structure_new_empty ("properties");
+
+ pspecs = g_object_class_list_properties (class, &n_props);
+ for (j = 0; j < n_props; j++) {
+ GValue val = { 0 };
+
+ spec = pspecs[j];
+ if (spec->value_type == GST_TYPE_CAPS) {
+ GstCaps *caps;
+ gchar *caps_str;
+
+ g_object_get (object, spec->name, &caps, NULL);
+ caps_str = gst_caps_to_string (caps);
+ gst_structure_set (structure, spec->name, G_TYPE_STRING, caps_str, NULL);
+ if (caps)
+ gst_caps_unref (caps);
+ g_free (caps_str);
+ } else if (_can_serialize_spec (spec)) {
+ _init_value_from_spec_for_serialization (&val, spec);
+ g_object_get_property (object, spec->name, &val);
+ gst_structure_set_value (structure, spec->name, &val);
+ g_value_unset (&val);
+ }
+ }
+ g_free (pspecs);
+
+ if (fieldname) {
+ va_list varargs;
+ va_start (varargs, fieldname);
+ gst_structure_remove_fields_valist (structure, fieldname, varargs);
+ va_end (varargs);
+ }
+
+ ret = gst_structure_to_string (structure);
+ gst_structure_free (structure);
+
+ return ret;
+}
+
+static inline void
+_save_assets (GESXmlFormatter * self, GString * str, GESProject * project)
+{
+ char *properties, *metas;
+ GESAsset *asset, *proxy;
+ GList *assets, *tmp;
+
+ assets = ges_project_list_assets (project, GES_TYPE_EXTRACTABLE);
+ for (tmp = assets; tmp; tmp = tmp->next) {
+ asset = GES_ASSET (tmp->data);
+ properties = _serialize_properties (G_OBJECT (asset), NULL);
+ metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (asset));
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <asset id='%s' extractable-type-name='%s' properties='%s' metadatas='%s' ",
+ ges_asset_get_id (asset),
+ g_type_name (ges_asset_get_extractable_type (asset)), properties,
+ metas));
+
+ /*TODO Save the whole list of proxies */
+ proxy = ges_asset_get_proxy (asset);
+ if (proxy) {
+ append_escaped (str, g_markup_printf_escaped (" proxy-id='%s' ",
+ ges_asset_get_id (proxy)));
+
+ if (!g_list_find (assets, proxy)) {
+ assets = g_list_append (assets, gst_object_ref (proxy));
+
+ if (!tmp->next)
+ tmp->next = g_list_last (assets);
+ }
+
+ self->priv->min_version = MAX (self->priv->min_version, 3);
+ }
+ g_string_append (str, "/>\n");
+ g_free (properties);
+ g_free (metas);
+ }
+ g_list_free_full (assets, gst_object_unref);
+}
+
+static inline void
+_save_tracks (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
+{
+ gchar *strtmp, *metas;
+ GESTrack *track;
+ GList *tmp, *tracks;
+ char *properties;
+
+ guint nb_tracks = 0;
+
+ tracks = ges_timeline_get_tracks (timeline);
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ track = GES_TRACK (tmp->data);
+ properties = _serialize_properties (G_OBJECT (track), NULL);
+ strtmp = gst_caps_to_string (ges_track_get_caps (track));
+ metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (track));
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <track caps='%s' track-type='%i' track-id='%i' properties='%s' metadatas='%s'/>\n",
+ strtmp, track->type, nb_tracks++, properties, metas));
+ g_free (strtmp);
+ g_free (metas);
+ g_free (properties);
+ }
+ g_list_free_full (tracks, gst_object_unref);
+}
+
+static inline void
+_save_children_properties (GString * str, GESTimelineElement * element)
+{
+ GstStructure *structure;
+ GParamSpec **pspecs, *spec;
+ guint i, n_props;
+ gchar *struct_str;
+
+ pspecs = ges_timeline_element_list_children_properties (element, &n_props);
+
+ structure = gst_structure_new_empty ("properties");
+ for (i = 0; i < n_props; i++) {
+ GValue val = { 0 };
+ spec = pspecs[i];
+
+ if (_can_serialize_spec (spec)) {
+ gchar *spec_name =
+ g_strdup_printf ("%s::%s", g_type_name (spec->owner_type),
+ spec->name);
+
+ _init_value_from_spec_for_serialization (&val, spec);
+ ges_timeline_element_get_child_property_by_pspec (element, spec, &val);
+ gst_structure_set_value (structure, spec_name, &val);
+
+ g_free (spec_name);
+ g_value_unset (&val);
+ }
+ g_param_spec_unref (spec);
+ }
+ g_free (pspecs);
+
+ struct_str = gst_structure_to_string (structure);
+ append_escaped (str,
+ g_markup_printf_escaped (" children-properties='%s'", struct_str));
+ gst_structure_free (structure);
+ g_free (struct_str);
+}
+
+/* TODO : Use this function for every track element with controllable properties */
+static inline void
+_save_keyframes (GString * str, GESTrackElement * trackelement, gint index)
+{
+ GHashTable *bindings_hashtable;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ bindings_hashtable =
+ ges_track_element_get_all_control_bindings (trackelement);
+
+ g_hash_table_iter_init (&iter, bindings_hashtable);
+
+ /* We iterate over the bindings, and save the timed values */
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (GST_IS_DIRECT_CONTROL_BINDING ((GstControlBinding *) value)) {
+ GstControlSource *source;
+ gboolean absolute = FALSE;
+ GstDirectControlBinding *binding;
+
+ binding = (GstDirectControlBinding *) value;
+
+ g_object_get (binding, "control-source", &source,
+ "absolute", &absolute, NULL);
+
+ if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
+ GList *timed_values, *tmp;
+ GstInterpolationMode mode;
+
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <binding type='%s' source_type='interpolation' property='%s'",
+ absolute ? "direct-absolute" : "direct", (gchar *) key));
+
+ g_object_get (source, "mode", &mode, NULL);
+ append_escaped (str, g_markup_printf_escaped (" mode='%d'", mode));
+ append_escaped (str, g_markup_printf_escaped (" track_id='%d'", index));
+ append_escaped (str, g_markup_printf_escaped (" values ='"));
+ timed_values =
+ gst_timed_value_control_source_get_all
+ (GST_TIMED_VALUE_CONTROL_SOURCE (source));
+ for (tmp = timed_values; tmp; tmp = tmp->next) {
+ gchar strbuf[G_ASCII_DTOSTR_BUF_SIZE];
+ GstTimedValue *value;
+
+ value = (GstTimedValue *) tmp->data;
+ append_escaped (str, g_markup_printf_escaped (" %" G_GUINT64_FORMAT
+ ":%s ", value->timestamp, g_ascii_dtostr (strbuf,
+ G_ASCII_DTOSTR_BUF_SIZE, value->value)));
+ }
+ append_escaped (str, g_markup_printf_escaped ("'/>\n"));
+ } else
+ GST_DEBUG ("control source not in [interpolation]");
+ } else
+ GST_DEBUG ("Binding type not in [direct, direct-absolute]");
+ }
+}
+
+static inline void
+_save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
+ GESTimeline * timeline)
+{
+ GESTrack *tck;
+ GList *tmp, *tracks;
+ gchar *properties, *metas;
+ guint track_id = 0;
+ gboolean serialize;
+ gchar *extractable_id;
+
+ g_object_get (trackelement, "serialize", &serialize, NULL);
+ if (!serialize) {
+
+ GST_DEBUG_OBJECT (trackelement, "Should not be serialized");
+
+ return;
+ }
+
+ tck = ges_track_element_get_track (trackelement);
+ if (tck == NULL) {
+ GST_WARNING_OBJECT (trackelement, " Not in any track, can not save it");
+
+ return;
+ }
+
+ tracks = ges_timeline_get_tracks (timeline);
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ if (tmp->data == tck)
+ break;
+ track_id++;
+ }
+ g_list_free_full (tracks, gst_object_unref);
+
+ properties = _serialize_properties (G_OBJECT (trackelement), "start",
+ "in-point", "duration", "locked", "max-duration", "name", "priority",
+ NULL);
+ metas =
+ ges_meta_container_metas_to_string (GES_META_CONTAINER (trackelement));
+ extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (trackelement));
+ append_escaped (str,
+ g_markup_printf_escaped (" <effect asset-id='%s' clip-id='%u'"
+ " type-name='%s' track-type='%i' track-id='%i' properties='%s' metadatas='%s'",
+ extractable_id, clip_id,
+ g_type_name (G_OBJECT_TYPE (trackelement)), tck->type, track_id,
+ properties, metas));
+ g_free (extractable_id);
+ g_free (properties);
+ g_free (metas);
+
+ _save_children_properties (str, GES_TIMELINE_ELEMENT (trackelement));
+ append_escaped (str, g_markup_printf_escaped (">\n"));
+
+ _save_keyframes (str, trackelement, -1);
+
+ append_escaped (str, g_markup_printf_escaped (" </effect>\n"));
+}
+
+static inline void
+_save_layers (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
+{
+ gchar *properties, *metas;
+ GESLayer *layer;
+ GESClip *clip;
+ GList *tmplayer, *tmpclip, *clips;
+ GESXmlFormatterPrivate *priv = self->priv;
+
+ for (tmplayer = timeline->layers; tmplayer; tmplayer = tmplayer->next) {
+ guint priority;
+ layer = GES_LAYER (tmplayer->data);
+
+ priority = ges_layer_get_priority (layer);
+ properties = _serialize_properties (G_OBJECT (layer), "priority", NULL);
+ metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (layer));
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <layer priority='%i' properties='%s' metadatas='%s'>\n",
+ priority, properties, metas));
+ g_free (properties);
+ g_free (metas);
+
+ clips = ges_layer_get_clips (layer);
+ for (tmpclip = clips; tmpclip; tmpclip = tmpclip->next) {
+ GList *effects, *tmpeffect;
+ GList *tmptrackelement;
+ GList *tracks;
+ gboolean serialize;
+ gchar *extractable_id;
+
+ clip = GES_CLIP (tmpclip->data);
+
+ g_object_get (clip, "serialize", &serialize, NULL);
+ if (!serialize) {
+ GST_DEBUG_OBJECT (clip, "Should not be serialized");
+ continue;
+ }
+
+ /* We escape all mandatrorry properties that are handled sparetely
+ * and vtype for StandarTransition as it is the asset ID */
+ properties = _serialize_properties (G_OBJECT (clip),
+ "supported-formats", "rate", "in-point", "start", "duration",
+ "max-duration", "priority", "vtype", "uri", NULL);
+ extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
+ metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (clip));
+ append_escaped (str,
+ g_markup_printf_escaped (" <clip id='%i' asset-id='%s'"
+ " type-name='%s' layer-priority='%i' track-types='%i' start='%"
+ G_GUINT64_FORMAT "' duration='%" G_GUINT64_FORMAT "' inpoint='%"
+ G_GUINT64_FORMAT "' rate='%d' properties='%s' metadatas='%s'",
+ priv->nbelements, extractable_id,
+ g_type_name (G_OBJECT_TYPE (clip)), priority,
+ ges_clip_get_supported_formats (clip), _START (clip),
+ _DURATION (clip), _INPOINT (clip), 0, properties, metas));
+ g_free (metas);
+
+ if (GES_IS_TRANSITION_CLIP (clip)) {
+ _save_children_properties (str, GES_TIMELINE_ELEMENT (clip));
+ self->priv->min_version = MAX (self->priv->min_version, 4);
+ }
+ g_string_append (str, ">\n");
+
+ g_free (extractable_id);
+ g_free (properties);
+
+ g_hash_table_insert (self->priv->element_id, clip,
+ GINT_TO_POINTER (priv->nbelements));
+
+
+ /* Effects must always be serialized in the right priority order.
+ * List order is guaranteed by the fact that ges_clip_get_top_effects
+ * sorts the effects. */
+ effects = ges_clip_get_top_effects (clip);
+ for (tmpeffect = effects; tmpeffect; tmpeffect = tmpeffect->next) {
+ _save_effect (str, priv->nbelements,
+ GES_TRACK_ELEMENT (tmpeffect->data), timeline);
+ }
+ g_list_free (effects);
+ tracks = ges_timeline_get_tracks (timeline);
+
+ for (tmptrackelement = GES_CONTAINER_CHILDREN (clip); tmptrackelement;
+ tmptrackelement = tmptrackelement->next) {
+ gint index;
+ gboolean serialize;
+
+ if (!GES_IS_SOURCE (tmptrackelement->data))
+ continue;
+
+ g_object_get (tmptrackelement->data, "serialize", &serialize, NULL);
+ if (!serialize) {
+ GST_DEBUG_OBJECT (tmptrackelement->data, "Should not be serialized");
+ continue;
+ }
+
+ index =
+ g_list_index (tracks,
+ ges_track_element_get_track (tmptrackelement->data));
+ append_escaped (str,
+ g_markup_printf_escaped (" <source track-id='%i'", index));
+ _save_children_properties (str, tmptrackelement->data);
+ append_escaped (str, g_markup_printf_escaped (">\n"));
+ _save_keyframes (str, tmptrackelement->data, index);
+ append_escaped (str, g_markup_printf_escaped (" </source>\n"));
+ }
+
+ g_list_free_full (tracks, gst_object_unref);
+
+ g_string_append (str, " </clip>\n");
+
+ priv->nbelements++;
+ }
+ g_list_free_full (clips, (GDestroyNotify) gst_object_unref);
+ g_string_append (str, " </layer>\n");
+ }
+}
+
+static void
+_save_group (GESXmlFormatter * self, GString * str, GList ** seen_groups,
+ GESGroup * group)
+{
+ GList *tmp;
+ gboolean serialize;
+ gchar *properties, *metadatas;
+
+ g_object_get (group, "serialize", &serialize, NULL);
+ if (!serialize) {
+
+ GST_DEBUG_OBJECT (group, "Should not be serialized");
+
+ return;
+ }
+
+ if (g_list_find (*seen_groups, group)) {
+ GST_DEBUG_OBJECT (group, "Already serialized");
+
+ return;
+ }
+
+ *seen_groups = g_list_prepend (*seen_groups, group);
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ if (GES_IS_GROUP (tmp->data)) {
+ _save_group (self, str, seen_groups,
+ GES_GROUP (GES_TIMELINE_ELEMENT (tmp->data)));
+ }
+ }
+
+ properties = _serialize_properties (G_OBJECT (group), NULL);
+
+ metadatas = ges_meta_container_metas_to_string (GES_META_CONTAINER (group));
+ self->priv->min_version = MAX (self->priv->min_version, 5);
+
+ g_string_append_printf (str,
+ " <group id='%d' properties='%s' metadatas='%s'>\n",
+ self->priv->nbelements, properties, metadatas);
+ g_free (properties);
+ g_free (metadatas);
+ g_hash_table_insert (self->priv->element_id, group,
+ GINT_TO_POINTER (self->priv->nbelements));
+ self->priv->nbelements++;
+
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ gint id = GPOINTER_TO_INT (g_hash_table_lookup (self->priv->element_id,
+ tmp->data));
+
+ g_string_append_printf (str, " <child id='%d' name='%s'/>\n", id,
+ GES_TIMELINE_ELEMENT_NAME (tmp->data));
+ }
+ g_string_append (str, " </group>\n");
+}
+
+static void
+_save_groups (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
+{
+ GList *tmp;
+ GList *seen_groups = NULL;
+
+ g_string_append (str, " <groups>\n");
+ for (tmp = ges_timeline_get_groups (timeline); tmp; tmp = tmp->next) {
+ _save_group (self, str, &seen_groups, tmp->data);
+ }
+ g_list_free (seen_groups);
+ g_string_append (str, " </groups>\n");
+}
+
+static inline void
+_save_timeline (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
+{
+ gchar *properties = NULL, *metas = NULL;
+
+ properties = _serialize_properties (G_OBJECT (timeline), "update", "name",
+ "async-handling", "message-forward", NULL);
+
+ ges_meta_container_set_uint64 (GES_META_CONTAINER (timeline), "duration",
+ ges_timeline_get_duration (timeline));
+ metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (timeline));
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <timeline properties='%s' metadatas='%s'>\n", properties, metas));
+
+ _save_tracks (self, str, timeline);
+ _save_layers (self, str, timeline);
+ _save_groups (self, str, timeline);
+
+ g_string_append (str, " </timeline>\n");
+
+ g_free (properties);
+ g_free (metas);
+}
+
+static void
+_save_stream_profiles (GESXmlFormatter * self, GString * str,
+ GstEncodingProfile * sprof, const gchar * profilename, guint id)
+{
+ gchar *tmpc;
+ GstCaps *tmpcaps;
+ const gchar *preset, *preset_name, *name, *description;
+
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <stream-profile parent='%s' id='%d' type='%s' "
+ "presence='%d' ", profilename, id,
+ gst_encoding_profile_get_type_nick (sprof),
+ gst_encoding_profile_get_presence (sprof)));
+
+ if (!gst_encoding_profile_is_enabled (sprof)) {
+ append_escaped (str, g_strdup ("enabled='0' "));
+
+ self->priv->min_version = MAX (self->priv->min_version, 2);
+ }
+
+ tmpcaps = gst_encoding_profile_get_format (sprof);
+ if (tmpcaps) {
+ tmpc = gst_caps_to_string (tmpcaps);
+ append_escaped (str, g_markup_printf_escaped ("format='%s' ", tmpc));
+ gst_caps_unref (tmpcaps);
+ g_free (tmpc);
+ }
+
+ name = gst_encoding_profile_get_name (sprof);
+ if (name)
+ append_escaped (str, g_markup_printf_escaped ("name='%s' ", name));
+
+ description = gst_encoding_profile_get_description (sprof);
+ if (description)
+ append_escaped (str, g_markup_printf_escaped ("description='%s' ",
+ description));
+
+ preset = gst_encoding_profile_get_preset (sprof);
+ if (preset) {
+ GstElement *encoder;
+
+ append_escaped (str, g_markup_printf_escaped ("preset='%s' ", preset));
+
+ encoder = get_element_for_encoding_profile (sprof,
+ GST_ELEMENT_FACTORY_TYPE_ENCODER);
+ if (encoder) {
+ if (GST_IS_PRESET (encoder) &&
+ gst_preset_load_preset (GST_PRESET (encoder), preset)) {
+
+ gchar *settings = _serialize_properties (G_OBJECT (encoder), NULL);
+ append_escaped (str,
+ g_markup_printf_escaped ("preset-properties='%s' ", settings));
+ g_free (settings);
+ }
+ gst_object_unref (encoder);
+ }
+ }
+
+ preset_name = gst_encoding_profile_get_preset_name (sprof);
+ if (preset_name)
+ append_escaped (str, g_markup_printf_escaped ("preset-name='%s' ",
+ preset_name));
+
+ tmpcaps = gst_encoding_profile_get_restriction (sprof);
+ if (tmpcaps) {
+ tmpc = gst_caps_to_string (tmpcaps);
+ append_escaped (str, g_markup_printf_escaped ("restriction='%s' ", tmpc));
+ gst_caps_unref (tmpcaps);
+ g_free (tmpc);
+ }
+
+ if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
+ GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
+
+ append_escaped (str,
+ g_markup_printf_escaped ("pass='%d' variableframerate='%i' ",
+ gst_encoding_video_profile_get_pass (vp),
+ gst_encoding_video_profile_get_variableframerate (vp)));
+ }
+
+ g_string_append (str, "/>\n");
+}
+
+static inline void
+_save_encoding_profiles (GESXmlFormatter * self, GString * str,
+ GESProject * project)
+{
+ GstCaps *profformat;
+ const gchar *profname, *profdesc, *profpreset, *proftype, *profpresetname;
+
+ const GList *tmp;
+ GList *profiles = g_list_reverse (g_list_copy ((GList *)
+ ges_project_list_encoding_profiles (project)));
+
+ for (tmp = profiles; tmp; tmp = tmp->next) {
+ GstEncodingProfile *prof = GST_ENCODING_PROFILE (tmp->data);
+
+ profname = gst_encoding_profile_get_name (prof);
+ profdesc = gst_encoding_profile_get_description (prof);
+ profpreset = gst_encoding_profile_get_preset (prof);
+ profpresetname = gst_encoding_profile_get_preset_name (prof);
+ proftype = gst_encoding_profile_get_type_nick (prof);
+
+ append_escaped (str,
+ g_markup_printf_escaped
+ (" <encoding-profile name='%s' description='%s' type='%s' ",
+ profname, profdesc, proftype));
+
+ if (profpreset) {
+ GstElement *element;
+
+ append_escaped (str, g_markup_printf_escaped ("preset='%s' ",
+ profpreset));
+
+ if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
+ element = get_element_for_encoding_profile (prof,
+ GST_ELEMENT_FACTORY_TYPE_MUXER);
+ } else {
+ element = get_element_for_encoding_profile (prof,
+ GST_ELEMENT_FACTORY_TYPE_ENCODER);
+ }
+
+ if (element) {
+ if (GST_IS_PRESET (element) &&
+ gst_preset_load_preset (GST_PRESET (element), profpreset)) {
+ gchar *settings = _serialize_properties (G_OBJECT (element), NULL);
+ append_escaped (str,
+ g_markup_printf_escaped ("preset-properties='%s' ", settings));
+ g_free (settings);
+ }
+ gst_object_unref (element);
+ }
+
+ }
+
+ if (profpresetname)
+ append_escaped (str, g_markup_printf_escaped ("preset-name='%s' ",
+ profpresetname));
+
+ profformat = gst_encoding_profile_get_format (prof);
+ if (profformat) {
+ gchar *format = gst_caps_to_string (profformat);
+ append_escaped (str, g_markup_printf_escaped ("format='%s' ", format));
+ g_free (format);
+ gst_caps_unref (profformat);
+ }
+
+ g_string_append (str, ">\n");
+
+ if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
+ guint i = 0;
+ const GList *tmp2;
+ GstEncodingContainerProfile *container_prof;
+
+ container_prof = GST_ENCODING_CONTAINER_PROFILE (prof);
+ for (tmp2 = gst_encoding_container_profile_get_profiles (container_prof);
+ tmp2; tmp2 = tmp2->next, i++) {
+ GstEncodingProfile *sprof = (GstEncodingProfile *) tmp2->data;
+ _save_stream_profiles (self, str, sprof, profname, i);
+ }
+ }
+ append_escaped (str,
+ g_markup_printf_escaped (" </encoding-profile>\n"));
+ }
+ g_list_free (profiles);
+}
+
+static GString *
+_save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
+{
+ GString *str;
+ GESProject *project;
+
+ gchar *projstr = NULL, *version;
+ gchar *properties = NULL, *metas = NULL;
+ GESXmlFormatter *self = GES_XML_FORMATTER (formatter);
+ GESXmlFormatterPrivate *priv;
+
+
+ priv = _GET_PRIV (formatter);
+
+ priv->min_version = 1;
+ project = formatter->project;
+ str = priv->str = g_string_new (NULL);
+
+ properties = _serialize_properties (G_OBJECT (project), NULL);
+ metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (project));
+ append_escaped (str,
+ g_markup_printf_escaped (" <project properties='%s' metadatas='%s'>\n",
+ properties, metas));
+ g_free (properties);
+ g_free (metas);
+
+ g_string_append (str, " <encoding-profiles>\n");
+ _save_encoding_profiles (GES_XML_FORMATTER (formatter), str, project);
+ g_string_append (str, " </encoding-profiles>\n");
+
+ g_string_append (str, " <ressources>\n");
+ _save_assets (self, str, project);
+ g_string_append (str, " </ressources>\n");
+
+ _save_timeline (self, str, timeline);
+ g_string_append (str, "</project>\n</ges>");
+
+ projstr = g_strdup_printf ("<ges version='%i.%i'>\n", API_VERSION,
+ priv->min_version);
+ g_string_prepend (str, projstr);
+ g_free (projstr);
+
+ ges_meta_container_set_int (GES_META_CONTAINER (project),
+ GES_META_FORMAT_VERSION, priv->min_version);
+
+ version = g_strdup_printf ("%d.%d", API_VERSION,
+ GES_XML_FORMATTER (formatter)->priv->min_version);
+
+ ges_meta_container_set_string (GES_META_CONTAINER (project),
+ GES_META_FORMAT_VERSION, version);
+
+ g_free (version);
+
+ priv->str = NULL;
+
+ return str;
+}
+
+/***********************************************
+ * *
+ * GObject virtual methods implementation *
+ * *
+ ***********************************************/
+
+static void
+_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+}
+
+static void
+_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+}
+
+static void
+ges_xml_formatter_init (GESXmlFormatter * self)
+{
+ GESXmlFormatterPrivate *priv = ges_xml_formatter_get_instance_private (self);
+
+ priv->project_opened = FALSE;
+ priv->element_id = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ self->priv = priv;
+ self->priv->min_version = 1;
+}
+
+static void
+_dispose (GObject * object)
+{
+ g_clear_pointer (&GES_XML_FORMATTER (object)->priv->element_id,
+ g_hash_table_unref);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+ges_xml_formatter_class_init (GESXmlFormatterClass * self_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (self_class);
+ GESBaseXmlFormatterClass *basexmlformatter_class;
+
+ basexmlformatter_class = GES_BASE_XML_FORMATTER_CLASS (self_class);
+
+ object_class->get_property = _get_property;
+ object_class->set_property = _set_property;
+ object_class->dispose = _dispose;
+
+ basexmlformatter_class->content_parser.start_element = _parse_element_start;
+ basexmlformatter_class->content_parser.end_element = _parse_element_end;
+ basexmlformatter_class->content_parser.text = NULL;
+ basexmlformatter_class->content_parser.passthrough = NULL;
+ basexmlformatter_class->content_parser.error = _error_parsing;
+
+ ges_formatter_class_register_metas (GES_FORMATTER_CLASS (self_class),
+ "ges", "GStreamer Editing Services project files",
+ "xges", "application/ges", VERSION, GST_RANK_PRIMARY);
+
+ basexmlformatter_class->save = _save;
+}
+
+#undef COLLECT_STR_OPT
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+
+#include "ges-base-xml-formatter.h"
+
+#ifndef GES_XML_FORMATTER_H
+#define GES_XML_FORMATTER_H
+
+G_BEGIN_DECLS
+#define GES_TYPE_XML_FORMATTER (ges_xml_formatter_get_type ())
+#define GES_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_XML_FORMATTER, GESXmlFormatter))
+#define GES_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_XML_FORMATTER, GESXmlFormatterClass))
+#define GES_IS_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_XML_FORMATTER))
+#define GES_IS_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_XML_FORMATTER))
+#define GES_XML_FORMATTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_XML_FORMATTER, GESXmlFormatterClass))
+typedef struct _GESXmlFormatterPrivate GESXmlFormatterPrivate;
+
+typedef struct
+{
+ GESBaseXmlFormatter parent;
+
+ GESXmlFormatterPrivate *priv;
+
+ gpointer _ges_reserved[GES_PADDING];
+} GESXmlFormatter;
+
+typedef struct
+{
+ GESBaseXmlFormatterClass parent;
+
+ gpointer _ges_reserved[GES_PADDING];
+} GESXmlFormatterClass;
+
+GES_API
+GType ges_xml_formatter_get_type (void);
+
+G_END_DECLS
+#endif /* _GES_XML_FORMATTER_H */
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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.
+ */
+
+/* TODO
+ * Add a deinit function
+ *
+ * Do not forget to
+ * + g_ptr_array_unref (new_paths);
+ * + g_hash_table_unref (tried_uris);
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <ges/ges.h>
+#include "ges/gstframepositioner.h"
+#include "ges-internal.h"
+
+#ifndef DISABLE_XPTV
+#include <ges/ges-pitivi-formatter.h>
+#endif
+
+#define GES_GNONLIN_VERSION_NEEDED_MAJOR 1
+#define GES_GNONLIN_VERSION_NEEDED_MINOR 2
+#define GES_GNONLIN_VERSION_NEEDED_MICRO 0
+
+GST_DEBUG_CATEGORY (_ges_debug_category);
+
+G_LOCK_DEFINE_STATIC (init_lock);
+
+/* (without holding ref) thread object for thread_self() validation
+ * between init/deinit
+ */
+static GThread *initialized_thread = NULL;
+
+#ifndef GST_DISABLE_GST_DEBUG
+static gpointer
+init_debug_category (gpointer data)
+{
+ /* initialize debugging category */
+ GST_DEBUG_CATEGORY_INIT (_ges_debug_category, "ges", GST_DEBUG_FG_YELLOW,
+ "GStreamer Editing Services");
+
+ return _ges_debug_category;
+}
+
+GstDebugCategory *
+_ges_debug (void)
+{
+ static GOnce my_once = G_ONCE_INIT;
+
+ g_once (&my_once, init_debug_category, NULL);
+
+ return my_once.retval;
+}
+#else
+GstDebugCategory *
+_ges_debug (void)
+{
+ return NULL;
+}
+
+#endif
+
+static gboolean
+ges_init_pre (GOptionContext * context, GOptionGroup * group, gpointer data,
+ GError ** error)
+{
+ if (initialized_thread) {
+ GST_DEBUG ("already initialized");
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
+ GError ** error)
+{
+ GESUriClipAssetClass *uriasset_klass = NULL;
+ GstElementFactory *nlecomposition_factory = NULL;
+
+ if (initialized_thread) {
+ GST_DEBUG ("already initialized ges");
+ return TRUE;
+ }
+
+ uriasset_klass = g_type_class_ref (GES_TYPE_URI_CLIP_ASSET);
+
+ if (!_ges_uri_asset_ensure_setup (uriasset_klass)) {
+ GST_ERROR ("cannot setup uri asset");
+ goto failed;
+ }
+
+ nlecomposition_factory = gst_element_factory_find ("nlecomposition");
+ if (!nlecomposition_factory) {
+ GST_ERROR ("The `nlecomposition` object was not found.");
+ if (error)
+ *error = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
+ "The `nle` plugin is missing.");
+
+ goto failed;
+ }
+ gst_object_unref (nlecomposition_factory);
+
+
+
+ /* register clip classes with the system */
+
+ g_type_class_ref (GES_TYPE_TEST_CLIP);
+ g_type_class_ref (GES_TYPE_URI_CLIP);
+ g_type_class_ref (GES_TYPE_TITLE_CLIP);
+ g_type_class_ref (GES_TYPE_TRANSITION_CLIP);
+ g_type_class_ref (GES_TYPE_OVERLAY_CLIP);
+ g_type_class_ref (GES_TYPE_OVERLAY_TEXT_CLIP);
+
+ g_type_class_ref (GES_TYPE_GROUP);
+
+ /* register formatter types with the system */
+#ifndef DISABLE_XPTV
+ g_type_class_ref (GES_TYPE_PITIVI_FORMATTER);
+#endif
+ g_type_class_ref (GES_TYPE_COMMAND_LINE_FORMATTER);
+ g_type_class_ref (GES_TYPE_XML_FORMATTER);
+
+ /* Register track elements */
+ g_type_class_ref (GES_TYPE_EFFECT);
+
+ ges_asset_cache_init ();
+
+ gst_element_register (NULL, "framepositioner", 0, GST_TYPE_FRAME_POSITIONNER);
+ gst_element_register (NULL, "gespipeline", 0, GES_TYPE_PIPELINE);
+
+ /* TODO: user-defined types? */
+ initialized_thread = g_thread_self ();
+ g_type_class_unref (uriasset_klass);
+
+ GST_DEBUG ("GStreamer Editing Services initialized");
+
+ return TRUE;
+
+failed:
+ if (uriasset_klass)
+ g_type_class_unref (uriasset_klass);
+
+ GST_ERROR ("Could not initialize GES.");
+
+ return FALSE;
+}
+
+/**
+ * ges_init:
+ *
+ * Initialize the GStreamer Editing Service. Call this before any usage of
+ * GES. You should take care of initilizing GStreamer before calling this
+ * function.
+ *
+ * MT safety.
+ * GStreamer Editing Services do not guarantee MT safety.
+ * An application is required to use GES APIs (including ges_deinit())
+ * in the thread where ges_init() was called.
+ */
+
+gboolean
+ges_init (void)
+{
+ gboolean ret;
+
+ G_LOCK (init_lock);
+ ges_init_pre (NULL, NULL, NULL, NULL);
+
+ ret = ges_init_post (NULL, NULL, NULL, NULL);
+ G_UNLOCK (init_lock);
+
+ return ret;
+}
+
+/**
+ * ges_deinit:
+ *
+ * Clean up any resources created by GES in ges_init().
+ *
+ * It is normally not needed to call this function in a normal application as the
+ * resources will automatically be freed when the program terminates.
+ * This function is therefore mostly used by testsuites and other memory profiling tools.
+ * This function should be called from the thread where ges_init() was called.
+ *
+ * After this call GES should not be used until another ges_init() call.
+ */
+void
+ges_deinit (void)
+{
+ G_LOCK (init_lock);
+
+ GST_INFO ("deinitializing GES");
+
+ if (!initialized_thread) {
+ GST_DEBUG ("nothing to deinitialize");
+ G_UNLOCK (init_lock);
+ return;
+ }
+
+ /* Allow deinit only from a thread where ges_init() was called */
+ g_assert (initialized_thread == g_thread_self ());
+
+ _ges_uri_asset_cleanup ();
+
+ g_type_class_unref (g_type_class_peek (GES_TYPE_TEST_CLIP));
+ g_type_class_unref (g_type_class_peek (GES_TYPE_URI_CLIP));
+ g_type_class_unref (g_type_class_peek (GES_TYPE_TITLE_CLIP));
+ g_type_class_unref (g_type_class_peek (GES_TYPE_TRANSITION_CLIP));
+ g_type_class_unref (g_type_class_peek (GES_TYPE_OVERLAY_CLIP));
+ g_type_class_unref (g_type_class_peek (GES_TYPE_OVERLAY_TEXT_CLIP));
+
+ g_type_class_unref (g_type_class_peek (GES_TYPE_GROUP));
+
+ /* register formatter types with the system */
+#ifndef DISABLE_XPTV
+ g_type_class_unref (g_type_class_peek (GES_TYPE_PITIVI_FORMATTER));
+#endif
+
+ g_type_class_unref (g_type_class_peek (GES_TYPE_COMMAND_LINE_FORMATTER));
+ g_type_class_unref (g_type_class_peek (GES_TYPE_XML_FORMATTER));
+
+ /* Register track elements */
+ g_type_class_unref (g_type_class_peek (GES_TYPE_EFFECT));
+
+ ges_asset_cache_deinit ();
+
+ initialized_thread = NULL;
+ G_UNLOCK (init_lock);
+
+ GST_INFO ("deinitialized GES");
+
+ return;
+}
+
+#ifndef GST_DISABLE_OPTION_PARSING
+static gboolean
+parse_goption_arg (const gchar * s_opt,
+ const gchar * arg, gpointer data, GError ** err)
+{
+ if (g_strcmp0 (s_opt, "--ges-version") == 0) {
+ g_print ("GStreamer Editing Services version %s\n", PACKAGE_VERSION);
+ exit (0);
+ } else if (g_strcmp0 (s_opt, "--ges-sample-paths") == 0) {
+ ges_add_missing_uri_relocation_uri (arg, FALSE);
+ } else if (g_strcmp0 (s_opt, "--ges-sample-path-recurse") == 0) {
+ ges_add_missing_uri_relocation_uri (arg, TRUE);
+ }
+
+ return TRUE;
+}
+#endif
+
+/**
+ * ges_init_get_option_group: (skip)
+ *
+ * Returns a #GOptionGroup with GES's argument specifications. The
+ * group is set up to use standard GOption callbacks, so when using this
+ * group in combination with GOption parsing methods, all argument parsing
+ * and initialization is automated.
+ *
+ * This function is useful if you want to integrate GES with other
+ * libraries that use GOption (see g_option_context_add_group() ).
+ *
+ * If you use this function, you should make sure you initialise the GStreamer
+ * as one of the very first things in your program. That means you need to
+ * use gst_init_get_option_group() and add it to the option context before
+ * using the ges_init_get_option_group() result.
+ *
+ * Returns: (transfer full): a pointer to GES's option group.
+ */
+GOptionGroup *
+ges_init_get_option_group (void)
+{
+#ifndef GST_DISABLE_OPTION_PARSING
+
+ GOptionGroup *group;
+ static const GOptionEntry ges_args[] = {
+ {"ges-version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ "Print the GStreamer Editing Services version",
+ NULL},
+ {"ges-sample-paths", 0, 0, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ "List of pathes to look assets in if they were moved"},
+ {"ges-sample-path-recurse", 0, 0, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ "Same as above, but recursing into the folder"},
+ {NULL}
+ };
+
+ group = g_option_group_new ("GES", "GStreamer Editing Services Options",
+ "Show GES Options", NULL, NULL);
+ g_option_group_set_parse_hooks (group, (GOptionParseFunc) ges_init_pre,
+ (GOptionParseFunc) ges_init_post);
+ g_option_group_add_entries (group, ges_args);
+
+ return group;
+
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ges_version:
+ * @major: (out): pointer to a guint to store the major version number
+ * @minor: (out): pointer to a guint to store the minor version number
+ * @micro: (out): pointer to a guint to store the micro version number
+ * @nano: (out): pointer to a guint to store the nano version number
+ *
+ * Gets the version number of the GStreamer Editing Services library.
+ */
+void
+ges_version (guint * major, guint * minor, guint * micro, guint * nano)
+{
+ g_return_if_fail (major);
+ g_return_if_fail (minor);
+ g_return_if_fail (micro);
+ g_return_if_fail (nano);
+
+ *major = GES_VERSION_MAJOR;
+ *minor = GES_VERSION_MINOR;
+ *micro = GES_VERSION_MICRO;
+ *nano = GES_VERSION_NANO;
+}
+
+/**
+ * ges_init_check:
+ * @argc: (inout) (allow-none): pointer to application's argc
+ * @argv: (inout) (array length=argc) (allow-none): pointer to application's argv
+ * @err: pointer to a #GError to which a message will be posted on error
+ *
+ * Initializes the GStreamer Editing Services library, setting up internal path lists,
+ * and loading evrything needed.
+ *
+ * This function will return %FALSE if GES could not be initialized
+ * for some reason.
+ *
+ * Returns: %TRUE if GES could be initialized.
+ */
+gboolean
+ges_init_check (int *argc, char **argv[], GError ** err)
+{
+#ifndef GST_DISABLE_OPTION_PARSING
+ GOptionGroup *group;
+ GOptionContext *ctx;
+#endif
+ gboolean res;
+
+ G_LOCK (init_lock);
+
+ if (initialized_thread) {
+ GST_DEBUG ("already initialized ges");
+ G_UNLOCK (init_lock);
+ return TRUE;
+ }
+#ifndef GST_DISABLE_OPTION_PARSING
+ ctx = g_option_context_new ("- GStreamer Editing Services initialization");
+ g_option_context_set_ignore_unknown_options (ctx, TRUE);
+ g_option_context_set_help_enabled (ctx, FALSE);
+ group = ges_init_get_option_group ();
+ g_option_context_add_group (ctx, group);
+ res = g_option_context_parse (ctx, argc, argv, err);
+ g_option_context_free (ctx);
+#endif
+
+ if (!res) {
+ G_UNLOCK (init_lock);
+ return res;
+ }
+
+ ges_init_pre (NULL, NULL, NULL, NULL);
+ res = ges_init_post (NULL, NULL, NULL, NULL);
+
+ G_UNLOCK (init_lock);
+
+ return res;
+}
+
+/**
+ * ges_is_initialized:
+ *
+ * Use this function to check if GES has been initialized with ges_init()
+ * or ges_init_check().
+ *
+ * Returns: %TRUE if initialization has been done, %FALSE otherwise.
+ *
+ * Since: 1.16
+ */
+gboolean
+ges_is_initialized (void)
+{
+ return initialized_thread ? TRUE : FALSE;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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 __GES_H__
+#define __GES_H__
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#include <ges/ges-prelude.h>
+#include <ges/ges-types.h>
+#include <ges/ges-enums.h>
+
+#include <ges/ges-timeline.h>
+#include <ges/ges-layer.h>
+#include <ges/ges-timeline-element.h>
+#include <ges/ges-clip.h>
+#include <ges/ges-pipeline.h>
+#include <ges/ges-source-clip.h>
+#include <ges/ges-test-clip.h>
+#include <ges/ges-title-clip.h>
+#include <ges/ges-operation-clip.h>
+#include <ges/ges-base-effect-clip.h>
+#include <ges/ges-overlay-clip.h>
+#include <ges/ges-text-overlay-clip.h>
+#include <ges/ges-base-transition-clip.h>
+#include <ges/ges-transition-clip.h>
+#include <ges/ges-effect-clip.h>
+#include <ges/ges-base-effect-clip.h>
+#include <ges/ges-uri-clip.h>
+#include <ges/ges-group.h>
+#include <ges/ges-screenshot.h>
+#include <ges/ges-asset.h>
+#include <ges/ges-clip-asset.h>
+#include <ges/ges-track-element-asset.h>
+#include <ges/ges-uri-asset.h>
+#include <ges/ges-project.h>
+#include <ges/ges-extractable.h>
+#include <ges/ges-base-xml-formatter.h>
+#include <ges/ges-xml-formatter.h>
+
+#include <ges/ges-track.h>
+#include <ges/ges-track-element.h>
+#include <ges/ges-source.h>
+#include <ges/ges-operation.h>
+
+#include <ges/ges-video-uri-source.h>
+#include <ges/ges-audio-uri-source.h>
+#include <ges/ges-image-source.h>
+#include <ges/ges-multi-file-source.h>
+#include <ges/ges-video-test-source.h>
+#include <ges/ges-audio-test-source.h>
+#include <ges/ges-title-source.h>
+#include <ges/ges-text-overlay.h>
+#include <ges/ges-transition.h>
+#include <ges/ges-video-transition.h>
+#include <ges/ges-audio-transition.h>
+#include <ges/ges-base-effect.h>
+#include <ges/ges-effect-asset.h>
+#include <ges/ges-effect.h>
+#include <ges/ges-formatter.h>
+#include <ges/ges-command-line-formatter.h>
+#include <ges/ges-utils.h>
+#include <ges/ges-meta-container.h>
+#include <ges/ges-gerror.h>
+#include <ges/ges-audio-track.h>
+#include <ges/ges-video-track.h>
+#include <ges/ges-version.h>
+
+G_BEGIN_DECLS
+
+
+GES_API
+gboolean ges_init (void);
+
+GES_API
+gboolean ges_init_check (int *argc, char **argv[], GError ** err);
+
+GES_API
+void ges_deinit (void);
+
+GES_API
+void ges_version (guint * major,
+ guint * minor,
+ guint * micro,
+ guint * nano);
+GES_API GOptionGroup *
+ges_init_get_option_group (void);
+
+GES_API
+gboolean ges_validate_register_action_types (void);
+
+GES_API
+gboolean ges_is_initialized (void);
+
+G_END_DECLS
+
+#endif /* __GES_H__ */
--- /dev/null
+VOID:OBJECT
+VOID:OBJECT,INT,INT
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2013 Mathieu Duponchelle <mduponchelle1@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstframepositioner.h"
+
+/* We need to define a max number of pixel so we can interpolate them */
+#define MAX_PIXELS 100000
+#define MIN_PIXELS -100000
+
+static void gst_frame_positioner_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec);
+static void gst_frame_positioner_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec);
+static GstFlowReturn gst_frame_positioner_transform_ip (GstBaseTransform *
+ trans, GstBuffer * buf);
+
+static gboolean
+gst_frame_positioner_meta_init (GstMeta * meta, gpointer params,
+ GstBuffer * buffer);
+static gboolean gst_frame_positioner_meta_transform (GstBuffer * dest,
+ GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data);
+
+enum
+{
+ PROP_0,
+ PROP_ALPHA,
+ PROP_POSX,
+ PROP_POSY,
+ PROP_ZORDER,
+ PROP_WIDTH,
+ PROP_HEIGHT
+};
+
+static GstStaticPadTemplate gst_frame_positioner_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw")
+ );
+
+static GstStaticPadTemplate gst_frame_positioner_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw")
+ );
+
+G_DEFINE_TYPE (GstFramePositioner, gst_frame_positioner,
+ GST_TYPE_BASE_TRANSFORM);
+
+static void
+_weak_notify_cb (GstFramePositioner * pos, GObject * old)
+{
+ pos->current_track = NULL;
+}
+
+static void
+gst_frame_positioner_update_properties (GstFramePositioner * pos,
+ gboolean track_mixing, gint old_track_width, gint old_track_height)
+{
+ GstCaps *caps;
+
+ if (pos->capsfilter == NULL)
+ return;
+
+ if (pos->track_width && pos->track_height &&
+ (!track_mixing || !pos->scale_in_compositor)) {
+ caps =
+ gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT,
+ pos->track_width, "height", G_TYPE_INT, pos->track_height, NULL);
+ } else {
+ caps = gst_caps_new_empty_simple ("video/x-raw");
+ }
+
+ if (pos->fps_n != -1)
+ gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, pos->fps_n,
+ pos->fps_d, NULL);
+
+ if (pos->par_n != -1)
+ gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+ pos->par_n, pos->par_d, NULL);
+
+ if (old_track_width && pos->width == old_track_width &&
+ old_track_height && pos->height == old_track_height &&
+ pos->track_height && pos->track_width &&
+ ((float) old_track_width / (float) old_track_height) ==
+ ((float) pos->track_width / (float) pos->track_height)) {
+
+ GST_DEBUG_OBJECT (pos, "Following track size width old_track: %d -- pos: %d"
+ " || height, old_track %d -- pos: %d",
+ old_track_width, pos->width, old_track_height, pos->height);
+
+ pos->width = pos->track_width;
+ pos->height = pos->track_height;
+ }
+
+ GST_DEBUG_OBJECT (caps, "setting caps");
+
+ g_object_set (pos->capsfilter, "caps", caps, NULL);
+
+ gst_caps_unref (caps);
+}
+
+static void
+sync_properties_from_track (GstFramePositioner * pos, GESTrack * track)
+{
+ gint width, height;
+ gint old_track_width, old_track_height;
+ GstCaps *caps;
+
+ g_object_get (track, "restriction-caps", &caps, NULL);
+
+ width = height = 0;
+
+ if (caps && gst_caps_get_size (caps) > 0) {
+ GstStructure *structure;
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (!gst_structure_get_int (structure, "width", &width))
+ width = 0;
+ if (!gst_structure_get_int (structure, "height", &height))
+ height = 0;
+ if (!gst_structure_get_fraction (structure, "framerate", &(pos->fps_n),
+ &(pos->fps_d)))
+ pos->fps_n = -1;
+
+ if (!gst_structure_get_fraction (structure, "pixel-aspect-ratio",
+ &(pos->par_n), &(pos->par_d)))
+ pos->par_n = -1;
+ }
+
+ old_track_width = pos->track_width;
+ old_track_height = pos->track_height;
+
+ pos->track_width = width;
+ pos->track_height = height;
+
+ GST_DEBUG_OBJECT (pos, "syncing framerate from caps : %d/%d", pos->fps_n,
+ pos->fps_d);
+ if (caps)
+ gst_caps_unref (caps);
+
+ gst_frame_positioner_update_properties (pos, ges_track_get_mixing (track),
+ old_track_width, old_track_height);
+}
+
+static void
+_track_restriction_changed_cb (GESTrack * track, GParamSpec * arg G_GNUC_UNUSED,
+ GstFramePositioner * pos)
+{
+ sync_properties_from_track (pos, track);
+}
+
+static void
+set_track (GstFramePositioner * pos)
+{
+ GESTrack *new_track;
+
+ if (pos->current_track) {
+ g_signal_handlers_disconnect_by_func (pos->current_track,
+ (GCallback) _track_restriction_changed_cb, pos);
+ g_object_weak_unref (G_OBJECT (pos->current_track),
+ (GWeakNotify) _weak_notify_cb, pos);
+ }
+
+ new_track = ges_track_element_get_track (pos->track_source);
+ if (new_track) {
+ pos->current_track = new_track;
+ g_object_weak_ref (G_OBJECT (new_track), (GWeakNotify) _weak_notify_cb,
+ pos);
+ GST_DEBUG_OBJECT (pos, "connecting to track : %p", pos->current_track);
+
+ g_signal_connect (pos->current_track, "notify::restriction-caps",
+ (GCallback) _track_restriction_changed_cb, pos);
+ sync_properties_from_track (pos, pos->current_track);
+ } else {
+ pos->current_track = NULL;
+ }
+}
+
+static void
+_track_changed_cb (GESTrackElement * trksrc, GParamSpec * arg G_GNUC_UNUSED,
+ GstFramePositioner * pos)
+{
+ set_track (pos);
+}
+
+static void
+_trk_element_weak_notify_cb (GstFramePositioner * pos, GObject * old)
+{
+ pos->track_source = NULL;
+ gst_object_unref (pos);
+}
+
+void
+ges_frame_positioner_set_source_and_filter (GstFramePositioner * pos,
+ GESTrackElement * trksrc, GstElement * capsfilter)
+{
+ pos->track_source = trksrc;
+ pos->capsfilter = capsfilter;
+ pos->current_track = ges_track_element_get_track (trksrc);
+
+ g_object_weak_ref (G_OBJECT (trksrc),
+ (GWeakNotify) _trk_element_weak_notify_cb, gst_object_ref (pos));
+ g_signal_connect (trksrc, "notify::track", (GCallback) _track_changed_cb,
+ pos);
+ set_track (pos);
+}
+
+static void
+gst_frame_positioner_dispose (GObject * object)
+{
+ GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
+
+ if (pos->track_source) {
+ g_signal_handlers_disconnect_by_func (pos->track_source, _track_changed_cb,
+ pos);
+ pos->track_source = NULL;
+ }
+
+ if (pos->current_track) {
+ g_signal_handlers_disconnect_by_func (pos->current_track,
+ _track_restriction_changed_cb, pos);
+ g_object_weak_unref (G_OBJECT (pos->current_track),
+ (GWeakNotify) _weak_notify_cb, pos);
+ pos->current_track = NULL;
+ }
+
+ G_OBJECT_CLASS (gst_frame_positioner_parent_class)->dispose (object);
+}
+
+static void
+gst_frame_positioner_class_init (GstFramePositionerClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstBaseTransformClass *base_transform_class =
+ GST_BASE_TRANSFORM_CLASS (klass);
+
+ gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
+ &gst_frame_positioner_src_template);
+ gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
+ &gst_frame_positioner_sink_template);
+
+ gobject_class->set_property = gst_frame_positioner_set_property;
+ gobject_class->get_property = gst_frame_positioner_get_property;
+ gobject_class->dispose = gst_frame_positioner_dispose;
+ base_transform_class->transform_ip =
+ GST_DEBUG_FUNCPTR (gst_frame_positioner_transform_ip);
+
+ /**
+ * gstframepositioner:alpha:
+ *
+ * The desired alpha for the stream.
+ */
+ g_object_class_install_property (gobject_class, PROP_ALPHA,
+ g_param_spec_double ("alpha", "alpha", "alpha of the stream",
+ 0.0, 1.0, 1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+
+ /**
+ * gstframepositioner:posx:
+ *
+ * The desired x position for the stream.
+ */
+ g_object_class_install_property (gobject_class, PROP_POSX,
+ g_param_spec_int ("posx", "posx", "x position of the stream",
+ MIN_PIXELS, MAX_PIXELS, 0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+
+
+ /**
+ * gstframepositioner:posy:
+ *
+ * The desired y position for the stream.
+ */
+ g_object_class_install_property (gobject_class, PROP_POSY,
+ g_param_spec_int ("posy", "posy", "y position of the stream",
+ MIN_PIXELS, MAX_PIXELS, 0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+
+ /**
+ * gstframepositioner:zorder:
+ *
+ * The desired z order for the stream.
+ */
+ g_object_class_install_property (gobject_class, PROP_ZORDER,
+ g_param_spec_uint ("zorder", "zorder", "z order of the stream",
+ 0, G_MAXUINT, 0, G_PARAM_READWRITE));
+
+ /**
+ * gesframepositioner:width:
+ *
+ * The desired width for that source.
+ * Set to 0 if size is not mandatory, will be set to width of the current track.
+ */
+ g_object_class_install_property (gobject_class, PROP_WIDTH,
+ g_param_spec_int ("width", "width", "width of the source",
+ 0, MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+
+ /**
+ * gesframepositioner:height:
+ *
+ * The desired height for that source.
+ * Set to 0 if size is not mandatory, will be set to height of the current track.
+ */
+ g_object_class_install_property (gobject_class, PROP_HEIGHT,
+ g_param_spec_int ("height", "height", "height of the source",
+ 0, MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+
+ gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
+ "frame positioner", "Metadata",
+ "This element provides with tagging facilities",
+ "mduponchelle1@gmail.com");
+}
+
+static void
+gst_frame_positioner_init (GstFramePositioner * framepositioner)
+{
+ framepositioner->alpha = 1.0;
+ framepositioner->posx = 0.0;
+ framepositioner->posy = 0.0;
+ framepositioner->zorder = 0;
+ framepositioner->width = 0;
+ framepositioner->height = 0;
+ framepositioner->fps_n = -1;
+ framepositioner->fps_d = -1;
+ framepositioner->track_width = 0;
+ framepositioner->track_height = 0;
+ framepositioner->capsfilter = NULL;
+ framepositioner->track_source = NULL;
+ framepositioner->current_track = NULL;
+ framepositioner->scale_in_compositor = TRUE;
+
+ framepositioner->par_n = -1;
+ framepositioner->par_d = 1;
+}
+
+void
+gst_frame_positioner_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstFramePositioner *framepositioner = GST_FRAME_POSITIONNER (object);
+ gboolean track_mixing = TRUE;
+
+ if (framepositioner->current_track)
+ track_mixing = ges_track_get_mixing (framepositioner->current_track);
+
+
+ GST_OBJECT_LOCK (framepositioner);
+ switch (property_id) {
+ case PROP_ALPHA:
+ framepositioner->alpha = g_value_get_double (value);
+ break;
+ case PROP_POSX:
+ framepositioner->posx = g_value_get_int (value);
+ break;
+ case PROP_POSY:
+ framepositioner->posy = g_value_get_int (value);
+ break;
+ case PROP_ZORDER:
+ framepositioner->zorder = g_value_get_uint (value);
+ break;
+ case PROP_WIDTH:
+ framepositioner->width = g_value_get_int (value);
+ gst_frame_positioner_update_properties (framepositioner, track_mixing,
+ 0, 0);
+ break;
+ case PROP_HEIGHT:
+ framepositioner->height = g_value_get_int (value);
+ gst_frame_positioner_update_properties (framepositioner, track_mixing,
+ 0, 0);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK (framepositioner);
+}
+
+void
+gst_frame_positioner_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
+ gint real_width, real_height;
+
+ GST_DEBUG_OBJECT (pos, "get_property");
+
+ switch (property_id) {
+ case PROP_ALPHA:
+ g_value_set_double (value, pos->alpha);
+ break;
+ case PROP_POSX:
+ g_value_set_int (value, pos->posx);
+ break;
+ case PROP_POSY:
+ g_value_set_int (value, pos->posy);
+ break;
+ case PROP_ZORDER:
+ g_value_set_uint (value, pos->zorder);
+ break;
+ case PROP_WIDTH:
+ real_width = (pos->width > 0) ? pos->width : pos->track_width;
+ g_value_set_int (value, real_width);
+ break;
+ case PROP_HEIGHT:
+ real_height = (pos->height > 0) ? pos->height : pos->track_height;
+ g_value_set_int (value, real_height);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+GType
+gst_frame_positioner_meta_api_get_type (void)
+{
+ static volatile GType type;
+ static const gchar *tags[] = { "video", NULL };
+
+ if (g_once_init_enter (&type)) {
+ GType _type = gst_meta_api_type_register ("GstFramePositionerApi", tags);
+ g_once_init_leave (&type, _type);
+ }
+ return type;
+}
+
+static const GstMetaInfo *
+gst_frame_positioner_get_info (void)
+{
+ static const GstMetaInfo *meta_info = NULL;
+
+ if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
+ const GstMetaInfo *meta =
+ gst_meta_register (gst_frame_positioner_meta_api_get_type (),
+ "GstFramePositionerMeta",
+ sizeof (GstFramePositionerMeta), gst_frame_positioner_meta_init,
+ NULL,
+ gst_frame_positioner_meta_transform);
+ g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta);
+ }
+ return meta_info;
+}
+
+static gboolean
+gst_frame_positioner_meta_init (GstMeta * meta, gpointer params,
+ GstBuffer * buffer)
+{
+ GstFramePositionerMeta *smeta;
+
+ smeta = (GstFramePositionerMeta *) meta;
+
+ smeta->alpha = 0.0;
+ smeta->posx = smeta->posy = smeta->height = smeta->width = 0;
+ smeta->zorder = 0;
+
+ return TRUE;
+}
+
+static gboolean
+gst_frame_positioner_meta_transform (GstBuffer * dest, GstMeta * meta,
+ GstBuffer * buffer, GQuark type, gpointer data)
+{
+ GstFramePositionerMeta *dmeta, *smeta;
+
+ smeta = (GstFramePositionerMeta *) meta;
+
+ if (GST_META_TRANSFORM_IS_COPY (type)) {
+ /* only copy if the complete data is copied as well */
+ dmeta =
+ (GstFramePositionerMeta *) gst_buffer_add_meta (dest,
+ gst_frame_positioner_get_info (), NULL);
+ dmeta->alpha = smeta->alpha;
+ dmeta->posx = smeta->posx;
+ dmeta->posy = smeta->posy;
+ dmeta->width = smeta->width;
+ dmeta->height = smeta->height;
+ dmeta->zorder = smeta->zorder;
+ }
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_frame_positioner_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
+{
+ GstFramePositionerMeta *meta;
+ GstFramePositioner *framepositioner = GST_FRAME_POSITIONNER (trans);
+ GstClockTime timestamp = GST_BUFFER_PTS (buf);
+
+ if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ gst_object_sync_values (GST_OBJECT (trans), timestamp);
+ }
+
+ meta =
+ (GstFramePositionerMeta *) gst_buffer_add_meta (buf,
+ gst_frame_positioner_get_info (), NULL);
+
+ GST_OBJECT_LOCK (framepositioner);
+ meta->alpha = framepositioner->alpha;
+ meta->posx = framepositioner->posx;
+ meta->posy = framepositioner->posy;
+ meta->width = framepositioner->width;
+ meta->height = framepositioner->height;
+ meta->zorder = framepositioner->zorder;
+ GST_OBJECT_UNLOCK (framepositioner);
+
+ return GST_FLOW_OK;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2013 Mathieu Duponchelle <mduponchelle1@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_FRAME_POSITIONNER_H_
+#define _GST_FRAME_POSITIONNER_H_
+
+#include <gst/base/gstbasetransform.h>
+#include <ges/ges-track-element.h>
+#include <ges/ges-track.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_FRAME_POSITIONNER (gst_frame_positioner_get_type())
+#define GST_FRAME_POSITIONNER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FRAME_POSITIONNER,GstFramePositioner))
+#define GST_FRAME_POSITIONNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FRAME_POSITIONNER,GstFramePositionerClass))
+#define GST_IS_FRAME_POSITIONNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FRAME_POSITIONNER))
+#define GST_IS_FRAME_POSITIONNER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FRAME_POSITIONNER))
+
+typedef struct _GstFramePositioner GstFramePositioner;
+typedef struct _GstFramePositionerClass GstFramePositionerClass;
+typedef struct _GstFramePositionerMeta GstFramePositionerMeta;
+
+struct _GstFramePositioner
+{
+ GstBaseTransform base_framepositioner;
+
+ GstElement *capsfilter;
+
+ GESTrackElement *track_source;
+ GESTrack *current_track;
+
+ gboolean scale_in_compositor;
+ gdouble alpha;
+ gint posx;
+ gint posy;
+ guint zorder;
+ gint width;
+ gint height;
+ gint track_width;
+ gint track_height;
+ gint fps_n;
+ gint fps_d;
+
+ gint par_n;
+ gint par_d;
+
+ /* This should never be made public, no padding needed */
+};
+
+struct _GstFramePositionerClass
+{
+ GstBaseTransformClass base_framepositioner_class;
+};
+
+struct _GstFramePositionerMeta {
+ GstMeta meta;
+
+ gdouble alpha;
+ gint posx;
+ gint posy;
+ gint height;
+ gint width;
+ guint zorder;
+};
+
+G_GNUC_INTERNAL void ges_frame_positioner_set_source_and_filter (GstFramePositioner *pos,
+ GESTrackElement *trksrc,
+ GstElement *capsfilter);
+G_GNUC_INTERNAL GType gst_frame_positioner_get_type (void);
+G_GNUC_INTERNAL GType
+gst_frame_positioner_meta_api_get_type (void);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+ges_sources = [
+ 'ges.c',
+ 'ges-enums.c',
+ 'ges-meta-container.c',
+ 'ges-timeline.c',
+ 'ges-layer.c',
+ 'ges-clip.c',
+ 'ges-pipeline.c',
+ 'ges-source-clip.c',
+ 'ges-base-effect-clip.c',
+ 'ges-effect-clip.c',
+ 'ges-uri-clip.c',
+ 'ges-operation-clip.c',
+ 'ges-base-transition-clip.c',
+ 'ges-transition-clip.c',
+ 'ges-test-clip.c',
+ 'ges-title-clip.c',
+ 'ges-overlay-clip.c',
+ 'ges-text-overlay-clip.c',
+ 'ges-track.c',
+ 'ges-audio-track.c',
+ 'ges-video-track.c',
+ 'ges-track-element.c',
+ 'ges-source.c',
+ 'ges-operation.c',
+ 'ges-video-source.c',
+ 'ges-audio-source.c',
+ 'ges-video-uri-source.c',
+ 'ges-audio-uri-source.c',
+ 'ges-image-source.c',
+ 'ges-multi-file-source.c',
+ 'ges-transition.c',
+ 'ges-audio-transition.c',
+ 'ges-video-transition.c',
+ 'ges-video-test-source.c',
+ 'ges-audio-test-source.c',
+ 'ges-title-source.c',
+ 'ges-text-overlay.c',
+ 'ges-base-effect.c',
+ 'ges-effect.c',
+ 'ges-screenshot.c',
+ 'ges-formatter.c',
+ 'ges-asset.c',
+ 'ges-uri-asset.c',
+ 'ges-clip-asset.c',
+ 'ges-track-element-asset.c',
+ 'ges-extractable.c',
+ 'ges-project.c',
+ 'ges-base-xml-formatter.c',
+ 'ges-xml-formatter.c',
+ 'ges-command-line-formatter.c',
+ 'ges-auto-transition.c',
+ 'ges-timeline-element.c',
+ 'ges-timeline-tree.c',
+ 'ges-container.c',
+ 'ges-effect-asset.c',
+ 'ges-smart-adder.c',
+ 'ges-smart-video-mixer.c',
+ 'ges-utils.c',
+ 'ges-group.c',
+ 'ges-validate.c',
+ 'ges-structured-interface.c',
+ 'ges-structure-parser.c',
+ 'gstframepositioner.c'
+]
+
+ges_headers = [
+ 'ges-types.h',
+ 'ges.h',
+ 'ges-prelude.h',
+ 'ges-enums.h',
+ 'ges-gerror.h',
+ 'ges-meta-container.h',
+ 'ges-timeline.h',
+ 'ges-layer.h',
+ 'ges-clip.h',
+ 'ges-pipeline.h',
+ 'ges-source-clip.h',
+ 'ges-uri-clip.h',
+ 'ges-base-effect-clip.h',
+ 'ges-effect-clip.h',
+ 'ges-operation-clip.h',
+ 'ges-base-transition-clip.h',
+ 'ges-transition-clip.h',
+ 'ges-test-clip.h',
+ 'ges-title-clip.h',
+ 'ges-overlay-clip.h',
+ 'ges-text-overlay-clip.h',
+ 'ges-base-effect.h',
+ 'ges-effect.h',
+ 'ges-track.h',
+ 'ges-audio-track.h',
+ 'ges-video-track.h',
+ 'ges-track-element.h',
+ 'ges-source.h',
+ 'ges-operation.h',
+ 'ges-video-source.h',
+ 'ges-audio-source.h',
+ 'ges-video-uri-source.h',
+ 'ges-audio-uri-source.h',
+ 'ges-image-source.h',
+ 'ges-multi-file-source.h',
+ 'ges-transition.h',
+ 'ges-audio-transition.h',
+ 'ges-video-transition.h',
+ 'ges-video-test-source.h',
+ 'ges-audio-test-source.h',
+ 'ges-title-source.h',
+ 'ges-text-overlay.h',
+ 'ges-screenshot.h',
+ 'ges-formatter.h',
+ 'ges-asset.h',
+ 'ges-uri-asset.h',
+ 'ges-clip-asset.h',
+ 'ges-track-element-asset.h',
+ 'ges-extractable.h',
+ 'ges-project.h',
+ 'ges-base-xml-formatter.h',
+ 'ges-xml-formatter.h',
+ 'ges-command-line-formatter.h',
+ 'ges-timeline-element.h',
+ 'ges-container.h',
+ 'ges-effect-asset.h',
+ 'ges-utils.h',
+ 'ges-group.h'
+]
+
+if libxml_dep.found()
+ ges_sources += ['ges-pitivi-formatter.c']
+ ges_headers += ['ges-pitivi-formatter.h']
+endif
+
+version_data = configuration_data()
+version_data.set('GES_VERSION_MAJOR', gst_version_major)
+version_data.set('GES_VERSION_MINOR', gst_version_minor)
+version_data.set('GES_VERSION_MICRO', gst_version_micro)
+version_data.set('GES_VERSION_NANO', gst_version_nano)
+
+configure_file(input : 'ges-version.h.in',
+ output : 'ges-version.h',
+ install_dir : join_paths(get_option('includedir'), 'gstreamer-1.0/ges'),
+ configuration : version_data)
+
+install_headers(ges_headers, subdir : 'gstreamer-1.0/ges')
+
+flex = find_program('flex', required : false)
+if not flex.found()
+ flex = find_program('win_flex', required : false)
+ if not flex.found()
+ error('flex not found')
+ endif
+endif
+
+parser = custom_target('gesparselex',
+ input : 'parse.l',
+ output : ['lex.priv_ges_parse_yy.c', 'ges-parse-lex.h'],
+ command : [flex, '-Ppriv_ges_parse_yy', '--header-file=@OUTPUT1@', '-o', '@OUTPUT0@', '@INPUT@']
+)
+
+libges = library('ges-1.0', ges_sources, parser,
+ version : libversion,
+ soversion : soversion,
+ darwin_versions : osxversion,
+ c_args : [ges_c_args] + ['-DBUILDING_GES'],
+ include_directories : [configinc],
+ install : true,
+ dependencies : libges_deps)
+
+ges_gen_sources = []
+if build_gir
+ ges_gir_extra_args = gir_init_section + [ '--c-include=ges/ges.h' ]
+ if meson.is_subproject()
+ # FIXME: There must be a better way to do this
+ # Need to pass the include path to find gst/gst.h and gst/gstenumtypes.h (built)
+ ges_gir_extra_args += ['--cflags-begin',
+ '-I' + meson.current_source_dir() + '/..',
+ '-I' + meson.current_build_dir() + '/..',
+ '--cflags-end']
+ endif
+ ges_gen_sources += [gnome.generate_gir(libges,
+ sources : ges_sources + ges_headers,
+ namespace : 'GES',
+ nsversion : apiversion,
+ identifier_prefix : 'GES',
+ symbol_prefix : 'ges',
+ export_packages : 'gst-editing-services-1.0',
+ includes : ['Gst-1.0', 'GstPbutils-1.0', 'GstVideo-1.0', 'Gio-2.0', 'GObject-2.0'],
+ install : true,
+ dependencies : libges_deps,
+ extra_args : ges_gir_extra_args
+ )]
+endif
+
+ges_dep = declare_dependency(link_with : libges,
+ include_directories : [configinc],
+ sources : ges_gen_sources,
+ dependencies : libges_deps,
+)
--- /dev/null
+%{
+#include "ges-structure-parser.h"
+
+%}
+
+%option noyywrap
+%option nounput
+%option reentrant
+%option extra-type="GESStructureParser *"
+%option never-interactive
+%option noinput
+%option nounistd
+
+CLIP [ ]+\+clip[ ]+
+TEST_CLIP [ ]+\+test-clip[ ]+
+TRANSITION [ ]+\+transition[ ]+
+EFFECT [ ]+\+effect[ ]+
+TITLE [ ]+\+title[ ]+
+
+SETTER [ ]+set-[^ ]+[ ]+
+
+%%
+
+\"(\\.|[^"])*\" {
+ ges_structure_parser_parse_string (yyextra, yytext, FALSE);
+}
+
+{CLIP}|{TRANSITION}|{EFFECT}|{TEST_CLIP}|{TITLE} {
+ ges_structure_parser_parse_symbol (yyextra, yytext);
+}
+
+{SETTER} {
+ ges_structure_parser_parse_setter (yyextra, yytext);
+}
+
+[ \t\n]+ {
+ ges_structure_parser_parse_whitespace (yyextra);
+}
+
+. {
+ /* add everything else */
+ ges_structure_parser_parse_default (yyextra, yytext);
+}
+
+%%
--- /dev/null
+<Project
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns="http://usefulinc.com/ns/doap#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:admin="http://webns.net/mvcb/">
+
+ <name>GStreamer Editing Services Library</name>
+ <shortname>gst-editing-services</shortname>
+ <homepage rdf:resource="http://gstreamer.freedesktop.org/modules/gst-editing-services.html" />
+ <created>2004-02-26</created>
+ <shortdesc xml:lang="en">
+a library for creating audio and video editors
+</shortdesc>
+ <description xml:lang="en">
+GStreamer library for creating audio and video editors
+ </description>
+ <category></category>
+ <bug-database rdf:resource="http://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer&component=gst-editing-services" />
+ <screenshots></screenshots>
+ <mailing-list rdf:resource="http://lists.freedesktop.org/mailman/listinfo/gstreamer-devel" />
+ <programming-language>C</programming-language>
+ <license rdf:resource="http://usefulinc.com/doap/licenses/lgpl"/>
+ <download-page rdf:resource="http://gstreamer.freedesktop.org/download/" />
+
+ <repository>
+ <GitRepository>
+ <location rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-editing-services"/>
+ <browse rdf:resource="http://gitlab.freedesktop.org/gstreamer/gst-editing-services"/>
+ </GitRepository>
+ </repository>
+
+ <release>
+ <Version>
+ <revision>1.16.2</revision>
+ <branch>1.16</branch>
+ <name></name>
+ <created>2019-12-03</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.16.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.16.1</revision>
+ <branch>1.16</branch>
+ <name></name>
+ <created>2019-09-23</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.16.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.16.0</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2019-04-19</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.16.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.15.90</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2019-04-11</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.15.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.15.2</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2019-02-26</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.15.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.15.1</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2019-01-17</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.15.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.14.0</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2018-03-19</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.14.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.13.91</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2018-03-13</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.13.91.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.13.90</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2018-03-03</created>
+ <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.13.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.13.1</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2018-02-15</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.13.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.12.4</revision>
+ <branch>1.12</branch>
+ <name></name>
+ <created>2017-12-07</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.12.4.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.12.3</revision>
+ <branch>1.12</branch>
+ <name></name>
+ <created>2017-09-18</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.12.3.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.12.2</revision>
+ <branch>1.12</branch>
+ <name></name>
+ <created>2017-07-14</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.12.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.12.1</revision>
+ <branch>1.12</branch>
+ <name></name>
+ <created>2017-06-20</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.12.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.12.0</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2017-05-04</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.12.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.11.91</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2017-04-27</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.11.91.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.11.90</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2017-04-07</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.11.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.11.2</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2017-02-24</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.11.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.11.1</revision>
+ <branch>master</branch>
+ <name></name>
+ <created>2017-01-12</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.11.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.10.0</revision>
+ <branch>master</branch>
+ <created>2016-11-01</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.10.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.9.90</revision>
+ <branch>master</branch>
+ <created>2016-09-30</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.9.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.9.2</revision>
+ <branch>master</branch>
+ <created>2016-09-01</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.9.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.9.1</revision>
+ <branch>master</branch>
+ <created>2016-06-06</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.9.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.8.0</revision>
+ <branch>master</branch>
+ <created>2016-03-24</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.8.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.7.91</revision>
+ <branch>master</branch>
+ <created>2016-03-15</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.7.91.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.7.90</revision>
+ <branch>master</branch>
+ <created>2016-03-01</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.7.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.7.2</revision>
+ <branch>master</branch>
+ <created>2016-02-19</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.7.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.7.1</revision>
+ <branch>master</branch>
+ <created>2015-12-24</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.7.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.6.2</revision>
+ <branch>1.6</branch>
+ <created>2015-12-14</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.6.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.6.1</revision>
+ <branch>1.6</branch>
+ <created>2015-10-30</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.6.1.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.6.0</revision>
+ <branch>1.6</branch>
+ <created>2015-09-25</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.6.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.5.91</revision>
+ <branch>1.5</branch>
+ <created>2015-09-18</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.5.91.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.5.90</revision>
+ <branch>1.5</branch>
+ <created>2015-08-20</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.5.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.5.2</revision>
+ <branch>1.5</branch>
+ <created>2015-06-24</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.5.2.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.4.0</revision>
+ <branch>1.4</branch>
+ <created>2014-10-20</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.4.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.3.90</revision>
+ <branch>1.0</branch>
+ <created>2014-09-23</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.3.90.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>1.2.0</revision>
+ <branch>1.0</branch>
+ <created>2011-01-20</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-1.2.0.tar.xz" />
+ </Version>
+ </release>
+
+ <release>
+ <Version>
+ <revision>0.10.1</revision>
+ <branch>0.10</branch>
+ <created>2011-01-20</created>
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-0.10.1.tar.bz2" />
+ <file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-editing-services/gst-editing-services-0.10.1.tar.gz" />
+ </Version>
+ </release>
+
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Thibault Saunier</foaf:name>
+ </foaf:Person>
+ </maintainer>
+
+</Project>
--- /dev/null
+#!/bin/sh
+#
+# Check that the code follows a consistant code style
+#
+
+# Check for existence of indent, and error out if not present.
+# On some *bsd systems the binary seems to be called gnunindent,
+# so check for that first.
+
+version=`gnuindent --version 2>/dev/null`
+if test "x$version" = "x"; then
+ version=`gindent --version 2>/dev/null`
+ if test "x$version" = "x"; then
+ version=`indent --version 2>/dev/null`
+ if test "x$version" = "x"; then
+ echo "GStreamer git pre-commit hook:"
+ echo "Did not find GNU indent, please install it before continuing."
+ exit 1
+ else
+ INDENT=indent
+ fi
+ else
+ INDENT=gindent
+ fi
+else
+ INDENT=gnuindent
+fi
+
+case `$INDENT --version` in
+ GNU*)
+ ;;
+ default)
+ echo "GStreamer git pre-commit hook:"
+ echo "Did not find GNU indent, please install it before continuing."
+ echo "(Found $INDENT, but it doesn't seem to be GNU indent)"
+ exit 1
+ ;;
+esac
+
+INDENT_PARAMETERS="--braces-on-if-line \
+ --case-brace-indentation0 \
+ --case-indentation2 \
+ --braces-after-struct-decl-line \
+ --line-length80 \
+ --no-tabs \
+ --cuddle-else \
+ --dont-line-up-parentheses \
+ --continuation-indentation4 \
+ --honour-newlines \
+ --tab-size8 \
+ --indent-level2 \
+ --leave-preprocessor-space"
+
+echo "--Checking style--"
+for file in `git diff-index --cached --name-only HEAD --diff-filter=ACMR| grep "\.c$"` ; do
+ # nf is the temporary checkout. This makes sure we check against the
+ # revision in the index (and not the checked out version).
+ nf=`git checkout-index --temp ${file} | cut -f 1`
+ newfile=`mktemp /tmp/${nf}.XXXXXX` || exit 1
+ $INDENT ${INDENT_PARAMETERS} \
+ $nf -o $newfile 2>> /dev/null
+ # FIXME: Call indent twice as it tends to do line-breaks
+ # different for every second call.
+ $INDENT ${INDENT_PARAMETERS} \
+ $newfile 2>> /dev/null
+ diff -u -p "${nf}" "${newfile}"
+ r=$?
+ rm "${newfile}"
+ rm "${nf}"
+ if [ $r != 0 ] ; then
+echo "================================================================================================="
+echo " Code style error in: $file "
+echo " "
+echo " Please fix before committing. Don't forget to run git add before trying to commit again. "
+echo " If the whole file is to be committed, this should work (run from the top-level directory): "
+echo " "
+echo " gst-indent $file; git add $file; git commit"
+echo " "
+echo "================================================================================================="
+ exit 1
+ fi
+done
+echo "--Checking style pass--"
--- /dev/null
+project('gst-editing-services', 'c',
+ version : '1.16.2',
+ meson_version : '>= 0.47',
+ default_options : [ 'warning_level=1',
+ 'buildtype=debugoptimized' ])
+
+gst_version = meson.project_version()
+version_arr = gst_version.split('.')
+gst_version = meson.project_version()
+version_arr = gst_version.split('.')
+gst_version_major = version_arr[0].to_int()
+gst_version_minor = version_arr[1].to_int()
+gst_version_micro = version_arr[2].to_int()
+ if version_arr.length() == 4
+ gst_version_nano = version_arr[3].to_int()
+else
+ gst_version_nano = 0
+endif
+
+apiversion = '1.0'
+soversion = 0
+# maintaining compatibility with the previous libtool versioning
+# current = minor * 100 + micro
+curversion = gst_version_minor * 100 + gst_version_micro
+libversion = '@0@.@1@.0'.format(soversion, curversion)
+osxversion = curversion + 1
+
+glib_req = '>= 2.40.0'
+gst_req = '>= @0@.@1@.0'.format(gst_version_major, gst_version_minor)
+
+cc = meson.get_compiler('c')
+
+cdata = configuration_data()
+
+# Ignore several spurious warnings for things gstreamer does very commonly
+# If a warning is completely useless and spammy, use '/wdXXXX' to suppress it
+# If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once
+# NOTE: Only add warnings here if you are sure they're spurious
+if cc.get_id() == 'msvc'
+ add_project_arguments(
+ '/wd4018', # implicit signed/unsigned conversion
+ '/wd4146', # unary minus on unsigned (beware INT_MIN)
+ '/wd4244', # lossy type conversion (e.g. double -> int)
+ '/wd4305', # truncating type conversion (e.g. double -> float)
+ language : 'c')
+endif
+
+if cc.has_link_argument('-Wl,-Bsymbolic-functions')
+ add_project_link_arguments('-Wl,-Bsymbolic-functions', language : 'c')
+endif
+
+# Symbol visibility
+if cc.get_id() == 'msvc'
+ export_define = '__declspec(dllexport) extern'
+elif cc.has_argument('-fvisibility=hidden')
+ add_project_arguments('-fvisibility=hidden', language: 'c')
+ export_define = 'extern __attribute__ ((visibility ("default")))'
+else
+ export_define = 'extern'
+endif
+
+# Passing this through the command line would be too messy
+cdata.set('GST_API_EXPORT', export_define)
+
+# Disable strict aliasing
+if cc.has_argument('-fno-strict-aliasing')
+ add_project_arguments('-fno-strict-aliasing', language: 'c')
+endif
+
+cdata.set('VERSION', '"@0@"'.format(gst_version))
+cdata.set('PACKAGE', '"gst-editing-services"')
+cdata.set('PACKAGE_VERSION', '"@0@"'.format(gst_version))
+cdata.set('PACKAGE_BUGREPORT', '"http://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer"')
+cdata.set('PACKAGE_NAME', '"GStreamer Editing Services"')
+cdata.set('GST_PACKAGE_NAME', '"GStreamer Editing Services"')
+cdata.set('GST_PACKAGE_ORIGIN', '"Unknown package origin"')
+cdata.set('GST_LICENSE', '"LGPL"')
+
+# Mandatory GST deps
+gst_dep = dependency('gstreamer-' + apiversion, version : gst_req,
+ fallback : ['gstreamer', 'gst_dep'])
+gstpbutils_dep = dependency('gstreamer-pbutils-' + apiversion, version : gst_req,
+ fallback : ['gst-plugins-base', 'pbutils_dep'])
+gstvideo_dep = dependency('gstreamer-video-' + apiversion, version : gst_req,
+ fallback : ['gst-plugins-base', 'video_dep'])
+gstbase_dep = dependency('gstreamer-base-1.0', version : gst_req,
+ fallback : ['gstreamer', 'gst_base_dep'])
+if host_machine.system() != 'windows'
+ gstcheck_dep = dependency('gstreamer-check-1.0', version : gst_req,
+ required : get_option('tests'),
+ fallback : ['gstreamer', 'gst_check_dep'])
+endif
+gstcontroller_dep = dependency('gstreamer-controller-1.0', version : gst_req,
+ fallback : ['gstreamer', 'gst_controller_dep'])
+gstvalidate_dep = dependency('gst-validate-1.0', version : gst_req, required : false,
+ fallback : ['gst-devtools', 'validate_dep'])
+
+gio_dep = dependency('gio-2.0', fallback: ['glib', 'libgio_dep'])
+libxml_dep = dependency('libxml-2.0', required: get_option('xptv'))
+cdata.set('DISABLE_XPTV', not libxml_dep.found())
+
+# TODO Properly port to Gtk 3
+# gtk_dep = dependency('gtk+-3.0', required : false)
+
+libges_deps = [gst_dep, gstbase_dep, gstvideo_dep, gstpbutils_dep,
+ gstcontroller_dep, gio_dep, libxml_dep]
+
+if gstvalidate_dep.found()
+ libges_deps = libges_deps + [gstvalidate_dep]
+ cdata.set('HAVE_GST_VALIDATE', 1)
+endif
+
+configure_file(output : 'config.h', configuration : cdata)
+
+
+gir = find_program('g-ir-scanner', required : get_option('introspection'))
+gnome = import('gnome')
+
+# Fixme, not very elegant.
+build_gir = gir.found() and not meson.is_cross_build()
+gir_init_section = [ '--add-init-section=' + \
+ 'extern void gst_init(gint*,gchar**);' + \
+ 'extern void ges_init(void);' + \
+ 'g_setenv("GST_REGISTRY_1.0", "/no/way/this/exists.reg", TRUE);' + \
+ 'g_setenv("GST_PLUGIN_PATH_1_0", "", TRUE);' + \
+ 'g_setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", TRUE);' + \
+ 'g_setenv("GST_DEBUG", "0", TRUE);' + \
+ 'gst_init(NULL,NULL);' + \
+ 'ges_init();', '--quiet']
+
+ges_c_args = ['-DHAVE_CONFIG_H', '-DG_LOG_DOMAIN="GES"']
+plugins_install_dir = '@0@/gstreamer-1.0'.format(get_option('libdir'))
+
+pkgconfig = import('pkgconfig')
+plugins_pkgconfig_install_dir = join_paths(plugins_install_dir, 'pkgconfig')
+if get_option('default_library') == 'shared'
+ # If we don't build static plugins there is no need to generate pc files
+ plugins_pkgconfig_install_dir = disabler()
+endif
+
+if gst_dep.type_name() == 'internal'
+ gst_debug_disabled = not subproject('gstreamer').get_variable('gst_debug')
+else
+ # We can't check that in the case of subprojects as we won't
+ # be able to build against an internal dependency (which is not built yet)
+ gst_debug_disabled = cc.has_header_symbol('gst/gstconfig.h', 'GST_DISABLE_GST_DEBUG', dependencies: gst_dep)
+endif
+
+if gst_debug_disabled and cc.has_argument('-Wno-unused')
+ add_project_arguments('-Wno-unused', language: 'c')
+endif
+
+warning_flags = [
+ '-Wmissing-declarations',
+ '-Wmissing-prototypes',
+ '-Wredundant-decls',
+ '-Wundef',
+ '-Wwrite-strings',
+ '-Wformat',
+ '-Wformat-security',
+ '-Winit-self',
+ '-Wmissing-include-dirs',
+ '-Waddress',
+ '-Wno-multichar',
+ '-Wdeclaration-after-statement',
+ '-Wvla',
+ '-Wpointer-arith',
+]
+
+foreach extra_arg : warning_flags
+ if cc.has_argument (extra_arg)
+ add_project_arguments([extra_arg], language: 'c')
+ endif
+endforeach
+
+configinc = include_directories('.')
+subdir('ges')
+subdir('plugins')
+subdir('tools')
+subdir('pkgconfig')
+subdir('tests')
+subdir('examples')
+
+override_detector = '''
+import sys
+import os
+
+prefix = sys.argv[1]
+version = sys.version_info
+
+# If we are installing in the same prefix as PyGobject
+# make sure to install in the right place.
+import gi.overrides
+
+overrides_path = os.path.dirname(gi.overrides.__file__)
+if os.path.commonprefix([overrides_path, prefix]) == prefix:
+ print(overrides_path)
+ exit(0)
+
+# Otherwise follow python's way of install site packages inside
+# the provided prefix
+if os.name == 'posix':
+ print(os.path.join(
+ prefix, 'lib', 'python%d.%d' % (version.major, version.minor),
+ 'site-packages', 'gi', 'overrides'))
+else:
+ print(os.path.join(
+ prefix, 'Lib', 'Python%d%d' % (version.major, version.minor),
+ 'site-packages', 'gi', 'overrides'))
+'''
+python3 = import('python').find_installation()
+pygi_override_dir = get_option('pygi-overrides-dir')
+if pygi_override_dir == ''
+ cres = run_command(python3, '-c', override_detector, get_option('prefix'))
+ if cres.returncode() == 0
+ pygi_override_dir = cres.stdout().strip()
+ endif
+ if cres.stderr() != ''
+ message(cres.stderr())
+ endif
+endif
+
+if pygi_override_dir != ''
+ message('pygobject overrides directory ' + pygi_override_dir)
+ subdir('bindings/python')
+endif
+
+if build_machine.system() == 'windows'
+ message('Disabling gtk-doc while building on Windows')
+else
+ if find_program('gtkdoc-scan', required : get_option('gtk_doc')).found()
+ subdir('docs')
+ else
+ message('Not building documentation as gtk-doc was not found')
+ endif
+endif
+
+run_command(python3, '-c', 'import shutil; shutil.copy("hooks/pre-commit.hook", ".git/hooks/pre-commit")')
--- /dev/null
+option('gtk_doc', type : 'feature', value : 'auto', yield : true,
+ description : 'Build API documentation with gtk-doc')
+option('introspection', type : 'feature', value : 'auto', yield : true,
+ description : 'Generate gobject-introspection bindings')
+option('tests', type : 'feature', value : 'auto', yield : true,
+ description : 'Build and enable unit tests')
+option('pygi-overrides-dir', type : 'string', value : '',
+ description: 'Path to pygobject overrides directory')
+option('xptv', type : 'feature', value : 'auto',
+ description : 'Build the deprecated xptv formater')
\ No newline at end of file
--- /dev/null
+pcfiles = \
+ gst-editing-services-@GST_API_VERSION@.pc
+
+pcfiles_uninstalled = \
+ gst-editing-services-@GST_API_VERSION@-uninstalled.pc
+
+all-local: $(pcfiles) $(pcfiles_uninstalled)
+
+### how to generate pc files
+%-@GST_API_VERSION@.pc: %.pc
+ cp $< $@
+%-@GST_API_VERSION@-uninstalled.pc: %-uninstalled.pc
+### the uninstalled libdir is depend of the build system used so set it here
+### rather than hardcoding it in the file directly.
+ $(AM_V_GEN) sed \
+ -e "s|[@]geslibdir[@]|$(abs_top_builddir)/ges/.libs|" \
+ $< > $@.tmp && mv $@.tmp $@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = $(pcfiles)
+
+EXTRA_DIST = \
+ gst-editing-services.pc.in \
+ gst-editing-services-uninstalled.pc.in
+CLEANFILES = $(pcfiles) $(pcfiles_uninstalled)
--- /dev/null
+# the standard variables don't make sense for an uninstalled copy
+prefix=
+exec_prefix=
+libdir=@geslibdir@
+includedir=@abs_top_builddir@
+
+Name: gst-editing-services
+Description: GStreamer Editing Services
+Version: @VERSION@
+Requires: gstreamer-@GST_API_VERSION@ gstreamer-base-@GST_API_VERSION@ gstreamer-controller-@GST_API_VERSION@ gstreamer-pbutils-@GST_API_VERSION@
+Libs: -L${libdir} -lges-@GST_API_VERSION@
+Cflags: -I@abs_top_srcdir@ -I@abs_top_builddir@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/gstreamer-@GST_API_VERSION@
+
+Name: gst-editing-services
+Description: GStreamer Editing Services
+Version: @VERSION@
+Requires: gstreamer-@GST_API_VERSION@ gstreamer-base-@GST_API_VERSION@ gstreamer-controller-@GST_API_VERSION@ gstreamer-pbutils-@GST_API_VERSION@
+Libs: -L${libdir} -lges-@GST_API_VERSION@
+Cflags: -I${includedir}
--- /dev/null
+pkgconf = configuration_data()
+
+pkgconf.set('prefix', get_option('prefix'))
+pkgconf.set('exec_prefix', '${prefix}')
+pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
+pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
+pkgconf.set('GST_API_VERSION', apiversion)
+pkgconf.set('VERSION', gst_version)
+
+# needed for generating -uninstalled.pc files
+pkgconf.set('abs_top_builddir', join_paths(meson.current_build_dir(), '..'))
+pkgconf.set('abs_top_srcdir', join_paths(meson.current_source_dir(), '..'))
+pkgconf.set('geslibdir', join_paths(meson.build_root(), libges.outdir()))
+
+pkg_install_dir = '@0@/pkgconfig'.format(get_option('libdir'))
+
+pkg_files = ['gst-editing-services']
+
+foreach p : pkg_files
+ infile = p + '.pc.in'
+ outfile = p + '-1.0.pc'
+ configure_file(input : infile,
+ output : outfile,
+ configuration : pkgconf,
+ install_dir : pkg_install_dir)
+
+ infile = p + '-uninstalled.pc.in'
+ outfile = p + '-1.0-uninstalled.pc'
+ configure_file(input : infile,
+ output : outfile,
+ configuration : pkgconf)
+endforeach
+
--- /dev/null
+SUBDIRS = ges nle
--- /dev/null
+plugin_LTLIBRARIES = libgstges.la
+
+libgstges_la_SOURCES = \
+ gesplugin.c \
+ gessrc.c \
+ gesdemux.c
+
+libgstges_la_CFLAGS = -I$(top_srcdir) \
+ $(GST_PBUTILS_CFLAGS) \
+ $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+
+libgstges_la_LIBADD = \
+ $(top_builddir)/ges/libges-@GST_API_VERSION@.la \
+ $(GST_PBUTILS_LIBS) \
+ $(GST_PLUGINS_BASE_LIBS) \
+ $(GST_BASE_LIBS) $(GST_LIBS)
+
+libgstges_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = \
+ gesdemux.h \
+ gessrc.h
+
--- /dev/null
+/* GStreamer GES plugin
+ *
+ * Copyright (C) 2019 Igalia S.L
+ * Author: 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gesdemux.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+ /**
+ * SECTION:gstdemux
+ * @short_description: A GstBin subclasses use to use GESTimeline
+ * as demux inside any GstPipeline.
+ * @see_also: #GESTimeline
+ *
+ * The gstdemux is a bin that will simply expose the track source pads
+ * and implements the GstUriHandler interface using a custom ges://0Xpointer
+ * uri scheme.
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <glib/gstdio.h>
+#include <gst/pbutils/pbutils.h>
+#include "gesdemux.h"
+
+GST_DEBUG_CATEGORY_STATIC (gesdemux);
+#define GST_CAT_DEFAULT gesdemux
+
+static GstStaticPadTemplate video_src_template =
+GST_STATIC_PAD_TEMPLATE ("video_src",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("video/x-raw(ANY)"));
+
+static GstStaticPadTemplate audio_src_template =
+ GST_STATIC_PAD_TEMPLATE ("audio_src",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("audio/x-raw(ANY);"));
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/xges"));
+
+G_DEFINE_TYPE (GESDemux, ges_demux, GST_TYPE_BIN);
+
+enum
+{
+ PROP_0,
+ PROP_TIMELINE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+static gboolean
+ges_demux_set_timeline (GESDemux * self, GESTimeline * timeline)
+{
+ GList *tmp;
+ guint naudiopad = 0, nvideopad = 0;
+ GstBin *sbin = GST_BIN (self);
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+
+ if (self->timeline) {
+ GST_ERROR_OBJECT (self, "Implement changing timeline support");
+
+ return FALSE;
+ }
+
+ GST_INFO_OBJECT (self, "Setting timeline: %" GST_PTR_FORMAT, timeline);
+ self->timeline = gst_object_ref (timeline);
+
+ if (!gst_bin_add (sbin, GST_ELEMENT (self->timeline))) {
+ GST_ERROR_OBJECT (self, "Could not add timeline to myself!");
+
+ return FALSE;
+ }
+ for (tmp = self->timeline->tracks; tmp; tmp = tmp->next) {
+ GstPad *gpad;
+ gchar *name = NULL;
+ GstElement *queue;
+ GESTrack *track = GES_TRACK (tmp->data);
+ GstPad *tmppad, *pad =
+ ges_timeline_get_pad_for_track (self->timeline, track);
+ GstStaticPadTemplate *template;
+
+ if (!pad) {
+ GST_WARNING_OBJECT (self, "No pad for track: %" GST_PTR_FORMAT, track);
+
+ continue;
+ }
+
+ if (track->type == GES_TRACK_TYPE_AUDIO) {
+ name = g_strdup_printf ("audio_%u", naudiopad++);
+ template = &audio_src_template;
+ } else if (track->type == GES_TRACK_TYPE_VIDEO) {
+ name = g_strdup_printf ("video_%u", nvideopad++);
+ template = &video_src_template;
+ } else {
+ GST_INFO_OBJECT (self, "Track type not handled: %" GST_PTR_FORMAT, track);
+ continue;
+ }
+
+ queue = gst_element_factory_make ("queue", NULL);
+ /* Add queues the same way as in GESPipeline */
+ g_object_set (G_OBJECT (queue), "max-size-buffers", 0,
+ "max-size-bytes", 0, "max-size-time", (gint64) 2 * GST_SECOND, NULL);
+ gst_bin_add (GST_BIN (self), queue);
+ gst_element_sync_state_with_parent (GST_ELEMENT (queue));
+
+ tmppad = gst_element_get_static_pad (queue, "sink");
+ if (gst_pad_link (pad, tmppad) != GST_PAD_LINK_OK) {
+ GST_ERROR_OBJECT (self, "Could not link %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (tmppad));
+
+ gst_object_unref (tmppad);
+ gst_object_unref (queue);
+ continue;
+ }
+
+ tmppad = gst_element_get_static_pad (queue, "src");
+ gpad = gst_ghost_pad_new_from_template (name, tmppad,
+ gst_static_pad_template_get (template));
+
+ gst_pad_set_active (gpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (self), gpad);
+ GST_DEBUG_OBJECT (self, "Adding pad: %" GST_PTR_FORMAT, gpad);
+ }
+
+ gst_element_sync_state_with_parent (GST_ELEMENT (self->timeline));
+
+ return TRUE;
+}
+
+static void
+ges_demux_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESDemux *self = GES_DEMUX (object);
+
+ switch (property_id) {
+ case PROP_TIMELINE:
+ g_value_set_object (value, self->timeline);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_demux_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_demux_dispose (GObject * object)
+{
+ GESDemux *self = GES_DEMUX (object);
+
+ if (self->timeline)
+ gst_clear_object (&self->timeline);
+}
+
+static void
+ges_demux_class_init (GESDemuxClass * self_class)
+{
+ GObjectClass *gclass = G_OBJECT_CLASS (self_class);
+ GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (self_class);
+
+ GST_DEBUG_CATEGORY_INIT (gesdemux, "gesdemux", 0, "ges demux element");
+
+ gclass->get_property = ges_demux_get_property;
+ gclass->set_property = ges_demux_set_property;
+ gclass->dispose = ges_demux_dispose;
+
+ /**
+ * GESDemux:timeline:
+ *
+ * Timeline to use in this source.
+ */
+ properties[PROP_TIMELINE] = g_param_spec_object ("timeline", "Timeline",
+ "Timeline to use in this source.",
+ GES_TYPE_TIMELINE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gclass, PROP_LAST, properties);
+
+ gst_element_class_set_static_metadata (gstelement_klass,
+ "GStreamer Editing Services based 'demuxer'",
+ "Codec/Demux/Editing",
+ "Demuxer for complex timeline file formats using GES.",
+ "Thibault Saunier <tsaunier@igalia.com");
+
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&sink_template));
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&video_src_template));
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&audio_src_template));
+}
+
+typedef struct
+{
+ GESTimeline *timeline;
+ gchar *uri;
+ GMainLoop *ml;
+ GError *error;
+ GMutex lock;
+ GCond cond;
+ gulong loaded_sigid;
+ gulong error_sigid;
+} TimelineConstructionData;
+
+static void
+project_loaded_cb (GESProject * project, GESTimeline * timeline,
+ TimelineConstructionData * data)
+{
+ g_mutex_lock (&data->lock);
+ data->timeline = timeline;
+ g_signal_handler_disconnect (project, data->loaded_sigid);
+ data->loaded_sigid = 0;
+ g_mutex_unlock (&data->lock);
+
+ g_main_loop_quit (data->ml);
+}
+
+static void
+error_loading_asset_cb (GESProject * project, GError * error, gchar * id,
+ GType extractable_type, TimelineConstructionData * data)
+{
+ g_mutex_lock (&data->lock);
+ data->error = g_error_copy (error);
+ g_signal_handler_disconnect (project, data->error_sigid);
+ data->error_sigid = 0;
+ g_mutex_unlock (&data->lock);
+
+ g_main_loop_quit (data->ml);
+}
+
+/* TODO: Add a way to run a function in the right GES thread */
+static gboolean
+ges_timeline_new_from_uri_from_main_thread (TimelineConstructionData * data)
+{
+ GESProject *project = ges_project_new (data->uri);
+ GESUriClipAssetClass *klass = g_type_class_peek (GES_TYPE_URI_CLIP_ASSET);
+ GstDiscoverer *previous_discoverer = klass->discoverer;
+ GstClockTime timeout;
+ G_GNUC_UNUSED void *unused;
+
+ g_object_get (previous_discoverer, "timeout", &timeout, NULL);
+
+ /* Make sure to use a new discoverer in case we are being discovered,
+ * as discovering is done one by one, and the global discoverer won't
+ * have the chance to discover the project assets */
+ g_mutex_lock (&data->lock);
+ klass->discoverer = gst_discoverer_new (timeout, &data->error);
+ if (data->error) {
+ klass->discoverer = previous_discoverer;
+ g_mutex_unlock (&data->lock);
+
+ goto done;
+ }
+ g_signal_connect (klass->discoverer, "discovered",
+ G_CALLBACK (klass->discovered), NULL);
+ gst_discoverer_start (klass->discoverer);
+
+ data->ml = g_main_loop_new (NULL, TRUE);
+ data->loaded_sigid =
+ g_signal_connect (project, "loaded", G_CALLBACK (project_loaded_cb),
+ data);
+ data->error_sigid =
+ g_signal_connect (project, "error-loading-asset",
+ G_CALLBACK (error_loading_asset_cb), data);
+
+ unused = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), &data->error));
+ if (data->error) {
+ g_mutex_unlock (&data->lock);
+
+ goto done;
+ }
+ g_mutex_unlock (&data->lock);
+
+ g_main_loop_run (data->ml);
+ g_main_loop_unref (data->ml);
+
+done:
+
+ g_mutex_lock (&data->lock);
+
+ /* Set previous discoverer back! */
+
+ if (klass->discoverer)
+ gst_object_unref (klass->discoverer);
+ klass->discoverer = previous_discoverer;
+
+ if (data->timeline)
+ ges_timeline_commit (data->timeline);
+
+ if (data->loaded_sigid)
+ g_signal_handler_disconnect (project, data->loaded_sigid);
+
+ if (data->error_sigid)
+ g_signal_handler_disconnect (project, data->error_sigid);
+
+ gst_clear_object (&project);
+
+ g_cond_broadcast (&data->cond);
+ g_mutex_unlock (&data->lock);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GESDemux *self = GES_DEMUX (parent);
+
+ switch (event->type) {
+ case GST_EVENT_EOS:{
+ GstMapInfo map;
+ GstBuffer *xges_buffer;
+ gboolean ret = TRUE;
+ gsize available;
+
+ available = gst_adapter_available (self->input_adapter);
+ if (available == 0) {
+ GST_WARNING_OBJECT (self,
+ "Received EOS without any serialized timeline.");
+
+ return gst_pad_event_default (pad, parent, event);
+ }
+
+ xges_buffer = gst_adapter_take_buffer (self->input_adapter, available);
+ if (gst_buffer_map (xges_buffer, &map, GST_MAP_READ)) {
+ GError *err = NULL;
+ gchar *filename = NULL, *uri = NULL;
+ TimelineConstructionData data = { 0, };
+ gint f = g_file_open_tmp (NULL, &filename, &err);
+ GMainContext *main_context = g_main_context_default ();
+
+ GST_ERROR ("Loading %s", filename);
+ if (err) {
+ GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE,
+ ("Could not open temporary file to write timeline description"),
+ ("%s", err->message));
+
+ goto error;
+ }
+
+ g_file_set_contents (filename, (gchar *) map.data, map.size, &err);
+ if (err) {
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
+ ("Could not write temporary timeline description file"),
+ ("%s", err->message));
+
+ goto error;
+ }
+
+ uri = gst_filename_to_uri (filename, NULL);
+ data.uri = uri;
+
+ g_main_context_invoke (main_context,
+ (GSourceFunc) ges_timeline_new_from_uri_from_main_thread, &data);
+ g_mutex_lock (&data.lock);
+ while (!data.error && !data.timeline)
+ g_cond_wait (&data.cond, &data.lock);
+ data.loaded_sigid = 0;
+ data.error_sigid = 0;
+ g_mutex_unlock (&data.lock);
+
+ if (data.error) {
+ GST_ELEMENT_ERROR (self, STREAM, DEMUX,
+ ("Could not create timeline from description"),
+ ("%s", data.error->message));
+ g_clear_error (&data.error);
+
+ goto error;
+ }
+
+ GST_INFO_OBJECT (self, "Timeline properly loaded: %" GST_PTR_FORMAT,
+ data.timeline);
+ ges_demux_set_timeline (self, data.timeline);
+ done:
+ g_free (filename);
+ g_free (uri);
+ g_close (f, NULL);
+ return ret;
+ error:
+ ret = FALSE;
+ goto done;
+ } else {
+ GST_ELEMENT_ERROR (self, RESOURCE, READ,
+ ("Could not map buffer containing timeline description"),
+ ("Not info"));
+ }
+ GST_ERROR_OBJECT (xges_buffer, ":YAY");
+ }
+ default:
+ break;
+ }
+
+ return gst_pad_event_default (pad, parent, event);
+}
+
+static GstFlowReturn
+ges_demux_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ GESDemux *self = GES_DEMUX (parent);
+
+ gst_adapter_push (self->input_adapter, buffer);
+
+ GST_INFO_OBJECT (self, "Received buffer, total size is %i bytes",
+ (gint) gst_adapter_available (self->input_adapter));
+
+ return GST_FLOW_OK;
+}
+
+static void
+ges_demux_init (GESDemux * self)
+{
+ ges_init ();
+ self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+ gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
+
+ self->input_adapter = gst_adapter_new ();
+
+ gst_pad_set_chain_function (self->sinkpad,
+ GST_DEBUG_FUNCPTR (ges_demux_sink_chain));
+
+ gst_pad_set_event_function (self->sinkpad,
+ GST_DEBUG_FUNCPTR (ges_demux_sink_event));
+}
--- /dev/null
+/* GStreamer GES plugin
+ *
+ * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gesdemux.h
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 __GES_DEMUX_H__
+#define __GES_DEMUX_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include <ges/ges.h>
+
+G_BEGIN_DECLS
+
+GType ges_demux_get_type (void);
+
+#define GES_DEMUX_TYPE (ges_demux_get_type ())
+#define GES_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_DEMUX_TYPE, GESDemux))
+#define GES_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_DEMUX_TYPE, GESDemuxClass))
+#define GES_IS_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_DEMUX_TYPE))
+#define GES_IS_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_DEMUX_TYPE))
+#define GES_DEMUX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_DEMUX_TYPE, GESDemuxClass))
+
+typedef struct {
+ GstBin parent;
+
+ GESTimeline *timeline;
+ GstPad *sinkpad;
+
+ GstAdapter *input_adapter;
+} GESDemux;
+
+typedef struct {
+ GstBinClass parent;
+
+} GESDemuxClass;
+
+G_END_DECLS
+#endif /* __GES_DEMUX_H__ */
+
--- /dev/null
+/* GStreamer GES plugin
+ *
+ * Copyright (C) 2015 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * gstges.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+#include "gessrc.h"
+#include "gesdemux.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean res = 1;
+
+ res |= gst_element_register (plugin, "gessrc", GST_RANK_NONE, GES_SRC_TYPE);
+
+ res |= gst_element_register (plugin, "gesdemux", GST_RANK_PRIMARY,
+ GES_DEMUX_TYPE);
+
+ return res;
+}
+
+/* plugin export resolution */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ ges,
+ "GStreamer Editing Services Plugin",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
--- /dev/null
+/* GStreamer Editing Services GStreamer plugin
+ * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gessrc.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+
+ **
+ * SECTION:gessrc
+ * @short_description: A GstBin subclasses use to use GESTimeline
+ * as sources inside any GstPipeline.
+ * @see_also: #GESTimeline
+ *
+ * The gessrc is a bin that will simply expose the track src pads
+ * and implements the GstUriHandler interface using a custom `ges://`
+ * uri scheme.
+ *
+ * NOTE: That to use it inside playbin and friends you **need** to
+ * set gessrc::timeline property yourself.
+ *
+ * Example with #playbin:
+ *
+ * {{../../examples/c/gessrc.c}}
+ *
+ * Example with #GstPlayer:
+ *
+ * {{../../examples/python/gst-player.py}}
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include "gessrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (gessrc);
+#define GST_CAT_DEFAULT gessrc
+
+static GstStaticPadTemplate video_src_template =
+GST_STATIC_PAD_TEMPLATE ("video_src",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("video/x-raw(ANY)"));
+
+static GstStaticPadTemplate audio_src_template =
+ GST_STATIC_PAD_TEMPLATE ("audio_src",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("audio/x-raw(ANY);"));
+
+enum
+{
+ PROP_0,
+ PROP_TIMELINE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+static gboolean
+ges_src_set_timeline (GESSrc * self, GESTimeline * timeline)
+{
+ GList *tmp;
+ guint naudiopad = 0, nvideopad = 0;
+ GstBin *sbin = GST_BIN (self);
+
+ g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
+
+ if (self->timeline) {
+ GST_FIXME_OBJECT (self, "Implement changing timeline support");
+
+ return FALSE;
+ }
+
+ self->timeline = timeline;
+
+ gst_bin_add (sbin, GST_ELEMENT (self->timeline));
+ for (tmp = self->timeline->tracks; tmp; tmp = tmp->next) {
+ GstPad *gpad;
+ gchar *name = NULL;
+ GstElement *queue;
+ GESTrack *track = GES_TRACK (tmp->data);
+ GstPad *tmppad, *pad =
+ ges_timeline_get_pad_for_track (self->timeline, track);
+ GstStaticPadTemplate *template;
+
+ if (!pad) {
+ GST_INFO_OBJECT (self, "No pad for track: %" GST_PTR_FORMAT, track);
+
+ continue;
+ }
+
+ if (track->type == GES_TRACK_TYPE_AUDIO) {
+ name = g_strdup_printf ("audio_%u", naudiopad++);
+ template = &audio_src_template;
+ } else if (track->type == GES_TRACK_TYPE_VIDEO) {
+ name = g_strdup_printf ("video_%u", nvideopad++);
+ template = &video_src_template;
+ } else {
+ GST_INFO_OBJECT (self, "Track type not handled: %" GST_PTR_FORMAT, track);
+ continue;
+ }
+
+ queue = gst_element_factory_make ("queue", NULL);
+ /* Add queues the same way as in GESPipeline */
+ g_object_set (G_OBJECT (queue), "max-size-buffers", 0,
+ "max-size-bytes", 0, "max-size-time", (gint64) 2 * GST_SECOND, NULL);
+ gst_bin_add (GST_BIN (self), queue);
+
+ tmppad = gst_element_get_static_pad (queue, "sink");
+ if (gst_pad_link (pad, tmppad) != GST_PAD_LINK_OK) {
+ GST_ERROR ("Could not link %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (tmppad));
+
+ gst_object_unref (tmppad);
+ gst_object_unref (queue);
+ continue;
+ }
+
+ tmppad = gst_element_get_static_pad (queue, "src");
+ gpad = gst_ghost_pad_new_from_template (name, tmppad,
+ gst_static_pad_template_get (template));
+
+ gst_pad_set_active (gpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (self), gpad);
+ }
+
+ gst_element_sync_state_with_parent (GST_ELEMENT (self->timeline));
+
+ return TRUE;
+}
+
+/*** GSTURIHANDLER INTERFACE *************************************************/
+
+static GstURIType
+ges_src_uri_get_type (GType type)
+{
+ return GST_URI_SRC;
+}
+
+static const gchar *const *
+ges_src_uri_get_protocols (GType type)
+{
+ static const gchar *protocols[] = { "ges", NULL };
+
+ return protocols;
+}
+
+static gchar *
+ges_src_uri_get_uri (GstURIHandler * handler)
+{
+ GESSrc *self = GES_SRC (handler);
+
+ return self->timeline ? g_strdup_printf ("ges://%s",
+ GST_OBJECT_NAME (self->timeline)) : NULL;
+}
+
+static gboolean
+ges_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
+ GError ** error)
+{
+ return TRUE;
+}
+
+static void
+ges_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ iface->get_type = ges_src_uri_get_type;
+ iface->get_protocols = ges_src_uri_get_protocols;
+ iface->get_uri = ges_src_uri_get_uri;
+ iface->set_uri = ges_src_uri_set_uri;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESSrc, ges_src, GST_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, ges_src_uri_handler_init));
+
+static void
+ges_src_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESSrc *self = GES_SRC (object);
+
+ switch (property_id) {
+ case PROP_TIMELINE:
+ g_value_set_object (value, self->timeline);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_src_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESSrc *self = GES_SRC (object);
+
+ switch (property_id) {
+ case PROP_TIMELINE:
+ ges_src_set_timeline (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_src_class_init (GESSrcClass * self_class)
+{
+ GObjectClass *gclass = G_OBJECT_CLASS (self_class);
+ GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (self_class);
+
+ GST_DEBUG_CATEGORY_INIT (gessrc, "gessrc", 0, "ges src element");
+
+ gclass->get_property = ges_src_get_property;
+ gclass->set_property = ges_src_set_property;
+
+ /**
+ * GESSrc:timeline:
+ *
+ * Timeline to use in this src.
+ */
+ properties[PROP_TIMELINE] = g_param_spec_object ("timeline", "Timeline",
+ "Timeline to use in this src.",
+ GES_TYPE_TIMELINE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gclass, PROP_LAST, properties);
+
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&video_src_template));
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&audio_src_template));
+}
+
+static void
+ges_src_init (GESSrc * self)
+{
+}
--- /dev/null
+/* GStreamer GES plugin
+ *
+ * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gstges.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GES_SRC_H__
+#define __GES_SRC_H__
+
+#include <gst/gst.h>
+#include <ges/ges.h>
+
+G_BEGIN_DECLS
+
+GType ges_src_get_type (void);
+
+#define GES_SRC_TYPE (ges_src_get_type ())
+#define GES_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_SRC_TYPE, GESSrc))
+#define GES_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_SRC_TYPE, GESSrcClass))
+#define GES_IS_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_SRC_TYPE))
+#define GES_IS_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_SRC_TYPE))
+#define GES_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_SRC_TYPE, GESSrcClass))
+
+typedef struct {
+ GstBin parent;
+
+ GESTimeline *timeline;
+} GESSrc;
+
+typedef struct {
+ GstBinClass parent;
+
+} GESSrcClass;
+
+G_END_DECLS
+#endif /* __GES_SRC_H__ */
--- /dev/null
+gstges_sources = ['gesplugin.c', 'gessrc.c', 'gesdemux.c']
+
+gstges = library('gstges', gstges_sources,
+ dependencies : [gst_dep, ges_dep],
+ include_directories: [configinc],
+ c_args : ges_c_args,
+ install : true,
+ install_dir : plugins_install_dir,
+)
+pkgconfig.generate(gstges, install_dir : plugins_pkgconfig_install_dir)
--- /dev/null
+subdir('nle')
+subdir('ges')
\ No newline at end of file
--- /dev/null
+Makefile
+Makefile.in
+*.o
+*.lo
+*.la
+.deps
+.libs
+nleversion.h
--- /dev/null
+plugin_LTLIBRARIES = libgstnle.la
+
+libgstnle_la_SOURCES = gstnle.c \
+ nleobject.c \
+ nlecomposition.c \
+ nleghostpad.c \
+ nleoperation.c \
+ nlesource.c \
+ nleurisource.c
+
+libgstnle_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_BASE_CFLAGS) $(GST_CFLAGS) \
+ -I$(top_srcdir)
+
+libgstnle_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
+ $(GST_BASE_LIBS) $(GST_LIBS)
+
+libgstnle_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = \
+ nle.h \
+ nleobject.h \
+ nlecomposition.h \
+ nletypes.h \
+ nleghostpad.h \
+ nleoperation.h \
+ nlesource.h \
+ nletypes.h \
+ nleurisource.h
--- /dev/null
+VOID:OBJECT,UINT
+
--- /dev/null
+/* Non Linear Engine plugin
+ *
+ * Copyright (C) 2015 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * gstnle.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include "nle.h"
+
+struct _elements_entry
+{
+ const gchar *name;
+ GType (*type) (void);
+};
+
+static struct _elements_entry _elements[] = {
+ {"nlesource", nle_source_get_type},
+ {"nlecomposition", nle_composition_get_type},
+ {"nleoperation", nle_operation_get_type},
+ {"nleurisource", nle_urisource_get_type},
+ {NULL, 0}
+};
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gint i = 0;
+
+ for (; _elements[i].name; i++)
+ if (!(gst_element_register (plugin,
+ _elements[i].name, GST_RANK_NONE, (_elements[i].type) ())))
+ return FALSE;
+
+ nle_init_ghostpad_category ();
+
+ return TRUE;
+}
+
+/* plugin export resolution */
+GST_PLUGIN_DEFINE
+ (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ nle,
+ "GStreamer Non Linear Engine",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
--- /dev/null
+nle_sources = ['nleobject.c',
+ 'nlecomposition.c',
+ 'nleghostpad.c',
+ 'nleoperation.c',
+ 'nlesource.c',
+ 'nleurisource.c',
+ 'gstnle.c'
+]
+
+nle = library('gstnle', nle_sources,
+ dependencies : [gst_dep, gstbase_dep],
+ include_directories: [configinc],
+ c_args : ges_c_args,
+ install : true,
+ install_dir : plugins_install_dir,
+)
+pkgconfig.generate(nle, install_dir : plugins_pkgconfig_install_dir)
--- /dev/null
+/* Gnonlin
+ * Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
+ * <2004-2008> Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __NLE_H__
+#define __NLE_H__
+
+#include <gst/gst.h>
+
+#include "nletypes.h"
+
+#include "nleobject.h"
+#include "nleghostpad.h"
+#include "nlesource.h"
+#include "nlecomposition.h"
+#include "nleoperation.h"
+
+#include "nleurisource.h"
+
+#endif /* __GST_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
+ * 2004-2008 Edward Hervey <bilboed@bilboed.com>
+ * 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+ * 2014 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "nle.h"
+
+/**
+ * SECTION:element-nlecomposition
+ *
+ * A NleComposition contains NleObjects such as NleSources and NleOperations,
+ * and connects them dynamically to create a composition timeline.
+ */
+
+static GstStaticPadTemplate nle_composition_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (nlecomposition_debug);
+#define GST_CAT_DEFAULT nlecomposition_debug
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (nlecomposition_debug,"nlecomposition", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Composition");
+#define nle_composition_parent_class parent_class
+
+enum
+{
+ PROP_0,
+ PROP_DEACTIVATED_ELEMENTS_STATE,
+ PROP_LAST,
+};
+
+/* Properties from NleObject */
+enum
+{
+ NLEOBJECT_PROP_START,
+ NLEOBJECT_PROP_STOP,
+ NLEOBJECT_PROP_DURATION,
+ NLEOBJECT_PROP_LAST
+};
+
+enum
+{
+ COMMIT_SIGNAL,
+ COMMITED_SIGNAL,
+ LAST_SIGNAL
+};
+
+typedef enum
+{
+ COMP_UPDATE_STACK_INITIALIZE,
+ COMP_UPDATE_STACK_ON_COMMIT,
+ COMP_UPDATE_STACK_ON_EOS,
+ COMP_UPDATE_STACK_ON_SEEK,
+ COMP_UPDATE_STACK_NONE
+} NleUpdateStackReason;
+
+static const char *UPDATE_PIPELINE_REASONS[] = {
+ "Initialize", "Commit", "EOS", "Seek"
+};
+
+typedef struct
+{
+ NleComposition *comp;
+ GstEvent *event;
+} SeekData;
+
+typedef struct
+{
+ NleComposition *comp;
+ NleObject *object;
+} ChildIOData;
+
+typedef struct
+{
+ NleComposition *comp;
+ gint32 seqnum;
+
+ NleUpdateStackReason reason;
+} UpdateCompositionData;
+
+typedef struct _Action
+{
+ GCClosure closure;
+ gint priority;
+} Action;
+
+struct _NleCompositionPrivate
+{
+ gboolean dispose_has_run;
+
+ /*
+ Sorted List of NleObjects , ThreadSafe
+ objects_start : sorted by start-time then priority
+ objects_stop : sorted by stop-time then priority
+ objects_hash : contains all controlled objects
+
+ Those list should be manipulated exclusively in the main context
+ or while the task is totally stopped.
+ */
+ GList *objects_start;
+ GList *objects_stop;
+ GHashTable *objects_hash;
+
+ /* List of NleObject to be inserted or removed from the composition on the
+ * next commit */
+ GHashTable *pending_io;
+
+ gulong ghosteventprobe;
+
+ /* current stack, list of NleObject* */
+ GNode *current;
+
+ /* List of NleObject whose start/duration will be the same as the composition */
+ GList *expandables;
+
+ /* currently configured stack seek start/stop time.
+ * In forward playback:
+ * - current_stack_start: The start of the current stack or the start value
+ * of the seek if the stack has been seeked 'in the middle'
+ * - current_stack_stop: The stop time of the current stack
+ *
+ * Reconstruct pipeline ONLY if seeking outside of those values
+ * FIXME : current_stack_start isn't always the earliest time before which the
+ * timeline doesn't need to be modified
+ */
+ GstClockTime current_stack_start;
+ GstClockTime current_stack_stop;
+
+ /* Seek segment handler */
+ /* Represents the current segment that is being played,
+ * In forwards playback (logic is the same but swapping start and
+ * stop in backward playback):
+ * - segment->start: start of the current segment being played,
+ * at each stack change it will advance to the newly configured
+ * stack start.
+ * - segment->stop is the final stop of the segment being played.
+ * if a seek with a stop time happened, it will be the stop time
+ * otherwise, it will be the composition duration.
+ */
+ GstSegment *segment;
+
+ /* Segment representing the last seek. Simply initialized
+ * segment if no seek occured. */
+ GstSegment *seek_segment;
+ guint64 next_base_time;
+
+ /*
+ OUR sync_handler on the child_bus
+ We are called before nle_object_sync_handler
+ */
+ GstPadEventFunction nle_event_pad_func;
+ gboolean send_stream_start;
+
+ /* Protect the actions list */
+ GMutex actions_lock;
+ GCond actions_cond;
+ GList *actions;
+ Action *current_action;
+
+ gboolean running;
+ gboolean initialized;
+
+ GstElement *current_bin;
+
+ gboolean seeking_itself;
+ gint real_eos_seqnum;
+ gint next_eos_seqnum;
+ guint32 flush_seqnum;
+
+ /* 0 means that we already received the right caps or segment */
+ gint seqnum_to_restart_task;
+ gboolean waiting_serialized_query_or_buffer;
+
+ gboolean tearing_down_stack;
+ gboolean suppress_child_error;
+
+ NleUpdateStackReason updating_reason;
+};
+
+#define ACTION_CALLBACK(__action) (((GCClosure*) (__action))->callback)
+
+static guint _signals[LAST_SIGNAL] = { 0 };
+
+static GParamSpec *nleobject_properties[NLEOBJECT_PROP_LAST];
+
+G_DEFINE_TYPE_WITH_CODE (NleComposition, nle_composition, NLE_TYPE_OBJECT,
+ G_ADD_PRIVATE (NleComposition)
+ _do_init);
+
+#define OBJECT_IN_ACTIVE_SEGMENT(comp,element) \
+ ((NLE_OBJECT_START(element) < comp->priv->current_stack_stop) && \
+ (NLE_OBJECT_STOP(element) >= comp->priv->current_stack_start))
+
+static void nle_composition_dispose (GObject * object);
+static void nle_composition_finalize (GObject * object);
+static void nle_composition_reset (NleComposition * comp);
+
+static gboolean nle_composition_add_object (GstBin * bin, GstElement * element);
+
+static gboolean
+nle_composition_remove_object (GstBin * bin, GstElement * element);
+
+static GstStateChangeReturn
+nle_composition_change_state (GstElement * element, GstStateChange transition);
+
+static inline void nle_composition_reset_target_pad (NleComposition * comp);
+
+static gboolean
+seek_handling (NleComposition * comp, gint32 seqnum,
+ NleUpdateStackReason update_stack_reason);
+static gint objects_start_compare (NleObject * a, NleObject * b);
+static gint objects_stop_compare (NleObject * a, NleObject * b);
+static GstClockTime get_current_position (NleComposition * comp);
+
+static gboolean update_pipeline (NleComposition * comp,
+ GstClockTime currenttime, gint32 seqnum,
+ NleUpdateStackReason update_stack_reason);
+static gboolean nle_composition_commit_func (NleObject * object,
+ gboolean recurse);
+static void update_start_stop_duration (NleComposition * comp);
+
+static gboolean
+nle_composition_event_handler (GstPad * ghostpad, GstObject * parent,
+ GstEvent * event);
+static void _relink_single_node (NleComposition * comp, GNode * node,
+ GstEvent * toplevel_seek);
+static void _update_pipeline_func (NleComposition * comp,
+ UpdateCompositionData * ucompo);
+static void _commit_func (NleComposition * comp,
+ UpdateCompositionData * ucompo);
+static GstEvent *get_new_seek_event (NleComposition * comp, gboolean initial,
+ gboolean updatestoponly);
+static gboolean _nle_composition_add_object (NleComposition * comp,
+ NleObject * object);
+static gboolean _nle_composition_remove_object (NleComposition * comp,
+ NleObject * object);
+static void _deactivate_stack (NleComposition * comp,
+ gboolean flush_downstream);
+static gboolean _set_real_eos_seqnum_from_seek (NleComposition * comp,
+ GstEvent * event);
+static void _emit_commited_signal_func (NleComposition * comp, gpointer udata);
+static void _restart_task (NleComposition * comp);
+static void
+_add_action (NleComposition * comp, GCallback func, gpointer data,
+ gint priority);
+static gboolean
+_is_ready_to_restart_task (NleComposition * comp, GstEvent * event);
+
+
+/* COMP_REAL_START: actual position to start current playback at. */
+#define COMP_REAL_START(comp) \
+ (MAX (comp->priv->segment->start, NLE_OBJECT_START (comp)))
+
+#define COMP_REAL_STOP(comp) \
+ (GST_CLOCK_TIME_IS_VALID (comp->priv->segment->stop) ? \
+ (MIN (comp->priv->segment->stop, NLE_OBJECT_STOP (comp))) : \
+ NLE_OBJECT_STOP (comp))
+
+#define ACTIONS_LOCK(comp) G_STMT_START { \
+ GST_LOG_OBJECT (comp, "Getting ACTIONS_LOCK in thread %p", \
+ g_thread_self()); \
+ g_mutex_lock(&((NleComposition*)comp)->priv->actions_lock); \
+ GST_LOG_OBJECT (comp, "Got ACTIONS_LOCK in thread %p", \
+ g_thread_self()); \
+} G_STMT_END
+
+#define ACTIONS_UNLOCK(comp) G_STMT_START { \
+ g_mutex_unlock(&((NleComposition*)comp)->priv->actions_lock); \
+ GST_LOG_OBJECT (comp, "Unlocked ACTIONS_LOCK in thread %p", \
+ g_thread_self()); \
+} G_STMT_END
+
+#define WAIT_FOR_AN_ACTION(comp) G_STMT_START { \
+ GST_LOG_OBJECT (comp, "Waiting for an action in thread %p", \
+ g_thread_self()); \
+ g_cond_wait(&((NleComposition*)comp)->priv->actions_cond, \
+ &((NleComposition*)comp)->priv->actions_lock); \
+ GST_LOG_OBJECT (comp, "Done WAITING for an action in thread %p", \
+ g_thread_self()); \
+} G_STMT_END
+
+#define SIGNAL_NEW_ACTION(comp) G_STMT_START { \
+ GST_LOG_OBJECT (comp, "Signalling new action from thread %p", \
+ g_thread_self()); \
+ g_cond_signal(&((NleComposition*)comp)->priv->actions_cond); \
+} G_STMT_END
+
+#define GET_TASK_LOCK(comp) (&(NLE_COMPOSITION(comp)->task_rec_lock))
+
+static inline gboolean
+_have_to_flush_downstream (NleUpdateStackReason update_reason)
+{
+ if (update_reason == COMP_UPDATE_STACK_ON_COMMIT ||
+ update_reason == COMP_UPDATE_STACK_ON_SEEK ||
+ update_reason == COMP_UPDATE_STACK_INITIALIZE)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+_assert_proper_thread (NleComposition * comp)
+{
+ if (comp->task && gst_task_get_state (comp->task) != GST_TASK_STOPPED &&
+ g_thread_self () != comp->task->thread) {
+ g_warning ("Trying to touch children in a thread different from"
+ " its dedicated thread!");
+ }
+}
+
+static void
+_remove_actions_for_type (NleComposition * comp, GCallback callback)
+{
+ GList *tmp;
+
+ ACTIONS_LOCK (comp);
+
+ GST_LOG_OBJECT (comp, "finding action[callback=%s], action count = %d",
+ GST_DEBUG_FUNCPTR_NAME (callback), g_list_length (comp->priv->actions));
+ tmp = g_list_first (comp->priv->actions);
+ while (tmp != NULL) {
+ Action *act = tmp->data;
+ GList *removed = NULL;
+
+ if (ACTION_CALLBACK (act) == callback) {
+ GST_LOG_OBJECT (comp, "remove action for callback %s",
+ GST_DEBUG_FUNCPTR_NAME (callback));
+ removed = tmp;
+ g_closure_unref ((GClosure *) act);
+ comp->priv->actions = g_list_remove_link (comp->priv->actions, removed);
+ }
+
+ tmp = g_list_next (tmp);
+ if (removed)
+ g_list_free (removed);
+ }
+
+ ACTIONS_UNLOCK (comp);
+}
+
+static void
+_execute_actions (NleComposition * comp)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+ ACTIONS_LOCK (comp);
+ if (priv->running == FALSE) {
+ GST_DEBUG_OBJECT (comp, "Not running anymore");
+
+ ACTIONS_UNLOCK (comp);
+ return;
+ }
+
+ if (priv->actions == NULL)
+ WAIT_FOR_AN_ACTION (comp);
+
+ if (comp->priv->running == FALSE) {
+ GST_INFO_OBJECT (comp, "Done waiting but not running anymore");
+
+ ACTIONS_UNLOCK (comp);
+ return;
+ }
+
+ if (priv->actions) {
+ GValue params[1] = { G_VALUE_INIT };
+ GList *lact;
+
+ GST_LOG_OBJECT (comp, "scheduled actions [%d]",
+ g_list_length (priv->actions));
+
+ g_value_init (¶ms[0], G_TYPE_OBJECT);
+ g_value_set_object (¶ms[0], comp);
+
+ lact = g_list_first (priv->actions);
+ priv->actions = g_list_remove_link (priv->actions, lact);
+ priv->current_action = lact->data;
+ ACTIONS_UNLOCK (comp);
+
+ GST_INFO_OBJECT (comp, "Invoking %p:%s",
+ lact->data, GST_DEBUG_FUNCPTR_NAME ((ACTION_CALLBACK (lact->data))));
+ g_closure_invoke (lact->data, NULL, 1, params, NULL);
+ g_value_unset (¶ms[0]);
+
+ ACTIONS_LOCK (comp);
+ g_closure_unref (lact->data);
+ g_list_free (lact);
+ priv->current_action = NULL;
+ ACTIONS_UNLOCK (comp);
+
+ GST_LOG_OBJECT (comp, "remaining actions [%d]",
+ g_list_length (priv->actions));
+ } else {
+ ACTIONS_UNLOCK (comp);
+ }
+}
+
+static void
+_start_task (NleComposition * comp)
+{
+ GstTask *task;
+
+ ACTIONS_LOCK (comp);
+ comp->priv->running = TRUE;
+ ACTIONS_UNLOCK (comp);
+
+ GST_OBJECT_LOCK (comp);
+
+ task = comp->task;
+ if (task == NULL) {
+ gchar *taskname =
+ g_strdup_printf ("%s_update_management", GST_OBJECT_NAME (comp));
+
+ task = gst_task_new ((GstTaskFunction) _execute_actions, comp, NULL);
+ gst_object_set_name (GST_OBJECT_CAST (task), taskname);
+ gst_task_set_lock (task, GET_TASK_LOCK (comp));
+ GST_DEBUG_OBJECT (comp, "created task %p", task);
+ comp->task = task;
+ g_free (taskname);
+ }
+
+ gst_task_set_state (task, GST_TASK_STARTED);
+ GST_OBJECT_UNLOCK (comp);
+}
+
+static gboolean
+_stop_task (NleComposition * comp)
+{
+ gboolean res = TRUE;
+ GstTask *task;
+
+ GST_INFO_OBJECT (comp, "Stoping children management task");
+
+ ACTIONS_LOCK (comp);
+ comp->priv->running = FALSE;
+
+ /* Make sure we do not stay blocked trying to execute an action */
+ SIGNAL_NEW_ACTION (comp);
+ ACTIONS_UNLOCK (comp);
+
+ GST_DEBUG_OBJECT (comp, "stop task");
+
+ GST_OBJECT_LOCK (comp);
+ task = comp->task;
+ if (task == NULL)
+ goto no_task;
+ comp->task = NULL;
+ res = gst_task_set_state (task, GST_TASK_STOPPED);
+ GST_OBJECT_UNLOCK (comp);
+
+ if (!gst_task_join (task))
+ goto join_failed;
+
+ gst_object_unref (task);
+
+ return res;
+
+no_task:
+ {
+ GST_OBJECT_UNLOCK (comp);
+
+ /* this is not an error */
+ return TRUE;
+ }
+join_failed:
+ {
+ /* this is bad, possibly the application tried to join the task from
+ * the task's thread. We install the task again so that it will be stopped
+ * again from the right thread next time hopefully. */
+ GST_OBJECT_LOCK (comp);
+ GST_DEBUG_OBJECT (comp, "join failed");
+ /* we can only install this task if there was no other task */
+ if (comp->task == NULL)
+ comp->task = task;
+ GST_OBJECT_UNLOCK (comp);
+
+ return FALSE;
+ }
+
+ return res;
+}
+
+static void
+_post_start_composition_update (NleComposition * comp,
+ gint32 seqnum, NleUpdateStackReason reason)
+{
+ GstMessage *msg;
+
+ msg = gst_message_new_element (GST_OBJECT (comp),
+ gst_structure_new ("NleCompositionStartUpdate",
+ "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], NULL));
+
+ gst_message_set_seqnum (msg, seqnum);
+ gst_element_post_message (GST_ELEMENT (comp), msg);
+}
+
+static void
+_post_start_composition_update_done (NleComposition * comp,
+ gint32 seqnum, NleUpdateStackReason reason)
+{
+ GstMessage *msg = gst_message_new_element (GST_OBJECT (comp),
+ gst_structure_new ("NleCompositionUpdateDone",
+ "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason],
+ NULL));
+
+ gst_message_set_seqnum (msg, seqnum);
+ gst_element_post_message (GST_ELEMENT (comp), msg);
+}
+
+static void
+_seek_pipeline_func (NleComposition * comp, SeekData * seekd)
+{
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ NleCompositionPrivate *priv = comp->priv;
+
+ gst_event_parse_seek (seekd->event, &rate, &format, &flags,
+ &cur_type, &cur, &stop_type, &stop);
+
+ GST_DEBUG_OBJECT (seekd->comp,
+ "start:%" GST_TIME_FORMAT " -- stop:%" GST_TIME_FORMAT " flags:%d",
+ GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), flags);
+
+ gst_segment_do_seek (priv->segment,
+ rate, format, flags, cur_type, cur, stop_type, stop, NULL);
+ gst_segment_do_seek (priv->seek_segment,
+ rate, format, flags, cur_type, cur, stop_type, stop, NULL);
+
+ GST_DEBUG_OBJECT (seekd->comp, "Segment now has flags:%d",
+ priv->segment->flags);
+
+ /* FIXME: The idea was to avoid seeking on a stack if we know we will endup
+ * passed the end, but then we loose the flush, wich leads to hangs. Long
+ * term, we should just flush the stack instead to avoid the double seek. */
+#if 0
+ if (priv->segment->start >= NLE_OBJECT_STOP (seekd->comp)) {
+ GST_INFO_OBJECT (seekd->comp,
+ "Start %" GST_TIME_FORMAT " > comp->stop: %" GST_TIME_FORMAT
+ " Not seeking", GST_TIME_ARGS (priv->segment->start),
+ GST_TIME_ARGS (NLE_OBJECT_STOP (seekd->comp)));
+ GST_FIXME_OBJECT (seekd->comp, "HANDLE error async!");
+ return;
+ }
+#endif
+
+ _post_start_composition_update (seekd->comp,
+ gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK);
+
+ /* crop the segment start/stop values */
+ /* Only crop segment start value if we don't have a default object */
+ if (priv->expandables == NULL)
+ priv->segment->start =
+ MAX (priv->segment->start, NLE_OBJECT_START (seekd->comp));
+ priv->segment->stop =
+ MIN (priv->segment->stop, NLE_OBJECT_STOP (seekd->comp));
+
+ priv->next_base_time = 0;
+
+ seek_handling (seekd->comp, gst_event_get_seqnum (seekd->event),
+ COMP_UPDATE_STACK_ON_SEEK);
+
+ _post_start_composition_update_done (seekd->comp,
+ gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK);
+}
+
+/* Must be called with OBJECTS_LOCK taken */
+static void
+_process_pending_entries (NleComposition * comp)
+{
+ NleObject *object;
+ GHashTableIter iter;
+ gboolean deactivated_stack = FALSE;
+
+ NleCompositionPrivate *priv = comp->priv;
+
+ g_hash_table_iter_init (&iter, priv->pending_io);
+ while (g_hash_table_iter_next (&iter, (gpointer *) & object, NULL)) {
+ if (g_hash_table_contains (priv->objects_hash, object)) {
+
+ if (GST_OBJECT_PARENT (object) == GST_OBJECT_CAST (priv->current_bin) &&
+ deactivated_stack == FALSE) {
+ deactivated_stack = TRUE;
+
+ _deactivate_stack (comp, TRUE);
+ }
+
+ _nle_composition_remove_object (comp, object);
+ } else {
+ /* take a new ref on object as the current one will be released when
+ * object is removed from pending_io */
+ _nle_composition_add_object (comp, gst_object_ref (object));
+ }
+ }
+
+ g_hash_table_remove_all (priv->pending_io);
+}
+
+
+static inline gboolean
+_commit_values (NleComposition * comp)
+{
+ GList *tmp;
+ gboolean commited = FALSE;
+ NleCompositionPrivate *priv = comp->priv;
+
+ for (tmp = priv->objects_start; tmp; tmp = tmp->next) {
+ if (nle_object_commit (tmp->data, TRUE))
+ commited = TRUE;
+ }
+
+ GST_DEBUG_OBJECT (comp, "Linking up commit vmethod");
+ commited |= NLE_OBJECT_CLASS (parent_class)->commit (NLE_OBJECT (comp), TRUE);
+
+ return commited;
+}
+
+static gboolean
+_commit_all_values (NleComposition * comp)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+ priv->next_base_time = 0;
+
+ _process_pending_entries (comp);
+
+ if (_commit_values (comp) == FALSE) {
+
+ return FALSE;;
+ }
+
+ /* The topology of the composition might have changed, update the lists */
+ priv->objects_start = g_list_sort
+ (priv->objects_start, (GCompareFunc) objects_start_compare);
+ priv->objects_stop = g_list_sort
+ (priv->objects_stop, (GCompareFunc) objects_stop_compare);
+
+ return TRUE;
+}
+
+static gboolean
+_initialize_stack_func (NleComposition * comp, UpdateCompositionData * ucompo)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+
+ _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason);
+
+ _commit_all_values (comp);
+ update_start_stop_duration (comp);
+ comp->priv->next_base_time = 0;
+ /* set ghostpad target */
+ if (!(update_pipeline (comp, COMP_REAL_START (comp),
+ ucompo->seqnum, COMP_UPDATE_STACK_INITIALIZE))) {
+ GST_FIXME_OBJECT (comp, "PLEASE signal state change failure ASYNC");
+ }
+
+ _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason);
+ priv->initialized = TRUE;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+_remove_object_func (NleComposition * comp, ChildIOData * childio)
+{
+ NleObject *object = childio->object;
+
+ NleCompositionPrivate *priv = comp->priv;
+ NleObject *in_pending_io;
+
+ in_pending_io = g_hash_table_lookup (priv->pending_io, object);
+
+ if (!g_hash_table_contains (priv->objects_hash, object)) {
+ if (in_pending_io) {
+ GST_INFO_OBJECT (comp, "Object %" GST_PTR_FORMAT " was marked"
+ " for addition, removing it from the addition list", object);
+
+ g_hash_table_remove (priv->pending_io, object);
+ return;
+ }
+
+ GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
+ " not in the composition", object);
+
+ return;
+ }
+
+ if (in_pending_io) {
+ GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
+ " for removal", object);
+
+ return;
+ }
+
+ g_hash_table_add (priv->pending_io, gst_object_ref (object));
+
+ return;
+}
+
+static void
+_add_remove_object_action (NleComposition * comp, NleObject * object)
+{
+ ChildIOData *childio = g_slice_new0 (ChildIOData);
+
+ GST_DEBUG_OBJECT (comp, "Adding Action");
+
+ childio->comp = comp;
+ childio->object = object;
+
+ _add_action (comp, G_CALLBACK (_remove_object_func),
+ childio, G_PRIORITY_DEFAULT);
+}
+
+static void
+_add_object_func (NleComposition * comp, ChildIOData * childio)
+{
+ NleObject *object = childio->object;
+ NleCompositionPrivate *priv = comp->priv;
+ NleObject *in_pending_io;
+
+ in_pending_io = g_hash_table_lookup (priv->pending_io, object);
+
+ if (g_hash_table_contains (priv->objects_hash, object)) {
+
+ if (in_pending_io) {
+ GST_INFO_OBJECT (comp, "Object already in but marked in pendings"
+ " removing from pendings");
+ g_hash_table_remove (priv->pending_io, object);
+
+ return;
+ }
+ GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
+ " already in the composition", object);
+
+ return;
+ }
+
+ if (in_pending_io) {
+ GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
+ " for addition", object);
+
+ return;
+ }
+
+ /* current reference is hold by the action and will be released with it,
+ * so take a new one */
+ g_hash_table_add (priv->pending_io, gst_object_ref (object));
+}
+
+static void
+_add_add_object_action (NleComposition * comp, NleObject * object)
+{
+ ChildIOData *childio = g_slice_new0 (ChildIOData);
+
+ GST_DEBUG_OBJECT (comp, "Adding Action");
+
+ childio->comp = comp;
+ childio->object = object;
+
+ _add_action (comp, G_CALLBACK (_add_object_func), childio,
+ G_PRIORITY_DEFAULT);
+}
+
+static void
+_free_action (gpointer udata, Action * action)
+{
+ GST_LOG ("freeing %p action for %s", action,
+ GST_DEBUG_FUNCPTR_NAME (ACTION_CALLBACK (action)));
+ if (ACTION_CALLBACK (action) == _seek_pipeline_func) {
+ SeekData *seekd = (SeekData *) udata;
+
+ gst_event_unref (seekd->event);
+ g_slice_free (SeekData, seekd);
+ } else if (ACTION_CALLBACK (action) == _add_object_func) {
+ ChildIOData *iodata = (ChildIOData *) udata;
+
+ gst_object_unref (iodata->object);
+ g_slice_free (ChildIOData, iodata);
+ } else if (ACTION_CALLBACK (action) == _remove_object_func) {
+ g_slice_free (ChildIOData, udata);
+ } else if (ACTION_CALLBACK (action) == _update_pipeline_func ||
+ ACTION_CALLBACK (action) == _commit_func ||
+ ACTION_CALLBACK (action) == _initialize_stack_func) {
+ g_slice_free (UpdateCompositionData, udata);
+ }
+}
+
+static void
+_add_action_locked (NleComposition * comp, GCallback func,
+ gpointer data, gint priority)
+{
+ Action *action;
+ NleCompositionPrivate *priv = comp->priv;
+
+ action = (Action *) g_closure_new_simple (sizeof (Action), data);
+ g_closure_add_finalize_notifier ((GClosure *) action, data,
+ (GClosureNotify) _free_action);
+ ACTION_CALLBACK (action) = func;
+
+ action->priority = priority;
+ g_closure_set_marshal ((GClosure *) action, g_cclosure_marshal_VOID__VOID);
+
+ GST_INFO_OBJECT (comp, "Adding Action for function: %p:%s",
+ action, GST_DEBUG_FUNCPTR_NAME (func));
+
+ if (func == G_CALLBACK (_emit_commited_signal_func))
+ priv->actions = g_list_prepend (priv->actions, action);
+ else
+ priv->actions = g_list_append (priv->actions, action);
+
+ GST_LOG_OBJECT (comp, "the number of remaining actions: %d",
+ g_list_length (priv->actions));
+
+ SIGNAL_NEW_ACTION (comp);
+}
+
+static void
+_add_action (NleComposition * comp, GCallback func,
+ gpointer data, gint priority)
+{
+ ACTIONS_LOCK (comp);
+ _add_action_locked (comp, func, data, priority);
+ ACTIONS_UNLOCK (comp);
+}
+
+static void
+_add_seek_action (NleComposition * comp, GstEvent * event)
+{
+ SeekData *seekd;
+ GList *tmp;
+ guint32 seqnum = gst_event_get_seqnum (event);
+
+ ACTIONS_LOCK (comp);
+ /* Check if this is our current seqnum */
+ if (seqnum == comp->priv->next_eos_seqnum) {
+ GST_DEBUG_OBJECT (comp, "Not adding Action, same seqnum as previous seek");
+ ACTIONS_UNLOCK (comp);
+ return;
+ }
+
+ /* Check if this seqnum is already queued up but not handled yet */
+ for (tmp = comp->priv->actions; tmp != NULL; tmp = tmp->next) {
+ Action *act = tmp->data;
+
+ if (ACTION_CALLBACK (act) == G_CALLBACK (_seek_pipeline_func)) {
+ SeekData *tmp_data = ((GClosure *) act)->data;
+
+ if (gst_event_get_seqnum (tmp_data->event) == seqnum) {
+ GST_DEBUG_OBJECT (comp,
+ "Not adding Action, same seqnum as previous seek");
+ ACTIONS_UNLOCK (comp);
+ return;
+ }
+ }
+ }
+
+ /* Check if this seqnum is currently being handled */
+ if (comp->priv->current_action) {
+ Action *act = comp->priv->current_action;
+ if (ACTION_CALLBACK (act) == G_CALLBACK (_seek_pipeline_func)) {
+ SeekData *tmp_data = ((GClosure *) act)->data;
+
+ if (gst_event_get_seqnum (tmp_data->event) == seqnum) {
+ GST_DEBUG_OBJECT (comp,
+ "Not adding Action, same seqnum as previous seek");
+ ACTIONS_UNLOCK (comp);
+ return;
+ }
+ }
+ }
+
+ GST_DEBUG_OBJECT (comp, "Adding Action");
+
+ seekd = g_slice_new0 (SeekData);
+ seekd->comp = comp;
+ seekd->event = event;
+
+ comp->priv->next_eos_seqnum = 0;
+ comp->priv->real_eos_seqnum = 0;
+ _add_action_locked (comp, G_CALLBACK (_seek_pipeline_func), seekd,
+ G_PRIORITY_DEFAULT);
+
+ ACTIONS_UNLOCK (comp);
+}
+
+static void
+_remove_update_actions (NleComposition * comp)
+{
+ _remove_actions_for_type (comp, G_CALLBACK (_update_pipeline_func));
+}
+
+static void
+_remove_seek_actions (NleComposition * comp)
+{
+ _remove_actions_for_type (comp, G_CALLBACK (_seek_pipeline_func));
+}
+
+static void
+_add_update_compo_action (NleComposition * comp,
+ GCallback callback, NleUpdateStackReason reason)
+{
+ UpdateCompositionData *ucompo = g_slice_new0 (UpdateCompositionData);
+
+ ucompo->comp = comp;
+ ucompo->reason = reason;
+ ucompo->seqnum = gst_util_seqnum_next ();
+
+ GST_INFO_OBJECT (comp, "Updating because: %s -- Setting seqnum: %i",
+ UPDATE_PIPELINE_REASONS[reason], ucompo->seqnum);
+
+ _add_action (comp, callback, ucompo, G_PRIORITY_DEFAULT);
+}
+
+static void
+nle_composition_handle_message (GstBin * bin, GstMessage * message)
+{
+ NleComposition *comp = (NleComposition *) bin;
+ NleCompositionPrivate *priv = comp->priv;
+
+ if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR &&
+ (priv->tearing_down_stack || priv->suppress_child_error)) {
+ GST_FIXME_OBJECT (comp, "Dropping %" GST_PTR_FORMAT " message from "
+ " %" GST_PTR_FORMAT " tearing down: %d, suppressing error: %d",
+ message, GST_MESSAGE_SRC (message), priv->tearing_down_stack,
+ priv->suppress_child_error);
+ goto drop;
+ } else if (comp->priv->tearing_down_stack) {
+ GST_DEBUG_OBJECT (comp, "Dropping message %" GST_PTR_FORMAT " from "
+ "object being teared down to READY!", message);
+ goto drop;
+ }
+
+ GST_BIN_CLASS (parent_class)->handle_message (bin, message);
+
+ return;
+
+drop:
+ gst_message_unref (message);
+
+ return;
+}
+
+static void
+nle_composition_class_init (NleCompositionClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBinClass *gstbin_class;
+ NleObjectClass *nleobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbin_class = (GstBinClass *) klass;
+ nleobject_class = (NleObjectClass *) klass;
+
+ gst_element_class_set_static_metadata (gstelement_class,
+ "GNonLin Composition", "Filter/Editor", "Combines NLE objects",
+ "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>,"
+ " Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>,"
+ " Thibault Saunier <tsaunier@gnome.org>");
+
+ gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_composition_dispose);
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (nle_composition_finalize);
+
+ gstelement_class->change_state = nle_composition_change_state;
+
+ gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_composition_add_object);
+ gstbin_class->remove_element =
+ GST_DEBUG_FUNCPTR (nle_composition_remove_object);
+ gstbin_class->handle_message =
+ GST_DEBUG_FUNCPTR (nle_composition_handle_message);
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &nle_composition_src_template);
+
+ /* Get the paramspec of the NleObject klass so we can do
+ * fast notifies */
+ nleobject_properties[NLEOBJECT_PROP_START] =
+ g_object_class_find_property (gobject_class, "start");
+ nleobject_properties[NLEOBJECT_PROP_STOP] =
+ g_object_class_find_property (gobject_class, "stop");
+ nleobject_properties[NLEOBJECT_PROP_DURATION] =
+ g_object_class_find_property (gobject_class, "duration");
+
+ _signals[COMMITED_SIGNAL] =
+ g_signal_new ("commited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
+ G_TYPE_BOOLEAN);
+
+ GST_DEBUG_REGISTER_FUNCPTR (_seek_pipeline_func);
+ GST_DEBUG_REGISTER_FUNCPTR (_remove_object_func);
+ GST_DEBUG_REGISTER_FUNCPTR (_add_object_func);
+ GST_DEBUG_REGISTER_FUNCPTR (_update_pipeline_func);
+ GST_DEBUG_REGISTER_FUNCPTR (_commit_func);
+ GST_DEBUG_REGISTER_FUNCPTR (_emit_commited_signal_func);
+ GST_DEBUG_REGISTER_FUNCPTR (_initialize_stack_func);
+
+ /* Just be useless, so the compiler does not warn us
+ * about our uselessness */
+ nleobject_class->commit = nle_composition_commit_func;
+
+}
+
+static void
+nle_composition_init (NleComposition * comp)
+{
+ NleCompositionPrivate *priv;
+
+ GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_SOURCE);
+ GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_COMPOSITION);
+
+ priv = nle_composition_get_instance_private (comp);
+ priv->objects_start = NULL;
+ priv->objects_stop = NULL;
+
+ priv->segment = gst_segment_new ();
+ priv->seek_segment = gst_segment_new ();
+
+ g_rec_mutex_init (&comp->task_rec_lock);
+
+ priv->objects_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ g_mutex_init (&priv->actions_lock);
+ g_cond_init (&priv->actions_cond);
+
+ priv->pending_io = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ gst_object_unref, NULL);
+
+ comp->priv = priv;
+
+ priv->current_bin = gst_bin_new ("current-bin");
+ gst_bin_add (GST_BIN (comp), priv->current_bin);
+
+ nle_composition_reset (comp);
+
+ priv->nle_event_pad_func = GST_PAD_EVENTFUNC (NLE_OBJECT_SRC (comp));
+ gst_pad_set_event_function (NLE_OBJECT_SRC (comp),
+ GST_DEBUG_FUNCPTR (nle_composition_event_handler));
+}
+
+static void
+_remove_each_nleobj (gpointer data, gpointer udata)
+{
+ NleComposition *comp = NLE_COMPOSITION (udata);
+ NleObject *nleobj = NLE_OBJECT (data);
+
+ _nle_composition_remove_object (NLE_COMPOSITION (comp), NLE_OBJECT (nleobj));
+}
+
+static void
+_remove_each_action (gpointer data)
+{
+ Action *action = (Action *) (data);
+
+ GST_LOG ("remove action %p for %s", action,
+ GST_DEBUG_FUNCPTR_NAME (ACTION_CALLBACK (action)));
+ g_closure_invalidate ((GClosure *) action);
+ g_closure_unref ((GClosure *) action);
+}
+
+static void
+nle_composition_dispose (GObject * object)
+{
+ NleComposition *comp = NLE_COMPOSITION (object);
+ NleCompositionPrivate *priv = comp->priv;
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ g_list_foreach (priv->objects_start, _remove_each_nleobj, comp);
+ g_list_free (priv->objects_start);
+
+ g_list_foreach (priv->expandables, _remove_each_nleobj, comp);
+ g_list_free (priv->expandables);
+
+ g_list_foreach (priv->objects_stop, _remove_each_nleobj, comp);
+ g_list_free (priv->objects_stop);
+
+ g_list_free_full (priv->actions, (GDestroyNotify) _remove_each_action);
+
+ nle_composition_reset_target_pad (comp);
+
+ if (priv->pending_io) {
+ g_hash_table_unref (priv->pending_io);
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+nle_composition_finalize (GObject * object)
+{
+ NleComposition *comp = NLE_COMPOSITION (object);
+ NleCompositionPrivate *priv = comp->priv;
+
+ _assert_proper_thread (comp);
+
+ if (priv->current) {
+ g_node_destroy (priv->current);
+ priv->current = NULL;
+ }
+
+ g_hash_table_destroy (priv->objects_hash);
+
+ gst_segment_free (priv->segment);
+ gst_segment_free (priv->seek_segment);
+
+ g_rec_mutex_clear (&comp->task_rec_lock);
+
+ g_mutex_clear (&priv->actions_lock);
+ g_cond_clear (&priv->actions_cond);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/* signal_duration_change
+ * Creates a new GST_MESSAGE_DURATION_CHANGED with the currently configured
+ * composition duration and sends that on the bus.
+ */
+static inline void
+signal_duration_change (NleComposition * comp)
+{
+ gst_element_post_message (GST_ELEMENT_CAST (comp),
+ gst_message_new_duration_changed (GST_OBJECT_CAST (comp)));
+}
+
+static gboolean
+_remove_child (GValue * item, GValue * ret G_GNUC_UNUSED, GstBin * bin)
+{
+ GstElement *child = g_value_get_object (item);
+
+ if (NLE_IS_OPERATION (child))
+ nle_operation_hard_cleanup (NLE_OPERATION (child));
+
+
+ gst_bin_remove (bin, child);
+
+ return TRUE;
+}
+
+static void
+_empty_bin (GstBin * bin)
+{
+ GstIterator *children;
+
+ children = gst_bin_iterate_elements (bin);
+
+ while (G_UNLIKELY (gst_iterator_fold (children,
+ (GstIteratorFoldFunction) _remove_child, NULL,
+ bin) == GST_ITERATOR_RESYNC)) {
+ gst_iterator_resync (children);
+ }
+
+ gst_iterator_free (children);
+}
+
+static void
+nle_composition_reset (NleComposition * comp)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+ GST_DEBUG_OBJECT (comp, "resetting");
+
+ _assert_proper_thread (comp);
+
+ priv->current_stack_start = GST_CLOCK_TIME_NONE;
+ priv->current_stack_stop = GST_CLOCK_TIME_NONE;
+ priv->next_base_time = 0;
+
+ gst_segment_init (priv->segment, GST_FORMAT_TIME);
+ gst_segment_init (priv->seek_segment, GST_FORMAT_TIME);
+
+ if (priv->current)
+ g_node_destroy (priv->current);
+ priv->current = NULL;
+
+ nle_composition_reset_target_pad (comp);
+
+ priv->initialized = FALSE;
+ priv->send_stream_start = TRUE;
+ priv->real_eos_seqnum = 0;
+ priv->next_eos_seqnum = 0;
+ priv->flush_seqnum = 0;
+
+ _empty_bin (GST_BIN_CAST (priv->current_bin));
+
+ GST_DEBUG_OBJECT (comp, "Composition now resetted");
+}
+
+static GstPadProbeReturn
+ghost_event_probe_handler (GstPad * ghostpad G_GNUC_UNUSED,
+ GstPadProbeInfo * info, NleComposition * comp)
+{
+ GstPadProbeReturn retval = GST_PAD_PROBE_OK;
+ NleCompositionPrivate *priv = comp->priv;
+ GstEvent *event;
+
+ if (GST_IS_BUFFER (info->data) ||
+ (GST_IS_QUERY (info->data) && GST_QUERY_IS_SERIALIZED (info->data))) {
+ if (priv->waiting_serialized_query_or_buffer) {
+ GST_INFO_OBJECT (comp, "update_pipeline DONE");
+ _restart_task (comp);
+ }
+
+ return GST_PAD_PROBE_OK;
+ }
+
+ event = GST_PAD_PROBE_INFO_EVENT (info);
+
+ GST_DEBUG_OBJECT (comp, "event: %s", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_STOP:
+ if (_is_ready_to_restart_task (comp, event))
+ _restart_task (comp);
+
+ if (gst_event_get_seqnum (event) != comp->priv->flush_seqnum) {
+ GST_INFO_OBJECT (comp, "Dropping flush stop %d -- %d",
+ gst_event_get_seqnum (event), priv->seqnum_to_restart_task);
+ retval = GST_PAD_PROBE_DROP;
+ } else {
+ GST_INFO_OBJECT (comp, "Forwarding our flush stop with seqnum %i",
+ comp->priv->flush_seqnum);
+ gst_event_unref (event);
+ event = gst_event_new_flush_stop (TRUE);
+ GST_PAD_PROBE_INFO_DATA (info) = event;
+ gst_event_set_seqnum (event, comp->priv->flush_seqnum);
+ comp->priv->flush_seqnum = 0;
+ }
+ break;
+ case GST_EVENT_FLUSH_START:
+ if (gst_event_get_seqnum (event) != comp->priv->flush_seqnum) {
+ GST_INFO_OBJECT (comp, "Dropping flush start");
+ retval = GST_PAD_PROBE_DROP;
+ } else {
+ GST_INFO_OBJECT (comp, "Forwarding our flush start with seqnum %i",
+ comp->priv->flush_seqnum);
+ }
+ break;
+ case GST_EVENT_STREAM_START:
+ if (g_atomic_int_compare_and_exchange (&priv->send_stream_start, TRUE,
+ FALSE)) {
+ /* FIXME: Do we want to create a new stream ID here? */
+ GST_DEBUG_OBJECT (comp, "forward stream-start %p", event);
+ } else {
+ GST_DEBUG_OBJECT (comp, "dropping stream-start %p", event);
+ retval = GST_PAD_PROBE_DROP;
+ }
+ break;
+ case GST_EVENT_SEGMENT:
+ {
+ guint64 rstart, rstop;
+ const GstSegment *segment;
+ GstSegment copy;
+ GstEvent *event2;
+ /* next_base_time */
+
+ if (_is_ready_to_restart_task (comp, event))
+ _restart_task (comp);
+
+ gst_event_parse_segment (event, &segment);
+ gst_segment_copy_into (segment, ©);
+
+ rstart =
+ gst_segment_to_running_time (segment, GST_FORMAT_TIME,
+ segment->start);
+ rstop =
+ gst_segment_to_running_time (segment, GST_FORMAT_TIME, segment->stop);
+ copy.base = comp->priv->next_base_time;
+ GST_DEBUG_OBJECT (comp,
+ "Updating base time to %" GST_TIME_FORMAT ", next:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (comp->priv->next_base_time),
+ GST_TIME_ARGS (comp->priv->next_base_time + rstop - rstart));
+ comp->priv->next_base_time += rstop - rstart;
+
+ event2 = gst_event_new_segment (©);
+ GST_EVENT_SEQNUM (event2) = GST_EVENT_SEQNUM (event);
+
+ GST_PAD_PROBE_INFO_DATA (info) = event2;
+ gst_event_unref (event);
+ }
+ break;
+ case GST_EVENT_TAG:
+ GST_DEBUG_OBJECT (comp, "Dropping tag: %" GST_PTR_FORMAT, info->data);
+ retval = GST_PAD_PROBE_DROP;
+ break;
+ case GST_EVENT_EOS:
+ {
+ gint seqnum = gst_event_get_seqnum (event);
+
+ GST_INFO_OBJECT (comp, "Got EOS, last EOS seqnum id : %i current "
+ "seq num is: %i", comp->priv->real_eos_seqnum, seqnum);
+
+ if (_is_ready_to_restart_task (comp, event)) {
+ GST_INFO_OBJECT (comp, "We got an EOS right after seeing the right"
+ " segment, restarting task");
+
+ _restart_task (comp);
+ }
+
+ if (g_atomic_int_compare_and_exchange (&comp->priv->real_eos_seqnum,
+ seqnum, 1)) {
+
+ GST_INFO_OBJECT (comp, "Got EOS for real, seq ID is %i, fowarding it",
+ seqnum);
+
+ return GST_PAD_PROBE_OK;
+ }
+
+ if (priv->next_eos_seqnum == seqnum)
+ _add_update_compo_action (comp, G_CALLBACK (_update_pipeline_func),
+ COMP_UPDATE_STACK_ON_EOS);
+ else
+ GST_INFO_OBJECT (comp,
+ "Got an EOS but it seqnum %i != next eos seqnum %i", seqnum,
+ priv->next_eos_seqnum);
+
+ retval = GST_PAD_PROBE_DROP;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static gint
+priority_comp (NleObject * a, NleObject * b)
+{
+ if (a->priority < b->priority)
+ return -1;
+
+ if (a->priority > b->priority)
+ return 1;
+
+ return 0;
+}
+
+static inline gboolean
+have_to_update_pipeline (NleComposition * comp,
+ NleUpdateStackReason update_stack_reason)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+ if (update_stack_reason == COMP_UPDATE_STACK_ON_EOS)
+ return TRUE;
+
+ GST_DEBUG_OBJECT (comp,
+ "segment[%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT "] current[%"
+ GST_TIME_FORMAT "--%" GST_TIME_FORMAT "]",
+ GST_TIME_ARGS (priv->segment->start),
+ GST_TIME_ARGS (priv->segment->stop),
+ GST_TIME_ARGS (priv->current_stack_start),
+ GST_TIME_ARGS (priv->current_stack_stop));
+
+ if (priv->segment->start < priv->current_stack_start)
+ return TRUE;
+
+ if (priv->segment->start >= priv->current_stack_stop)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+nle_composition_commit_func (NleObject * object, gboolean recurse)
+{
+ _add_update_compo_action (NLE_COMPOSITION (object),
+ G_CALLBACK (_commit_func), COMP_UPDATE_STACK_ON_COMMIT);
+
+ return TRUE;
+}
+
+/*
+ * get_new_seek_event:
+ *
+ * Returns a seek event for the currently configured segment
+ * and start/stop values
+ *
+ * The GstSegment and current_stack_start|stop must have been configured
+ * before calling this function.
+ */
+static GstEvent *
+get_new_seek_event (NleComposition * comp, gboolean initial,
+ gboolean updatestoponly)
+{
+ GstSeekFlags flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH;
+ gint64 start, stop;
+ GstSeekType starttype = GST_SEEK_TYPE_SET;
+ NleCompositionPrivate *priv = comp->priv;
+
+ GST_DEBUG_OBJECT (comp, "initial:%d", initial);
+ /* remove the seek flag */
+ if (!initial)
+ flags |= (GstSeekFlags) priv->segment->flags;
+
+ GST_DEBUG_OBJECT (comp,
+ "private->segment->start:%" GST_TIME_FORMAT " current_stack_start%"
+ GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->start),
+ GST_TIME_ARGS (priv->current_stack_start));
+
+ GST_DEBUG_OBJECT (comp,
+ "private->segment->stop:%" GST_TIME_FORMAT " current_stack_stop%"
+ GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop),
+ GST_TIME_ARGS (priv->current_stack_stop));
+
+ start = GST_CLOCK_TIME_IS_VALID (priv->segment->start)
+ ? MAX (priv->segment->start, priv->current_stack_start)
+ : priv->current_stack_start;
+ stop = GST_CLOCK_TIME_IS_VALID (priv->segment->stop)
+ ? MIN (priv->segment->stop, priv->current_stack_stop)
+ : priv->current_stack_stop;
+
+ if (updatestoponly) {
+ starttype = GST_SEEK_TYPE_NONE;
+ start = GST_CLOCK_TIME_NONE;
+ }
+
+ GST_DEBUG_OBJECT (comp,
+ "Created new seek event. Flags:%d, start:%" GST_TIME_FORMAT ", stop:%"
+ GST_TIME_FORMAT ", rate:%lf", flags, GST_TIME_ARGS (start),
+ GST_TIME_ARGS (stop), priv->segment->rate);
+
+ return gst_event_new_seek (priv->segment->rate,
+ priv->segment->format, flags, starttype, start, GST_SEEK_TYPE_SET, stop);
+}
+
+/* OBJECTS LOCK must be taken when calling this ! */
+static GstClockTime
+get_current_position (NleComposition * comp)
+{
+ GstPad *pad;
+ NleObject *obj;
+ NleCompositionPrivate *priv = comp->priv;
+ gboolean res;
+ gint64 value = GST_CLOCK_TIME_NONE;
+ GstObject *parent, *tmp;
+
+ GstPad *peer;
+
+ parent = gst_object_get_parent (GST_OBJECT (comp));
+ while ((tmp = parent)) {
+ if (NLE_IS_COMPOSITION (parent)) {
+ GstClockTime parent_position =
+ get_current_position (NLE_COMPOSITION (parent));
+
+ if (parent_position > NLE_OBJECT_STOP (comp)
+ || parent_position < NLE_OBJECT_START (comp)) {
+ GST_INFO_OBJECT (comp,
+ "Global position outside of subcomposition, returning TIME_NONE");
+
+ return GST_CLOCK_TIME_NONE;
+ }
+
+ value =
+ parent_position - NLE_OBJECT_START (comp) + NLE_OBJECT_INPOINT (comp);
+ }
+
+ if (GST_IS_PIPELINE (parent)) {
+ if (gst_element_query_position (GST_ELEMENT (parent), GST_FORMAT_TIME,
+ &value)) {
+
+ gst_object_unref (parent);
+ return value;
+ }
+ }
+
+
+ parent = gst_object_get_parent (GST_OBJECT (parent));
+ gst_object_unref (tmp);
+ }
+
+ /* Try querying position downstream */
+ peer = gst_pad_get_peer (NLE_OBJECT (comp)->srcpad);
+ if (peer) {
+ res = gst_pad_query_position (peer, GST_FORMAT_TIME, &value);
+ gst_object_unref (peer);
+
+ if (res) {
+ GST_DEBUG_OBJECT (comp,
+ "Successfully got downstream position %" GST_TIME_FORMAT,
+ GST_TIME_ARGS ((guint64) value));
+ goto beach;
+ }
+ }
+
+ GST_DEBUG_OBJECT (comp, "Downstream position query failed");
+
+ /* resetting format/value */
+ value = GST_CLOCK_TIME_NONE;
+
+ /* If downstream fails , try within the current stack */
+ if (!priv->current) {
+ GST_DEBUG_OBJECT (comp, "No current stack, can't send query");
+ goto beach;
+ }
+
+ obj = (NleObject *) priv->current->data;
+
+ pad = NLE_OBJECT_SRC (obj);
+ res = gst_pad_query_position (pad, GST_FORMAT_TIME, &value);
+
+ if (G_UNLIKELY (res == FALSE)) {
+ GST_WARNING_OBJECT (comp, "query position failed");
+ value = GST_CLOCK_TIME_NONE;
+ } else {
+ GST_LOG_OBJECT (comp, "Query returned %" GST_TIME_FORMAT,
+ GST_TIME_ARGS ((guint64) value));
+ }
+
+beach:
+
+ if (!GST_CLOCK_TIME_IS_VALID (value)) {
+ if (GST_CLOCK_TIME_IS_VALID (comp->priv->current_stack_start)) {
+ value = comp->priv->current_stack_start;
+ } else {
+ GST_INFO_OBJECT (comp, "Current position is unknown, " "setting it to 0");
+
+ value = 0;
+ }
+ }
+
+ return (guint64) value;
+}
+
+/* WITH OBJECTS LOCK TAKEN */
+static gboolean
+update_base_time (GNode * node, GstClockTime * timestamp)
+{
+ if (NLE_IS_OPERATION (node->data))
+ nle_operation_update_base_time (NLE_OPERATION (node->data), *timestamp);
+
+ return FALSE;
+}
+
+/* WITH OBJECTS LOCK TAKEN */
+static void
+update_operations_base_time (NleComposition * comp, gboolean reverse)
+{
+ GstClockTime timestamp;
+
+ if (reverse)
+ timestamp = comp->priv->segment->stop;
+ else
+ timestamp = comp->priv->segment->start;
+
+ g_node_traverse (comp->priv->current, G_IN_ORDER, G_TRAVERSE_ALL, -1,
+ (GNodeTraverseFunc) update_base_time, ×tamp);
+}
+
+/* WITH OBJECTS LOCK TAKEN */
+static gboolean
+_seek_current_stack (NleComposition * comp, GstEvent * event,
+ gboolean flush_downstream)
+{
+ gboolean res;
+ NleCompositionPrivate *priv = comp->priv;
+ GstPad *peer = gst_pad_get_peer (NLE_OBJECT_SRC (comp));
+
+ GST_INFO_OBJECT (comp, "Seeking itself %" GST_PTR_FORMAT, event);
+
+ if (!peer) {
+ GST_ERROR_OBJECT (comp, "Can't seek because no pad available - "
+ "no children in the composition ready to be used, the duration is 0, "
+ "or not committed yet");
+ return FALSE;
+ }
+
+ if (flush_downstream) {
+ priv->flush_seqnum = gst_event_get_seqnum (event);
+ GST_INFO_OBJECT (comp, "sending flushes downstream with seqnum %d",
+ priv->flush_seqnum);
+ }
+
+ priv->seeking_itself = TRUE;
+ res = gst_pad_push_event (peer, event);
+ priv->seeking_itself = FALSE;
+ gst_object_unref (peer);
+
+ GST_DEBUG_OBJECT (comp, "Done seeking");
+
+ return res;
+}
+
+/*
+ Figures out if pipeline needs updating.
+ Updates it and sends the seek event.
+ Sends flush events downstream if needed.
+ can be called by user_seek or segment_done
+
+ update_stack_reason: The reason for which we need to handle 'seek'
+*/
+
+static gboolean
+seek_handling (NleComposition * comp, gint32 seqnum,
+ NleUpdateStackReason update_stack_reason)
+{
+ GST_DEBUG_OBJECT (comp, "Seek handling update pipeline reason: %s",
+ UPDATE_PIPELINE_REASONS[update_stack_reason]);
+
+ if (have_to_update_pipeline (comp, update_stack_reason)) {
+ if (comp->priv->segment->rate >= 0.0)
+ update_pipeline (comp, comp->priv->segment->start, seqnum,
+ update_stack_reason);
+ else
+ update_pipeline (comp, comp->priv->segment->stop, seqnum,
+ update_stack_reason);
+ } else {
+ GstEvent *toplevel_seek = get_new_seek_event (comp, FALSE, FALSE);
+
+ gst_event_set_seqnum (toplevel_seek, seqnum);
+ _set_real_eos_seqnum_from_seek (comp, toplevel_seek);
+
+ _remove_update_actions (comp);
+ update_operations_base_time (comp, !(comp->priv->segment->rate >= 0.0));
+ _seek_current_stack (comp, toplevel_seek,
+ _have_to_flush_downstream (update_stack_reason));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+nle_composition_event_handler (GstPad * ghostpad, GstObject * parent,
+ GstEvent * event)
+{
+ NleComposition *comp = (NleComposition *) parent;
+ NleCompositionPrivate *priv = comp->priv;
+ gboolean res = TRUE;
+
+ GST_DEBUG_OBJECT (comp, "event type:%s", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ /* Queue up a seek action if this seek event does not come from
+ * ourselves. Due to a possible race condition around the
+ * seeking_itself flag, we also check if the seek comes from
+ * our task thread. The seeking_itself flag only works as an
+ * optimization */
+ GST_OBJECT_LOCK (comp);
+ if (!priv->seeking_itself || (comp->task
+ && gst_task_get_state (comp->task) != GST_TASK_STOPPED
+ && g_thread_self () != comp->task->thread)) {
+ GST_OBJECT_UNLOCK (comp);
+ _add_seek_action (comp, event);
+ event = NULL;
+ GST_FIXME_OBJECT (comp, "HANDLE seeking errors!");
+
+ return TRUE;
+ }
+ GST_OBJECT_UNLOCK (comp);
+ break;
+ }
+ case GST_EVENT_QOS:
+ {
+ gdouble prop;
+ GstQOSType qostype;
+ GstClockTimeDiff diff;
+ GstClockTime timestamp;
+
+ gst_event_parse_qos (event, &qostype, &prop, &diff, ×tamp);
+
+ GST_DEBUG_OBJECT (comp,
+ "timestamp:%" GST_TIME_FORMAT " segment.start:%" GST_TIME_FORMAT
+ " segment.stop:%" GST_TIME_FORMAT " current_stack_start%"
+ GST_TIME_FORMAT " current_stack_stop:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp),
+ GST_TIME_ARGS (priv->seek_segment->start),
+ GST_TIME_ARGS (priv->seek_segment->stop),
+ GST_TIME_ARGS (priv->current_stack_start),
+ GST_TIME_ARGS (priv->current_stack_stop));
+
+ /* The problem with QoS events is the following:
+ * At each new internal segment (i.e. when we re-arrange our internal
+ * elements) we send flushing seeks to those elements (to properly
+ * configure their playback range) but don't let the FLUSH events get
+ * downstream.
+ *
+ * The problem is that the QoS running timestamps we receive from
+ * downstream will not have taken into account those flush.
+ *
+ * What we need to do is to translate to our internal running timestamps
+ * which for each configured segment starts at 0 for those elements.
+ *
+ * The generic algorithm for the incoming running timestamp translation
+ * is therefore:
+ * (original_seek_time : original seek position received from usptream)
+ * (current_segment_start : Start position of the currently configured
+ * timeline segment)
+ *
+ * difference = original_seek_time - current_segment_start
+ * new_qos_position = upstream_qos_position - difference
+ *
+ * The new_qos_position is only valid when:
+ * * it applies to the current segment (difference > 0)
+ * * The QoS difference + timestamp is greater than the difference
+ *
+ */
+
+ if (GST_CLOCK_TIME_IS_VALID (priv->seek_segment->start)) {
+ GstClockTimeDiff curdiff;
+
+ /* We'll either create a new event or discard it */
+ gst_event_unref (event);
+
+ if (priv->segment->rate < 0.0)
+ curdiff = priv->seek_segment->stop - priv->current_stack_stop;
+ else
+ curdiff = priv->current_stack_start - priv->seek_segment->start;
+ GST_DEBUG ("curdiff %" GST_TIME_FORMAT, GST_TIME_ARGS (curdiff));
+ if ((curdiff != 0) && ((timestamp < curdiff)
+ || (curdiff > timestamp + diff))) {
+ GST_DEBUG_OBJECT (comp,
+ "QoS event outside of current segment, discarding");
+ /* The QoS timestamp is before the currently set-up pipeline */
+ goto beach;
+ }
+
+ /* Substract the amount of running time we've already outputted
+ * until the currently configured pipeline from the QoS timestamp.*/
+ timestamp -= curdiff;
+ GST_DEBUG_OBJECT (comp,
+ "Creating new QoS event with timestamp %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp));
+ event = gst_event_new_qos (qostype, prop, diff, timestamp);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (res) {
+ GST_DEBUG_OBJECT (comp, "About to call nle_event_pad_func: %p",
+ priv->nle_event_pad_func);
+ res = priv->nle_event_pad_func (NLE_OBJECT (comp)->srcpad, parent, event);
+ GST_DEBUG_OBJECT (comp, "Done calling nle_event_pad_func() %d", res);
+ }
+
+beach:
+ return res;
+}
+
+static inline void
+nle_composition_reset_target_pad (NleComposition * comp)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+ GST_DEBUG_OBJECT (comp, "Removing ghostpad");
+
+ if (priv->ghosteventprobe) {
+ GstPad *target;
+
+ target = gst_ghost_pad_get_target ((GstGhostPad *) NLE_OBJECT_SRC (comp));
+ if (target)
+ gst_pad_remove_probe (target, priv->ghosteventprobe);
+ priv->ghosteventprobe = 0;
+ }
+
+ nle_object_ghost_pad_set_target (NLE_OBJECT (comp),
+ NLE_OBJECT_SRC (comp), NULL);
+ priv->send_stream_start = TRUE;
+}
+
+/* nle_composition_ghost_pad_set_target:
+ * target: The target #GstPad. The refcount will be decremented (given to the ghostpad).
+ */
+static void
+nle_composition_ghost_pad_set_target (NleComposition * comp, GstPad * target)
+{
+ GstPad *ptarget;
+ NleCompositionPrivate *priv = comp->priv;
+
+ if (target)
+ GST_DEBUG_OBJECT (comp, "target:%s:%s", GST_DEBUG_PAD_NAME (target));
+ else
+ GST_DEBUG_OBJECT (comp, "Removing target");
+
+
+ ptarget =
+ gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT (comp)->srcpad));
+ if (ptarget) {
+ gst_object_unref (ptarget);
+
+ if (ptarget == target) {
+ GST_DEBUG_OBJECT (comp,
+ "Target of srcpad is the same as existing one, not changing");
+ return;
+ }
+ }
+
+ /* Actually set the target */
+ nle_object_ghost_pad_set_target ((NleObject *) comp,
+ NLE_OBJECT (comp)->srcpad, target);
+
+ if (target && (priv->ghosteventprobe == 0)) {
+ priv->ghosteventprobe =
+ gst_pad_add_probe (target,
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH |
+ GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM |
+ GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM,
+ (GstPadProbeCallback) ghost_event_probe_handler, comp, NULL);
+ GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe);
+ }
+}
+
+static void
+refine_start_stop_in_region_above_priority (NleComposition * composition,
+ GstClockTime timestamp, GstClockTime start,
+ GstClockTime stop,
+ GstClockTime * rstart, GstClockTime * rstop, guint32 priority)
+{
+ GList *tmp;
+ NleObject *object;
+ GstClockTime nstart = start, nstop = stop;
+
+ GST_DEBUG_OBJECT (composition,
+ "timestamp:%" GST_TIME_FORMAT " start: %" GST_TIME_FORMAT " stop: %"
+ GST_TIME_FORMAT " priority:%u", GST_TIME_ARGS (timestamp),
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop), priority);
+
+ for (tmp = composition->priv->objects_start; tmp; tmp = tmp->next) {
+ object = (NleObject *) tmp->data;
+
+ GST_LOG_OBJECT (object, "START %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop));
+
+ if ((object->priority >= priority) || (!NLE_OBJECT_ACTIVE (object)))
+ continue;
+
+ if (object->start <= timestamp)
+ continue;
+
+ if (object->start >= nstop)
+ continue;
+
+ nstop = object->start;
+
+ GST_DEBUG_OBJECT (composition,
+ "START Found %s [prio:%u] at %" GST_TIME_FORMAT,
+ GST_OBJECT_NAME (object), object->priority,
+ GST_TIME_ARGS (object->start));
+
+ break;
+ }
+
+ for (tmp = composition->priv->objects_stop; tmp; tmp = tmp->next) {
+ object = (NleObject *) tmp->data;
+
+ GST_LOG_OBJECT (object, "STOP %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop));
+
+ if ((object->priority >= priority) || (!NLE_OBJECT_ACTIVE (object)))
+ continue;
+
+ if (object->stop >= timestamp)
+ continue;
+
+ if (object->stop <= nstart)
+ continue;
+
+ nstart = object->stop;
+
+ GST_DEBUG_OBJECT (composition,
+ "STOP Found %s [prio:%u] at %" GST_TIME_FORMAT,
+ GST_OBJECT_NAME (object), object->priority,
+ GST_TIME_ARGS (object->start));
+
+ break;
+ }
+
+ if (*rstart)
+ *rstart = nstart;
+
+ if (*rstop)
+ *rstop = nstop;
+}
+
+
+/*
+ * Converts a sorted list to a tree
+ * Recursive
+ *
+ * stack will be set to the next item to use in the parent.
+ * If operations number of sinks is limited, it will only use that number.
+ */
+
+static GNode *
+convert_list_to_tree (GList ** stack, GstClockTime * start,
+ GstClockTime * stop, guint32 * highprio)
+{
+ GNode *ret;
+ guint nbsinks;
+ gboolean limit;
+ GList *tmp;
+ NleObject *object;
+
+ if (!stack || !*stack)
+ return NULL;
+
+ object = (NleObject *) (*stack)->data;
+
+ GST_DEBUG ("object:%s , *start:%" GST_TIME_FORMAT ", *stop:%"
+ GST_TIME_FORMAT " highprio:%d",
+ GST_ELEMENT_NAME (object), GST_TIME_ARGS (*start),
+ GST_TIME_ARGS (*stop), *highprio);
+
+ /* update earliest stop */
+ if (GST_CLOCK_TIME_IS_VALID (*stop)) {
+ if (GST_CLOCK_TIME_IS_VALID (object->stop) && (*stop > object->stop))
+ *stop = object->stop;
+ } else {
+ *stop = object->stop;
+ }
+
+ if (GST_CLOCK_TIME_IS_VALID (*start)) {
+ if (GST_CLOCK_TIME_IS_VALID (object->start) && (*start < object->start))
+ *start = object->start;
+ } else {
+ *start = object->start;
+ }
+
+ if (NLE_OBJECT_IS_SOURCE (object)) {
+ *stack = g_list_next (*stack);
+
+ /* update highest priority.
+ * We do this here, since it's only used with sources (leafs of the tree) */
+ if (object->priority > *highprio)
+ *highprio = object->priority;
+
+ ret = g_node_new (object);
+
+ goto beach;
+ } else {
+ /* NleOperation */
+ NleOperation *oper = (NleOperation *) object;
+
+ GST_LOG_OBJECT (oper, "operation, num_sinks:%d", oper->num_sinks);
+
+ ret = g_node_new (object);
+ limit = (oper->dynamicsinks == FALSE);
+ nbsinks = oper->num_sinks;
+
+ /* FIXME : if num_sinks == -1 : request the proper number of pads */
+ for (tmp = g_list_next (*stack); tmp && (!limit || nbsinks);) {
+ g_node_append (ret, convert_list_to_tree (&tmp, start, stop, highprio));
+ if (limit)
+ nbsinks--;
+ }
+
+ *stack = tmp;
+ }
+
+beach:
+ GST_DEBUG_OBJECT (object,
+ "*start:%" GST_TIME_FORMAT " *stop:%" GST_TIME_FORMAT
+ " priority:%u", GST_TIME_ARGS (*start), GST_TIME_ARGS (*stop), *highprio);
+
+ return ret;
+}
+
+/*
+ * get_stack_list:
+ * @comp: The #NleComposition
+ * @timestamp: The #GstClockTime to look at
+ * @priority: The priority level to start looking from
+ * @activeonly: Only look for active elements if TRUE
+ * @start: The biggest start time of the objects in the stack
+ * @stop: The smallest stop time of the objects in the stack
+ * @highprio: The highest priority in the stack
+ *
+ * Not MT-safe, you should take the objects lock before calling it.
+ * Returns: A tree of #GNode sorted in priority order, corresponding
+ * to the given search arguments. The returned value can be #NULL.
+ *
+ * WITH OBJECTS LOCK TAKEN
+ */
+static GNode *
+get_stack_list (NleComposition * comp, GstClockTime timestamp,
+ guint32 priority, gboolean activeonly, GstClockTime * start,
+ GstClockTime * stop, guint * highprio)
+{
+ GList *tmp;
+ GList *stack = NULL;
+ GNode *ret = NULL;
+ GstClockTime nstart = GST_CLOCK_TIME_NONE;
+ GstClockTime nstop = GST_CLOCK_TIME_NONE;
+ GstClockTime first_out_of_stack = GST_CLOCK_TIME_NONE;
+ guint32 highest = 0;
+ gboolean reverse = (comp->priv->segment->rate < 0.0);
+
+ GST_DEBUG_OBJECT (comp,
+ "timestamp:%" GST_TIME_FORMAT ", priority:%u, activeonly:%d",
+ GST_TIME_ARGS (timestamp), priority, activeonly);
+
+ GST_LOG ("objects_start:%p objects_stop:%p", comp->priv->objects_start,
+ comp->priv->objects_stop);
+
+ if (reverse) {
+ for (tmp = comp->priv->objects_stop; tmp; tmp = g_list_next (tmp)) {
+ NleObject *object = (NleObject *) tmp->data;
+
+ GST_LOG_OBJECT (object,
+ "start: %" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT " , duration:%"
+ GST_TIME_FORMAT ", priority:%u, active:%d",
+ GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop),
+ GST_TIME_ARGS (object->duration), object->priority, object->active);
+
+ if (object->stop >= timestamp) {
+ if ((object->start < timestamp) &&
+ (object->priority >= priority) &&
+ ((!activeonly) || (NLE_OBJECT_ACTIVE (object)))) {
+ GST_LOG_OBJECT (comp, "adding %s: sorted to the stack",
+ GST_OBJECT_NAME (object));
+ stack = g_list_insert_sorted (stack, object,
+ (GCompareFunc) priority_comp);
+ if (NLE_IS_OPERATION (object))
+ nle_operation_update_base_time (NLE_OPERATION (object), timestamp);
+ }
+ } else {
+ GST_LOG_OBJECT (comp, "too far, stopping iteration");
+ first_out_of_stack = object->stop;
+ break;
+ }
+ }
+ } else {
+ for (tmp = comp->priv->objects_start; tmp; tmp = g_list_next (tmp)) {
+ NleObject *object = (NleObject *) tmp->data;
+
+ GST_LOG_OBJECT (object,
+ "start: %" GST_TIME_FORMAT " , stop:%" GST_TIME_FORMAT " , duration:%"
+ GST_TIME_FORMAT ", priority:%u", GST_TIME_ARGS (object->start),
+ GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->duration),
+ object->priority);
+
+ if (object->start <= timestamp) {
+ if ((object->stop > timestamp) &&
+ (object->priority >= priority) &&
+ ((!activeonly) || (NLE_OBJECT_ACTIVE (object)))) {
+ GST_LOG_OBJECT (comp, "adding %s: sorted to the stack",
+ GST_OBJECT_NAME (object));
+ stack = g_list_insert_sorted (stack, object,
+ (GCompareFunc) priority_comp);
+ if (NLE_IS_OPERATION (object))
+ nle_operation_update_base_time (NLE_OPERATION (object), timestamp);
+ }
+ } else {
+ GST_LOG_OBJECT (comp, "too far, stopping iteration");
+ first_out_of_stack = object->start;
+ break;
+ }
+ }
+ }
+
+ /* Insert the expandables */
+ if (G_LIKELY (timestamp < NLE_OBJECT_STOP (comp)))
+ for (tmp = comp->priv->expandables; tmp; tmp = tmp->next) {
+ GST_DEBUG_OBJECT (comp, "Adding expandable %s sorted to the list",
+ GST_OBJECT_NAME (tmp->data));
+ stack = g_list_insert_sorted (stack, tmp->data,
+ (GCompareFunc) priority_comp);
+ if (NLE_IS_OPERATION (tmp->data))
+ nle_operation_update_base_time (NLE_OPERATION (tmp->data), timestamp);
+ }
+
+ /* convert that list to a stack */
+ tmp = stack;
+ ret = convert_list_to_tree (&tmp, &nstart, &nstop, &highest);
+ if (GST_CLOCK_TIME_IS_VALID (first_out_of_stack)) {
+ if (reverse && nstart < first_out_of_stack)
+ nstart = first_out_of_stack;
+ else if (!reverse && nstop > first_out_of_stack)
+ nstop = first_out_of_stack;
+ }
+
+ GST_DEBUG ("nstart:%" GST_TIME_FORMAT ", nstop:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (nstart), GST_TIME_ARGS (nstop));
+
+ if (*stop)
+ *stop = nstop;
+ if (*start)
+ *start = nstart;
+ if (highprio)
+ *highprio = highest;
+
+ g_list_free (stack);
+
+ return ret;
+}
+
+/*
+ * get_clean_toplevel_stack:
+ * @comp: The #NleComposition
+ * @timestamp: The #GstClockTime to look at
+ * @stop_time: Pointer to a #GstClockTime for min stop time of returned stack
+ * @start_time: Pointer to a #GstClockTime for greatest start time of returned stack
+ *
+ * Returns: The new current stack for the given #NleComposition and @timestamp.
+ *
+ * WITH OBJECTS LOCK TAKEN
+ */
+static GNode *
+get_clean_toplevel_stack (NleComposition * comp, GstClockTime * timestamp,
+ GstClockTime * start_time, GstClockTime * stop_time)
+{
+ GNode *stack = NULL;
+ GstClockTime start = G_MAXUINT64;
+ GstClockTime stop = G_MAXUINT64;
+ guint highprio;
+ gboolean reverse = (comp->priv->segment->rate < 0.0);
+
+ GST_DEBUG_OBJECT (comp, "timestamp:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (*timestamp));
+ GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+
+ stack = get_stack_list (comp, *timestamp, 0, TRUE, &start, &stop, &highprio);
+
+ if (!stack &&
+ ((reverse && (*timestamp > COMP_REAL_START (comp))) ||
+ (!reverse && (*timestamp < COMP_REAL_STOP (comp))))) {
+ GST_ELEMENT_ERROR (comp, STREAM, WRONG_TYPE,
+ ("Gaps ( at %" GST_TIME_FORMAT
+ ") in the stream is not supported, the application is responsible"
+ " for filling them", GST_TIME_ARGS (*timestamp)),
+ ("Gap in the composition this should never"
+ "append, make sure to fill them"));
+
+ return NULL;
+ }
+
+ GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+
+ if (stack) {
+ guint32 top_priority = NLE_OBJECT_PRIORITY (stack->data);
+
+ /* Figure out if there's anything blocking us with smaller priority */
+ refine_start_stop_in_region_above_priority (comp, *timestamp, start,
+ stop, &start, &stop, (highprio == 0) ? top_priority : highprio);
+ }
+
+ if (*stop_time) {
+ if (stack)
+ *stop_time = stop;
+ else
+ *stop_time = 0;
+ }
+
+ if (*start_time) {
+ if (stack)
+ *start_time = start;
+ else
+ *start_time = 0;
+ }
+
+ GST_DEBUG_OBJECT (comp,
+ "Returning timestamp:%" GST_TIME_FORMAT " , start_time:%"
+ GST_TIME_FORMAT " , stop_time:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (*start_time),
+ GST_TIME_ARGS (*stop_time));
+
+ return stack;
+}
+
+static GstPadProbeReturn
+_drop_all_cb (GstPad * pad G_GNUC_UNUSED,
+ GstPadProbeInfo * info, NleComposition * comp)
+{
+ return GST_PAD_PROBE_DROP;
+}
+
+/* Must be called with OBJECTS_LOCK taken */
+static void
+_set_current_bin_to_ready (NleComposition * comp, gboolean flush_downstream)
+{
+ gint probe_id = -1;
+ GstPad *ptarget = NULL;
+ NleCompositionPrivate *priv = comp->priv;
+ GstEvent *flush_event;
+
+ comp->priv->tearing_down_stack = TRUE;
+ if (flush_downstream) {
+ ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT_SRC (comp)));
+ if (ptarget) {
+
+ /* Make sure that between the flush_start/flush_stop
+ * and the time we set the current_bin to READY, no
+ * buffer can ever get prerolled which would lead to
+ * a deadlock */
+ probe_id = gst_pad_add_probe (ptarget,
+ GST_PAD_PROBE_TYPE_DATA_BOTH | GST_PAD_PROBE_TYPE_EVENT_BOTH,
+ (GstPadProbeCallback) _drop_all_cb, comp, NULL);
+
+ GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe);
+
+ flush_event = gst_event_new_flush_start ();
+ priv->flush_seqnum = gst_event_get_seqnum (flush_event);
+ GST_INFO_OBJECT (comp, "sending flushes downstream with seqnum %d",
+ priv->flush_seqnum);
+ gst_pad_push_event (ptarget, flush_event);
+
+ }
+
+ }
+
+ gst_element_set_locked_state (priv->current_bin, TRUE);
+ gst_element_set_state (priv->current_bin, GST_STATE_READY);
+
+ if (ptarget) {
+ if (flush_downstream) {
+ flush_event = gst_event_new_flush_stop (TRUE);
+ gst_event_set_seqnum (flush_event, priv->flush_seqnum);
+
+ /* Force ad activation so that the event can actually travel.
+ * Not doing that would lead to the event being discarded.
+ */
+ gst_pad_set_active (ptarget, TRUE);
+ gst_pad_push_event (ptarget, flush_event);
+ gst_pad_set_active (ptarget, FALSE);
+ }
+
+ gst_pad_remove_probe (ptarget, probe_id);
+ gst_object_unref (ptarget);
+ }
+
+ comp->priv->tearing_down_stack = FALSE;
+}
+
+static void
+_emit_commited_signal_func (NleComposition * comp, gpointer udata)
+{
+ GST_INFO_OBJECT (comp, "Emiting COMMITED now that the stack " "is ready");
+
+ g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
+}
+
+static void
+_restart_task (NleComposition * comp)
+{
+ GST_INFO_OBJECT (comp, "Restarting task! after %s DONE",
+ UPDATE_PIPELINE_REASONS[comp->priv->updating_reason]);
+
+ if (comp->priv->updating_reason == COMP_UPDATE_STACK_ON_COMMIT)
+ _add_action (comp, G_CALLBACK (_emit_commited_signal_func), comp,
+ G_PRIORITY_HIGH);
+
+ comp->priv->seqnum_to_restart_task = 0;
+ comp->priv->waiting_serialized_query_or_buffer = FALSE;
+
+ comp->priv->updating_reason = COMP_UPDATE_STACK_NONE;
+ GST_OBJECT_LOCK (comp);
+ if (comp->task)
+ gst_task_start (comp->task);
+ GST_OBJECT_UNLOCK (comp);
+}
+
+static gboolean
+_is_ready_to_restart_task (NleComposition * comp, GstEvent * event)
+{
+ NleCompositionPrivate *priv = comp->priv;
+ gint seqnum = gst_event_get_seqnum (event);
+
+
+ if (comp->priv->seqnum_to_restart_task == seqnum) {
+ gchar *name = g_strdup_printf ("%s-new-stack__%" GST_TIME_FORMAT "--%"
+ GST_TIME_FORMAT "", GST_OBJECT_NAME (comp),
+ GST_TIME_ARGS (comp->priv->current_stack_start),
+ GST_TIME_ARGS (comp->priv->current_stack_stop));
+
+ GST_INFO_OBJECT (comp, "Got %s with proper seqnum"
+ " done with stack reconfiguration %" GST_PTR_FORMAT,
+ GST_EVENT_TYPE_NAME (event), event);
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (comp),
+ GST_DEBUG_GRAPH_SHOW_ALL, name);
+ g_free (name);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ GST_INFO_OBJECT (comp, "update_pipeline DONE");
+ return TRUE;
+ }
+
+ priv->waiting_serialized_query_or_buffer = TRUE;
+ return FALSE;
+
+ } else if (comp->priv->seqnum_to_restart_task) {
+ GST_INFO_OBJECT (comp, "WARNING: %s seqnum %i != wanted %i",
+ GST_EVENT_TYPE_NAME (event), seqnum,
+ comp->priv->seqnum_to_restart_task);
+ }
+
+ return FALSE;
+}
+
+static void
+_commit_func (NleComposition * comp, UpdateCompositionData * ucompo)
+{
+ GstClockTime curpos;
+ NleCompositionPrivate *priv = comp->priv;
+
+ _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason);
+
+ /* Get current so that it represent the duration it was
+ * before commiting children */
+ curpos = get_current_position (comp);
+
+ if (!_commit_all_values (comp)) {
+ GST_DEBUG_OBJECT (comp, "Nothing to commit, leaving");
+
+ g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, FALSE);
+ _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason);
+
+ return;
+ }
+
+ if (priv->initialized == FALSE) {
+ GST_DEBUG_OBJECT (comp, "Not initialized yet, just updating values");
+
+ update_start_stop_duration (comp);
+
+ g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
+
+ } else {
+ gboolean reverse;
+
+ /* And update the pipeline at current position if needed */
+ update_start_stop_duration (comp);
+
+ reverse = (priv->segment->rate < 0.0);
+ if (!reverse) {
+ GST_DEBUG_OBJECT (comp,
+ "Setting segment->start to curpos:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (curpos));
+ priv->segment->start = curpos;
+ } else {
+ GST_DEBUG_OBJECT (comp,
+ "Setting segment->stop to curpos:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (curpos));
+ priv->segment->stop = curpos;
+ }
+ update_pipeline (comp, curpos, ucompo->seqnum, COMP_UPDATE_STACK_ON_COMMIT);
+
+ if (!priv->current) {
+ GST_INFO_OBJECT (comp, "No new stack set, we can go and keep acting on"
+ " our children");
+
+ g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
+ }
+ }
+
+ _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason);
+}
+
+static void
+_update_pipeline_func (NleComposition * comp, UpdateCompositionData * ucompo)
+{
+ gboolean reverse;
+ NleCompositionPrivate *priv = comp->priv;
+
+ _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason);
+
+ /* Set up a non-initial seek on current_stack_stop */
+ reverse = (priv->segment->rate < 0.0);
+ if (!reverse) {
+ GST_DEBUG_OBJECT (comp,
+ "Setting segment->start to current_stack_stop:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (priv->current_stack_stop));
+ priv->segment->start = priv->current_stack_stop;
+ } else {
+ GST_DEBUG_OBJECT (comp,
+ "Setting segment->stop to current_stack_start:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (priv->current_stack_start));
+ priv->segment->stop = priv->current_stack_start;
+ }
+
+ seek_handling (comp, ucompo->seqnum, COMP_UPDATE_STACK_ON_EOS);
+
+ /* Post segment done if last seek was a segment seek */
+ if (!priv->current && (priv->segment->flags & GST_SEEK_FLAG_SEGMENT)) {
+ gint64 epos;
+
+ if (GST_CLOCK_TIME_IS_VALID (priv->segment->stop))
+ epos = (MIN (priv->segment->stop, NLE_OBJECT_STOP (comp)));
+ else
+ epos = NLE_OBJECT_STOP (comp);
+
+ GST_LOG_OBJECT (comp, "Emitting segment done pos %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (epos));
+ gst_element_post_message (GST_ELEMENT_CAST (comp),
+ gst_message_new_segment_done (GST_OBJECT (comp),
+ priv->segment->format, epos));
+ gst_pad_push_event (NLE_OBJECT (comp)->srcpad,
+ gst_event_new_segment_done (priv->segment->format, epos));
+ }
+
+ _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason);
+}
+
+static GstStateChangeReturn
+nle_composition_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn res;
+ NleComposition *comp = (NleComposition *) element;
+
+ GST_DEBUG_OBJECT (comp, "%s => %s",
+ gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+ gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ _start_task (comp);
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ /* state-lock all elements */
+ GST_DEBUG_OBJECT (comp,
+ "Setting all children to READY and locking their state");
+
+ _add_update_compo_action (comp, G_CALLBACK (_initialize_stack_func),
+ COMP_UPDATE_STACK_INITIALIZE);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ _stop_task (comp);
+
+ _remove_update_actions (comp);
+ _remove_seek_actions (comp);
+ _deactivate_stack (comp, TRUE);
+ comp->priv->tearing_down_stack = TRUE;
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ _stop_task (comp);
+
+ _remove_update_actions (comp);
+ _remove_seek_actions (comp);
+ comp->priv->tearing_down_stack = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ if (res == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (comp, "state change failure %s => %s",
+ gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+ gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+ comp->priv->tearing_down_stack = TRUE;
+ _stop_task (comp);
+ nle_composition_reset (comp);
+ gst_element_set_state (comp->priv->current_bin, GST_STATE_NULL);
+ comp->priv->tearing_down_stack = FALSE;
+
+ return res;
+ }
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ comp->priv->tearing_down_stack = FALSE;
+ nle_composition_reset (comp);
+
+ /* In READY we are still able to process actions. */
+ _start_task (comp);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ gst_element_set_state (comp->priv->current_bin, GST_STATE_NULL);
+ comp->priv->tearing_down_stack = FALSE;
+ break;
+ default:
+ break;
+ }
+
+ return res;
+}
+
+static gint
+objects_start_compare (NleObject * a, NleObject * b)
+{
+ if (a->start == b->start) {
+ if (a->priority < b->priority)
+ return -1;
+ if (a->priority > b->priority)
+ return 1;
+ return 0;
+ }
+ if (a->start < b->start)
+ return -1;
+ if (a->start > b->start)
+ return 1;
+ return 0;
+}
+
+static gint
+objects_stop_compare (NleObject * a, NleObject * b)
+{
+ if (a->stop == b->stop) {
+ if (a->priority < b->priority)
+ return -1;
+ if (a->priority > b->priority)
+ return 1;
+ return 0;
+ }
+ if (b->stop < a->stop)
+ return -1;
+ if (b->stop > a->stop)
+ return 1;
+ return 0;
+}
+
+/* WITH OBJECTS LOCK TAKEN */
+static void
+update_start_stop_duration (NleComposition * comp)
+{
+ NleObject *obj;
+ NleObject *cobj = (NleObject *) comp;
+ NleCompositionPrivate *priv = comp->priv;
+
+ _assert_proper_thread (comp);
+
+ if (!priv->objects_start) {
+ GST_INFO_OBJECT (comp, "no objects, resetting everything to 0");
+
+ if (cobj->start) {
+ cobj->start = cobj->pending_start = 0;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_START]);
+ }
+
+ if (cobj->duration) {
+ cobj->pending_duration = cobj->duration = 0;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_DURATION]);
+ signal_duration_change (comp);
+ }
+
+ if (cobj->stop) {
+ cobj->stop = 0;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_STOP]);
+ }
+
+ return;
+ }
+
+ /* If we have a default object, the start position is 0 */
+ if (priv->expandables) {
+ GST_INFO_OBJECT (cobj,
+ "Setting start to 0 because we have a default object");
+
+ if (cobj->start != 0) {
+ cobj->pending_start = cobj->start = 0;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_START]);
+ }
+
+ } else {
+
+ /* Else it's the first object's start value */
+ obj = (NleObject *) priv->objects_start->data;
+
+ if (obj->start != cobj->start) {
+ GST_INFO_OBJECT (obj, "setting start from %s to %" GST_TIME_FORMAT,
+ GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->start));
+ cobj->pending_start = cobj->start = obj->start;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_START]);
+ }
+
+ }
+
+ obj = (NleObject *) priv->objects_stop->data;
+
+ if (obj->stop != cobj->stop) {
+ GST_INFO_OBJECT (obj, "setting stop from %s to %" GST_TIME_FORMAT,
+ GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->stop));
+
+ if (priv->expandables) {
+ GList *tmp;
+
+ GST_INFO_OBJECT (comp, "RE-setting all expandables duration and commit");
+ for (tmp = priv->expandables; tmp; tmp = tmp->next) {
+ g_object_set (tmp->data, "duration", obj->stop, NULL);
+ nle_object_commit (NLE_OBJECT (tmp->data), FALSE);
+ }
+ }
+
+ priv->segment->stop = obj->stop;
+ cobj->stop = obj->stop;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_STOP]);
+ }
+
+ if ((cobj->stop - cobj->start) != cobj->duration) {
+ cobj->pending_duration = cobj->duration = cobj->stop - cobj->start;
+ g_object_notify_by_pspec (G_OBJECT (cobj),
+ nleobject_properties[NLEOBJECT_PROP_DURATION]);
+ signal_duration_change (comp);
+ }
+
+ GST_INFO_OBJECT (comp,
+ "start:%" GST_TIME_FORMAT
+ " stop:%" GST_TIME_FORMAT
+ " duration:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (cobj->start),
+ GST_TIME_ARGS (cobj->stop), GST_TIME_ARGS (cobj->duration));
+}
+
+static void
+_link_to_parent (NleComposition * comp, NleObject * newobj,
+ NleObject * newparent)
+{
+ GstPad *sinkpad;
+
+ /* relink to new parent in required order */
+ GST_LOG_OBJECT (comp, "Linking %s and %s",
+ GST_ELEMENT_NAME (GST_ELEMENT (newobj)),
+ GST_ELEMENT_NAME (GST_ELEMENT (newparent)));
+
+ sinkpad = get_unlinked_sink_ghost_pad ((NleOperation *) newparent);
+
+ if (G_UNLIKELY (sinkpad == NULL)) {
+ GST_WARNING_OBJECT (comp,
+ "Couldn't find an unlinked sinkpad from %s",
+ GST_ELEMENT_NAME (newparent));
+ } else {
+ if (G_UNLIKELY (gst_pad_link_full (NLE_OBJECT_SRC (newobj), sinkpad,
+ GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) {
+ GST_WARNING_OBJECT (comp, "Failed to link pads %s:%s - %s:%s",
+ GST_DEBUG_PAD_NAME (NLE_OBJECT_SRC (newobj)),
+ GST_DEBUG_PAD_NAME (sinkpad));
+ }
+ gst_object_unref (sinkpad);
+ }
+}
+
+static void
+_relink_children_recursively (NleComposition * comp,
+ NleObject * newobj, GNode * node, GstEvent * toplevel_seek)
+{
+ GNode *child;
+ guint nbchildren = g_node_n_children (node);
+ NleOperation *oper = (NleOperation *) newobj;
+
+ GST_INFO_OBJECT (newobj, "is a %s operation, analyzing the %d children",
+ oper->dynamicsinks ? "dynamic" : "regular", nbchildren);
+ /* Update the operation's number of sinks, that will make it have the proper
+ * number of sink pads to connect the children to. */
+ if (oper->dynamicsinks)
+ g_object_set (G_OBJECT (newobj), "sinks", nbchildren, NULL);
+
+ for (child = node->children; child; child = child->next)
+ _relink_single_node (comp, child, toplevel_seek);
+
+ if (G_UNLIKELY (nbchildren < oper->num_sinks))
+ GST_ELEMENT_ERROR (comp, STREAM, FAILED,
+ ("The NleComposition structure is not valid"),
+ ("%" GST_PTR_FORMAT
+ " Not enough sinkpads to link all objects to the operation ! "
+ "%d / %d, current toplevel seek %" GST_PTR_FORMAT,
+ oper, oper->num_sinks, nbchildren, toplevel_seek));
+
+ if (G_UNLIKELY (nbchildren == 0)) {
+ GST_ELEMENT_ERROR (comp, STREAM, FAILED,
+ ("The NleComposition structure is not valid"),
+ ("Operation %" GST_PTR_FORMAT
+ " has no child objects to be connected to "
+ "current toplevel seek: %" GST_PTR_FORMAT, oper, toplevel_seek));
+ }
+ /* Make sure we have enough sinkpads */
+}
+
+/*
+ * recursive depth-first relink stack function on new stack
+ *
+ * _ relink nodes with changed parent/order
+ * _ links new nodes with parents
+ * _ unblocks available source pads (except for toplevel)
+ *
+ * WITH OBJECTS LOCK TAKEN
+ */
+static void
+_relink_single_node (NleComposition * comp, GNode * node,
+ GstEvent * toplevel_seek)
+{
+ NleObject *newobj;
+ NleObject *newparent;
+ GNode *node_it;
+ GstPad *srcpad = NULL, *sinkpad = NULL;
+ GstEvent *translated_seek;
+
+ if (G_UNLIKELY (!node))
+ return;
+
+ newparent = G_NODE_IS_ROOT (node) ? NULL : (NleObject *) node->parent->data;
+ newobj = (NleObject *) node->data;
+
+ GST_DEBUG_OBJECT (comp, "newobj:%s",
+ GST_ELEMENT_NAME ((GstElement *) newobj));
+
+ newobj->recursive_media_duration_factor = 1.0f;
+ for (node_it = node; node_it != NULL; node_it = node_it->parent) {
+ NleObject *object = (NleObject *) node_it->data;
+ newobj->recursive_media_duration_factor *= object->media_duration_factor;
+ }
+
+ srcpad = NLE_OBJECT_SRC (newobj);
+
+ gst_bin_add (GST_BIN (comp->priv->current_bin), GST_ELEMENT (newobj));
+ gst_element_sync_state_with_parent (GST_ELEMENT_CAST (newobj));
+
+ translated_seek = nle_object_translate_incoming_seek (newobj,
+ gst_event_ref (toplevel_seek));
+
+ gst_element_send_event (GST_ELEMENT (newobj), translated_seek);
+
+ /* link to parent if needed. */
+ if (newparent) {
+ _link_to_parent (comp, newobj, newparent);
+
+ /* If there's an operation, inform it about priority changes */
+ sinkpad = gst_pad_get_peer (srcpad);
+ nle_operation_signal_input_priority_changed ((NleOperation *)
+ newparent, sinkpad, newobj->priority);
+ gst_object_unref (sinkpad);
+ }
+
+ /* Handle children */
+ if (NLE_IS_OPERATION (newobj))
+ _relink_children_recursively (comp, newobj, node, toplevel_seek);
+
+ GST_LOG_OBJECT (comp, "done with object %s",
+ GST_ELEMENT_NAME (GST_ELEMENT (newobj)));
+}
+
+
+
+/*
+ * compare_relink_stack:
+ * @comp: The #NleComposition
+ * @stack: The new stack
+ * @modify: TRUE if the timeline has changed and needs downstream flushes.
+ *
+ * Compares the given stack to the current one and relinks it if needed.
+ *
+ * WITH OBJECTS LOCK TAKEN
+ *
+ * Returns: The #GList of #NleObject no longer used
+ */
+
+static void
+_deactivate_stack (NleComposition * comp, gboolean flush_downstream)
+{
+ GstPad *ptarget;
+
+ GST_INFO_OBJECT (comp, "Deactivating current stack (flushing downstream: %d)",
+ flush_downstream);
+ _set_current_bin_to_ready (comp, flush_downstream);
+
+ ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT_SRC (comp)));
+ _empty_bin (GST_BIN_CAST (comp->priv->current_bin));
+
+ if (comp->priv->ghosteventprobe) {
+ GST_INFO_OBJECT (comp, "Removing old ghost pad probe");
+
+ gst_pad_remove_probe (ptarget, comp->priv->ghosteventprobe);
+ comp->priv->ghosteventprobe = 0;
+ }
+
+ if (ptarget)
+ gst_object_unref (ptarget);
+
+ GST_INFO_OBJECT (comp, "Stack desctivated");
+
+/* priv->current = NULL;
+ */
+}
+
+static void
+_relink_new_stack (NleComposition * comp, GNode * stack,
+ GstEvent * toplevel_seek)
+{
+ _relink_single_node (comp, stack, toplevel_seek);
+
+ gst_event_unref (toplevel_seek);
+}
+
+/* static void
+ * unlock_activate_stack (NleComposition * comp, GNode * node, GstState state)
+ * {
+ * GNode *child;
+ *
+ * GST_LOG_OBJECT (comp, "object:%s",
+ * GST_ELEMENT_NAME ((GstElement *) (node->data)));
+ *
+ * gst_element_set_locked_state ((GstElement *) (node->data), FALSE);
+ * gst_element_set_state (GST_ELEMENT (node->data), state);
+ *
+ * for (child = node->children; child; child = child->next)
+ * unlock_activate_stack (comp, child, state);
+ * }
+ */
+
+static gboolean
+are_same_stacks (GNode * stack1, GNode * stack2)
+{
+ gboolean res = FALSE;
+
+ /* TODO : FIXME : we should also compare start/inpoint */
+ /* stacks are not equal if one of them is NULL but not the other */
+ if ((!stack1 && stack2) || (stack1 && !stack2))
+ goto beach;
+
+ if (stack1 && stack2) {
+ GNode *child1, *child2;
+
+ /* if they don't contain the same source, not equal */
+ if (!(stack1->data == stack2->data))
+ goto beach;
+
+ /* if they don't have the same number of children, not equal */
+ if (!(g_node_n_children (stack1) == g_node_n_children (stack2)))
+ goto beach;
+
+ child1 = stack1->children;
+ child2 = stack2->children;
+ while (child1 && child2) {
+ if (!(are_same_stacks (child1, child2)))
+ goto beach;
+ child1 = g_node_next_sibling (child1);
+ child2 = g_node_next_sibling (child2);
+ }
+
+ /* if there's a difference in child number, stacks are not equal */
+ if (child1 || child2)
+ goto beach;
+ }
+
+ /* if stack1 AND stack2 are NULL, then they're equal (both empty) */
+ res = TRUE;
+
+beach:
+ GST_LOG ("Stacks are equal : %d", res);
+
+ return res;
+}
+
+static inline gboolean
+_activate_new_stack (NleComposition * comp)
+{
+ GstPad *pad;
+ GstElement *topelement;
+
+ NleCompositionPrivate *priv = comp->priv;
+
+ if (!priv->current) {
+ if ((!priv->objects_start)) {
+ nle_composition_reset_target_pad (comp);
+ priv->current_stack_start = 0;
+ priv->current_stack_stop = GST_CLOCK_TIME_NONE;
+ }
+
+ GST_DEBUG_OBJECT (comp, "Nothing else in the composition"
+ ", update 'worked'");
+ goto resync_state;
+ }
+
+ /* The stack is entirely ready, send seek out synchronously */
+ topelement = GST_ELEMENT (priv->current->data);
+ /* Get toplevel object source pad */
+ pad = NLE_OBJECT_SRC (topelement);
+
+ GST_INFO_OBJECT (comp,
+ "We have a valid toplevel element pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ nle_composition_ghost_pad_set_target (comp, pad);
+
+ GST_DEBUG_OBJECT (comp, "New stack activated!");
+
+resync_state:
+ gst_element_set_locked_state (priv->current_bin, FALSE);
+
+ GST_DEBUG ("going back to parent state");
+ priv->suppress_child_error = TRUE;
+ if (!gst_element_sync_state_with_parent (priv->current_bin)) {
+ gst_element_set_locked_state (priv->current_bin, TRUE);
+ gst_element_set_state (priv->current_bin, GST_STATE_NULL);
+ priv->suppress_child_error = FALSE;
+
+ GST_ELEMENT_ERROR (comp, CORE, STATE_CHANGE, (NULL),
+ ("Could not sync %" GST_PTR_FORMAT " state with parent",
+ priv->current_bin));
+ return FALSE;
+ }
+
+ priv->suppress_child_error = FALSE;
+ GST_DEBUG ("gone back to parent state");
+
+ return TRUE;
+}
+
+/* WITH OBJECTS LOCK TAKEN */
+static gboolean
+_set_real_eos_seqnum_from_seek (NleComposition * comp, GstEvent * event)
+{
+ GList *tmp;
+
+ gboolean should_check_objects = FALSE;
+ NleCompositionPrivate *priv = comp->priv;
+ gboolean reverse = (priv->segment->rate < 0);
+ gint stack_seqnum = gst_event_get_seqnum (event);
+
+ if (reverse && GST_CLOCK_TIME_IS_VALID (priv->current_stack_start))
+ should_check_objects = TRUE;
+ else if (!reverse && GST_CLOCK_TIME_IS_VALID (priv->current_stack_stop))
+ should_check_objects = TRUE;
+
+ if (should_check_objects) {
+ for (tmp = priv->objects_stop; tmp; tmp = g_list_next (tmp)) {
+ NleObject *object = (NleObject *) tmp->data;
+
+ if (!NLE_IS_SOURCE (object))
+ continue;
+
+ if ((!reverse && priv->current_stack_stop < object->stop) ||
+ (reverse && priv->current_stack_start > object->start)) {
+ priv->next_eos_seqnum = stack_seqnum;
+ g_atomic_int_set (&priv->real_eos_seqnum, 0);
+ return FALSE;
+ }
+ }
+ }
+
+ priv->next_eos_seqnum = stack_seqnum;
+ g_atomic_int_set (&priv->real_eos_seqnum, stack_seqnum);
+
+ return TRUE;
+}
+
+#ifndef GST_DISABLE_GST_DEBUG
+static gboolean
+_print_stack (GNode * node, gpointer res)
+{
+ NleObject *obj = NLE_OBJECT (node->data);
+
+ g_string_append_printf ((GString *) res,
+ "%*s [s=%" GST_TIME_FORMAT " - d=%" GST_TIME_FORMAT "] prio=%d\n",
+ g_node_depth (node) * 4, GST_OBJECT_NAME (obj),
+ GST_TIME_ARGS (NLE_OBJECT_START (obj)),
+ GST_TIME_ARGS (NLE_OBJECT_STOP (obj)), obj->priority);
+
+ return FALSE;
+}
+#endif
+
+static void
+_dump_stack (NleComposition * comp, GNode * stack)
+{
+#ifndef GST_DISABLE_GST_DEBUG
+ GString *res;
+
+ if (!stack)
+ return;
+
+ if (gst_debug_category_get_threshold (nlecomposition_debug) < GST_LEVEL_INFO)
+ return;
+
+ res = g_string_new (NULL);
+ g_string_append_printf (res, " ====> dumping stack [%" GST_TIME_FORMAT " - %"
+ GST_TIME_FORMAT "]:\n",
+ GST_TIME_ARGS (comp->priv->current_stack_start),
+ GST_TIME_ARGS (comp->priv->current_stack_stop));
+ g_node_traverse (stack, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, _print_stack, res);
+
+ GST_INFO_OBJECT (comp, "%s", res->str);
+ g_string_free (res, TRUE);
+#endif
+}
+
+/*
+ * update_pipeline:
+ * @comp: The #NleComposition
+ * @currenttime: The #GstClockTime to update at, can be GST_CLOCK_TIME_NONE.
+ * @update_reason: Reason why we are updating the pipeline
+ *
+ * Updates the internal pipeline and properties. If @currenttime is
+ * GST_CLOCK_TIME_NONE, it will not modify the current pipeline
+ *
+ * Returns: FALSE if there was an error updating the pipeline.
+ *
+ * WITH OBJECTS LOCK TAKEN
+ */
+static gboolean
+update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
+ NleUpdateStackReason update_reason)
+{
+
+ GstEvent *toplevel_seek;
+
+ GNode *stack = NULL;
+ gboolean samestack = FALSE;
+ gboolean updatestoponly = FALSE;
+ GstState state = GST_STATE (comp);
+ NleCompositionPrivate *priv = comp->priv;
+ GstClockTime new_stop = GST_CLOCK_TIME_NONE;
+ GstClockTime new_start = GST_CLOCK_TIME_NONE;
+ GstClockTime duration = NLE_OBJECT (comp)->duration - 1;
+
+ GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ?
+ GST_STATE (comp) : GST_STATE_NEXT (comp);
+
+ _assert_proper_thread (comp);
+
+ if (currenttime >= duration) {
+ currenttime = duration;
+ priv->segment->start = GST_CLOCK_TIME_NONE;
+ priv->segment->stop = GST_CLOCK_TIME_NONE;
+ }
+
+ GST_INFO_OBJECT (comp,
+ "currenttime:%" GST_TIME_FORMAT
+ " Reason: %s, Seqnum: %i", GST_TIME_ARGS (currenttime),
+ UPDATE_PIPELINE_REASONS[update_reason], seqnum);
+
+ if (!GST_CLOCK_TIME_IS_VALID (currenttime))
+ return FALSE;
+
+ if (state == GST_STATE_NULL && nextstate == GST_STATE_NULL) {
+ GST_DEBUG_OBJECT (comp, "STATE_NULL: not updating pipeline");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (comp,
+ "now really updating the pipeline, current-state:%s",
+ gst_element_state_get_name (state));
+
+ /* Get new stack and compare it to current one */
+ stack = get_clean_toplevel_stack (comp, ¤ttime, &new_start, &new_stop);
+ samestack = are_same_stacks (priv->current, stack);
+
+ /* set new current_stack_start/stop (the current zone over which the new stack
+ * is valid) */
+ if (priv->segment->rate >= 0.0) {
+ priv->current_stack_start = currenttime;
+ priv->current_stack_stop = new_stop;
+ } else {
+ priv->current_stack_start = new_start;
+ priv->current_stack_stop = currenttime;
+ }
+
+# if 0
+ /* FIXME -- We should be ablt to use updatestoponly in that case,
+ * but it simply does not work! Not using it leads to same
+ * behaviour, but less optimized */
+
+ gboolean startchanged, stopchanged;
+
+ if (priv->segment->rate >= 0.0) {
+ startchanged = priv->current_stack_start != currenttime;
+ stopchanged = priv->current_stack_stop != new_stop;
+ } else {
+ startchanged = priv->current_stack_start != new_start;
+ stopchanged = priv->current_stack_stop != currenttime;
+ }
+
+ if (samestack) {
+ if (startchanged || stopchanged) {
+ /* Update seek events need to be flushing if not in PLAYING,
+ * else we will encounter deadlocks. */
+ updatestoponly = (state == GST_STATE_PLAYING) ? FALSE : TRUE;
+ }
+ }
+#endif
+
+ toplevel_seek = get_new_seek_event (comp, TRUE, updatestoponly);
+ gst_event_set_seqnum (toplevel_seek, seqnum);
+ _set_real_eos_seqnum_from_seek (comp, toplevel_seek);
+
+ _remove_update_actions (comp);
+
+ /* If stacks are different, unlink/relink objects */
+ if (!samestack) {
+ _dump_stack (comp, stack);
+ _deactivate_stack (comp, _have_to_flush_downstream (update_reason));
+ _relink_new_stack (comp, stack, toplevel_seek);
+ }
+
+ /* Unlock all elements in new stack */
+ GST_INFO_OBJECT (comp, "Setting current stack [%" GST_TIME_FORMAT " - %"
+ GST_TIME_FORMAT "]", GST_TIME_ARGS (priv->current_stack_start),
+ GST_TIME_ARGS (priv->current_stack_stop));
+
+ if (priv->current)
+ g_node_destroy (priv->current);
+
+ priv->current = stack;
+
+ if (priv->current) {
+
+ GST_INFO_OBJECT (comp, "New stack set and ready to run, probing src pad"
+ " and stopping children thread until we are actually ready with"
+ " that new stack");
+
+ comp->priv->updating_reason = update_reason;
+ comp->priv->seqnum_to_restart_task = seqnum;
+
+ GST_OBJECT_LOCK (comp);
+ if (comp->task == NULL) {
+ GST_INFO_OBJECT (comp,
+ "No task set, it must have been stopped, returning");
+ GST_OBJECT_UNLOCK (comp);
+ return FALSE;
+ }
+
+ gst_task_pause (comp->task);
+ GST_OBJECT_UNLOCK (comp);
+ }
+
+ /* Activate stack */
+ if (!samestack)
+ return _activate_new_stack (comp);
+ else
+ return _seek_current_stack (comp, toplevel_seek,
+ _have_to_flush_downstream (update_reason));
+}
+
+static gboolean
+nle_composition_add_object (GstBin * bin, GstElement * element)
+{
+ NleObject *object;
+ NleComposition *comp = (NleComposition *) bin;
+
+ if (element == comp->priv->current_bin) {
+ GST_INFO_OBJECT (comp, "Adding internal bin");
+ return GST_BIN_CLASS (parent_class)->add_element (bin, element);
+ }
+
+ g_return_val_if_fail (NLE_IS_OBJECT (element), FALSE);
+
+ object = NLE_OBJECT (element);
+ gst_object_ref_sink (object);
+
+ object->in_composition = TRUE;
+ _add_add_object_action (comp, object);
+
+ return TRUE;
+}
+
+static gboolean
+_nle_composition_add_object (NleComposition * comp, NleObject * object)
+{
+ gboolean ret = TRUE;
+ NleCompositionPrivate *priv = comp->priv;
+
+ GST_DEBUG_OBJECT (comp, "element %s", GST_OBJECT_NAME (object));
+ GST_DEBUG_OBJECT (object, "%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (NLE_OBJECT_START (object)),
+ GST_TIME_ARGS (NLE_OBJECT_STOP (object)));
+
+ if ((NLE_OBJECT_IS_EXPANDABLE (object)) &&
+ g_list_find (priv->expandables, object)) {
+ GST_WARNING_OBJECT (comp,
+ "We already have an expandable, remove it before adding new one");
+ ret = FALSE;
+
+ goto chiringuito;
+ }
+
+ nle_object_set_caps (object, NLE_OBJECT (comp)->caps);
+ nle_object_set_commit_needed (NLE_OBJECT (comp));
+
+ if (!ret) {
+ GST_WARNING_OBJECT (comp, "couldn't add object");
+ goto chiringuito;
+ }
+
+ /* lock state of child ! */
+ GST_LOG_OBJECT (comp, "Locking state of %s", GST_ELEMENT_NAME (object));
+
+ if (NLE_OBJECT_IS_EXPANDABLE (object)) {
+ /* Only react on non-default objects properties */
+ g_object_set (object,
+ "start", (GstClockTime) 0,
+ "inpoint", (GstClockTime) 0,
+ "duration", (GstClockTimeDiff) NLE_OBJECT_STOP (comp), NULL);
+
+ GST_INFO_OBJECT (object, "Used as expandable, commiting now");
+ nle_object_commit (NLE_OBJECT (object), FALSE);
+ }
+
+ /* ...and add it to the hash table */
+ g_hash_table_add (priv->objects_hash, object);
+
+ /* Set the caps of the composition on the NleObject it handles */
+ if (G_UNLIKELY (!gst_caps_is_any (((NleObject *) comp)->caps)))
+ nle_object_set_caps ((NleObject *) object, ((NleObject *) comp)->caps);
+
+ /* Special case for default source. */
+ if (NLE_OBJECT_IS_EXPANDABLE (object)) {
+ /* It doesn't get added to objects_start and objects_stop. */
+ priv->expandables = g_list_prepend (priv->expandables, object);
+ goto beach;
+ }
+
+ /* add it sorted to the objects list */
+ priv->objects_start = g_list_insert_sorted
+ (priv->objects_start, object, (GCompareFunc) objects_start_compare);
+
+ if (priv->objects_start)
+ GST_LOG_OBJECT (comp,
+ "Head of objects_start is now %s [%" GST_TIME_FORMAT "--%"
+ GST_TIME_FORMAT "]",
+ GST_OBJECT_NAME (priv->objects_start->data),
+ GST_TIME_ARGS (NLE_OBJECT_START (priv->objects_start->data)),
+ GST_TIME_ARGS (NLE_OBJECT_STOP (priv->objects_start->data)));
+
+ priv->objects_stop = g_list_insert_sorted
+ (priv->objects_stop, object, (GCompareFunc) objects_stop_compare);
+
+ /* Now the object is ready to be commited and then used */
+
+beach:
+ return ret;
+
+chiringuito:
+ {
+ update_start_stop_duration (comp);
+ goto beach;
+ }
+}
+
+static gboolean
+nle_composition_remove_object (GstBin * bin, GstElement * element)
+{
+ NleObject *object;
+ NleComposition *comp = (NleComposition *) bin;
+
+ if (element == comp->priv->current_bin) {
+ GST_INFO_OBJECT (comp, "Removing internal bin");
+ return GST_BIN_CLASS (parent_class)->remove_element (bin, element);
+ }
+
+ g_return_val_if_fail (NLE_IS_OBJECT (element), FALSE);
+
+ object = NLE_OBJECT (element);
+
+ object->in_composition = FALSE;
+ _add_remove_object_action (comp, object);
+
+ return TRUE;
+}
+
+static gboolean
+_nle_composition_remove_object (NleComposition * comp, NleObject * object)
+{
+ NleCompositionPrivate *priv = comp->priv;
+
+ GST_DEBUG_OBJECT (comp, "removing object %s", GST_OBJECT_NAME (object));
+
+ if (!g_hash_table_contains (priv->objects_hash, object)) {
+ GST_INFO_OBJECT (comp, "object was not in composition");
+ return FALSE;
+ }
+
+ gst_element_set_locked_state (GST_ELEMENT (object), FALSE);
+ gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL);
+
+ /* handle default source */
+ if (NLE_OBJECT_IS_EXPANDABLE (object)) {
+ /* Find it in the list */
+ priv->expandables = g_list_remove (priv->expandables, object);
+ } else {
+ /* remove it from the objects list and resort the lists */
+ priv->objects_start = g_list_remove (priv->objects_start, object);
+ priv->objects_stop = g_list_remove (priv->objects_stop, object);
+ GST_LOG_OBJECT (object, "Removed from the objects start/stop list");
+ }
+
+ if (priv->current && NLE_OBJECT (priv->current->data) == NLE_OBJECT (object))
+ nle_composition_reset_target_pad (comp);
+
+ g_hash_table_remove (priv->objects_hash, object);
+
+ GST_LOG_OBJECT (object, "Done removing from the composition, now updating");
+
+ /* Make it possible to reuse the same object later */
+ nle_object_reset (NLE_OBJECT (object));
+ gst_object_unref (object);
+
+ return TRUE;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
+ * 2004-2008 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nlecomposition.h: Header for base NleComposition
+ *
+ * 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 __NLE_COMPOSITION_H__
+#define __NLE_COMPOSITION_H__
+
+#include <gst/gst.h>
+#include "nleobject.h"
+
+G_BEGIN_DECLS
+#define NLE_TYPE_COMPOSITION \
+ (nle_composition_get_type())
+#define NLE_COMPOSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_COMPOSITION,NleComposition))
+#define NLE_COMPOSITION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_COMPOSITION,NleCompositionClass))
+#define NLE_COMPOSITION_GET_CLASS(obj) \
+ (NLE_COMPOSITION_CLASS (G_OBJECT_GET_CLASS (obj)))
+#define NLE_IS_COMPOSITION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_COMPOSITION))
+#define NLE_IS_COMPOSITION_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_COMPOSITION))
+
+typedef struct _NleCompositionPrivate NleCompositionPrivate;
+
+struct _NleComposition
+{
+ NleObject parent;
+
+ GstTask * task;
+ GRecMutex task_rec_lock;
+
+ /*< private >*/
+ NleCompositionPrivate * priv;
+
+};
+
+struct _NleCompositionClass
+{
+ NleObjectClass parent_class;
+};
+
+GType nle_composition_get_type (void) G_GNUC_INTERNAL;
+
+G_END_DECLS
+#endif /* __NLE_COMPOSITION_H__ */
--- /dev/null
+/* Gnonlin
+ * Copyright (C) <2009> Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "nle.h"
+
+GST_DEBUG_CATEGORY_STATIC (nleghostpad);
+#define GST_CAT_DEFAULT nleghostpad
+
+typedef struct _NlePadPrivate NlePadPrivate;
+
+struct _NlePadPrivate
+{
+ NleObject *object;
+ NlePadPrivate *ghostpriv;
+ GstPadDirection dir;
+ GstPadEventFunction eventfunc;
+ GstPadQueryFunction queryfunc;
+
+ GstEvent *pending_seek;
+};
+
+/**
+ * nle_object_translate_incoming_seek:
+ * @object: A #NleObject.
+ * @event: (transfer full) A #GstEvent to translate
+ *
+ * Returns: (transfer full) new translated seek event
+ */
+GstEvent *
+nle_object_translate_incoming_seek (NleObject * object, GstEvent * event)
+{
+ GstEvent *event2;
+ GstFormat format;
+ gdouble rate;
+ GstSeekFlags flags;
+ GstSeekType curtype, stoptype;
+ GstSeekType ncurtype;
+ gint64 cur;
+ guint64 ncur;
+ gint64 stop;
+ guint64 nstop;
+ guint32 seqnum = GST_EVENT_SEQNUM (event);
+
+ gst_event_parse_seek (event, &rate, &format, &flags,
+ &curtype, &cur, &stoptype, &stop);
+
+ GST_DEBUG_OBJECT (object,
+ "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %"
+ GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype,
+ stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
+
+ if (G_UNLIKELY (format != GST_FORMAT_TIME))
+ goto invalid_format;
+
+ /* convert cur */
+ ncurtype = GST_SEEK_TYPE_SET;
+ if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET)
+ && (nle_object_to_media_time (object, cur, &ncur)))) {
+ /* cur is TYPE_SET and value is valid */
+ if (ncur > G_MAXINT64)
+ GST_WARNING_OBJECT (object, "return value too big...");
+ GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ncur));
+ } else if ((curtype != GST_SEEK_TYPE_NONE)) {
+ GST_DEBUG_OBJECT (object, "Limiting seek start to inpoint");
+ ncur = object->inpoint;
+ } else {
+ GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE");
+ ncur = cur;
+ ncurtype = GST_SEEK_TYPE_NONE;
+ }
+
+ /* convert stop, we also need to limit it to object->stop */
+ if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET)
+ && (nle_object_to_media_time (object, stop, &nstop)))) {
+ if (nstop > G_MAXINT64)
+ GST_WARNING_OBJECT (object, "return value too big...");
+ GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (nstop));
+ } else {
+ GST_DEBUG_OBJECT (object, "Limiting end of seek to media_stop");
+ nle_object_to_media_time (object, object->stop, &nstop);
+ if (nstop > G_MAXINT64)
+ GST_WARNING_OBJECT (object, "return value too big...");
+ GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (nstop));
+ }
+
+
+ /* add accurate seekflags */
+ if (G_UNLIKELY (!(flags & GST_SEEK_FLAG_ACCURATE))) {
+ GST_DEBUG_OBJECT (object, "Adding GST_SEEK_FLAG_ACCURATE");
+ flags |= GST_SEEK_FLAG_ACCURATE;
+ } else {
+ GST_DEBUG_OBJECT (object,
+ "event already has GST_SEEK_FLAG_ACCURATE : %d", flags);
+ }
+
+
+
+ GST_DEBUG_OBJECT (object,
+ "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %"
+ GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype,
+ GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop));
+
+ event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
+ ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop);
+ GST_EVENT_SEQNUM (event2) = seqnum;
+ gst_event_unref (event);
+
+ return event2;
+
+ /* ERRORS */
+invalid_format:
+ {
+ GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME");
+ return event;
+ }
+}
+
+static GstEvent *
+translate_outgoing_seek (NleObject * object, GstEvent * event)
+{
+ GstEvent *event2;
+ GstFormat format;
+ gdouble rate;
+ GstSeekFlags flags;
+ GstSeekType curtype, stoptype;
+ GstSeekType ncurtype;
+ gint64 cur;
+ guint64 ncur;
+ gint64 stop;
+ guint64 nstop;
+ guint32 seqnum = GST_EVENT_SEQNUM (event);
+
+ gst_event_parse_seek (event, &rate, &format, &flags,
+ &curtype, &cur, &stoptype, &stop);
+
+ GST_DEBUG_OBJECT (object,
+ "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %"
+ GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype,
+ stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
+
+ if (G_UNLIKELY (format != GST_FORMAT_TIME))
+ goto invalid_format;
+
+ /* convert cur */
+ ncurtype = GST_SEEK_TYPE_SET;
+ if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET)
+ && (nle_media_to_object_time (object, cur, &ncur)))) {
+ /* cur is TYPE_SET and value is valid */
+ if (ncur > G_MAXINT64)
+ GST_WARNING_OBJECT (object, "return value too big...");
+ GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ncur));
+ } else if ((curtype != GST_SEEK_TYPE_NONE)) {
+ GST_DEBUG_OBJECT (object, "Limiting seek start to start");
+ ncur = object->start;
+ } else {
+ GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE");
+ ncur = cur;
+ ncurtype = GST_SEEK_TYPE_NONE;
+ }
+
+ /* convert stop, we also need to limit it to object->stop */
+ if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET)
+ && (nle_media_to_object_time (object, stop, &nstop)))) {
+ if (nstop > G_MAXINT64)
+ GST_WARNING_OBJECT (object, "return value too big...");
+ GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (nstop));
+ } else {
+ GST_DEBUG_OBJECT (object, "Limiting end of seek to stop");
+ nstop = object->stop;
+ if (nstop > G_MAXINT64)
+ GST_WARNING_OBJECT (object, "return value too big...");
+ GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (nstop));
+ }
+
+ GST_DEBUG_OBJECT (object,
+ "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %"
+ GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype,
+ GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop));
+
+ event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
+ ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop);
+ GST_EVENT_SEQNUM (event2) = seqnum;
+
+ gst_event_unref (event);
+
+ return event2;
+
+ /* ERRORS */
+invalid_format:
+ {
+ GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME");
+ return event;
+ }
+}
+
+static GstEvent *
+translate_outgoing_segment (NleObject * object, GstEvent * event)
+{
+ const GstSegment *orig;
+ GstSegment segment;
+ GstEvent *event2;
+ guint32 seqnum = GST_EVENT_SEQNUM (event);
+
+ /* only modify the streamtime */
+ gst_event_parse_segment (event, &orig);
+
+ GST_DEBUG_OBJECT (object, "Got SEGMENT %" GST_SEGMENT_FORMAT, orig);
+
+ if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) {
+ GST_WARNING_OBJECT (object,
+ "Can't translate segments with format != GST_FORMAT_TIME");
+ return event;
+ }
+
+ gst_segment_copy_into (orig, &segment);
+
+ nle_media_to_object_time (object, orig->time, &segment.time);
+
+ if (G_UNLIKELY (segment.time > G_MAXINT64))
+ GST_WARNING_OBJECT (object, "Return value too big...");
+
+ GST_DEBUG_OBJECT (object, "Sending SEGMENT %" GST_SEGMENT_FORMAT, &segment);
+
+ event2 = gst_event_new_segment (&segment);
+ GST_EVENT_SEQNUM (event2) = seqnum;
+ gst_event_unref (event);
+
+ return event2;
+}
+
+static GstEvent *
+translate_incoming_segment (NleObject * object, GstEvent * event)
+{
+ GstEvent *event2;
+ const GstSegment *orig;
+ GstSegment segment;
+ guint32 seqnum = GST_EVENT_SEQNUM (event);
+
+ /* only modify the streamtime */
+ gst_event_parse_segment (event, &orig);
+
+ GST_DEBUG_OBJECT (object,
+ "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop),
+ GST_TIME_ARGS (orig->time));
+
+ if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) {
+ GST_WARNING_OBJECT (object,
+ "Can't translate segments with format != GST_FORMAT_TIME");
+ return event;
+ }
+
+ gst_segment_copy_into (orig, &segment);
+
+ if (!nle_object_to_media_time (object, orig->time, &segment.time)) {
+ GST_DEBUG ("Can't convert media_time, using 0");
+ segment.time = 0;
+ };
+
+ if (G_UNLIKELY (segment.time > G_MAXINT64))
+ GST_WARNING_OBJECT (object, "Return value too big...");
+
+ GST_DEBUG_OBJECT (object,
+ "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (segment.start),
+ GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time));
+
+ event2 = gst_event_new_segment (&segment);
+ GST_EVENT_SEQNUM (event2) = seqnum;
+ gst_event_unref (event);
+
+ return event2;
+}
+
+static gboolean
+internalpad_event_function (GstPad * internal, GstObject * parent,
+ GstEvent * event)
+{
+ NlePadPrivate *priv = gst_pad_get_element_private (internal);
+ NleObject *object = priv->object;
+ gboolean res;
+
+ GST_DEBUG_OBJECT (internal, "event:%s (seqnum::%d)",
+ GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event));
+
+ if (G_UNLIKELY (!(priv->eventfunc))) {
+ GST_WARNING_OBJECT (internal,
+ "priv->eventfunc == NULL !! What is going on ?");
+ return FALSE;
+ }
+
+ switch (priv->dir) {
+ case GST_PAD_SRC:{
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEGMENT:
+ event = translate_outgoing_segment (object, event);
+ break;
+ case GST_EVENT_EOS:
+ break;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case GST_PAD_SINK:{
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ event = translate_outgoing_seek (object, event);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ GST_DEBUG_OBJECT (internal, "Calling priv->eventfunc %p", priv->eventfunc);
+ res = priv->eventfunc (internal, parent, event);
+
+ return res;
+}
+
+/*
+ translate_outgoing_position_query
+
+ Should only be called:
+ _ if the query is a GST_QUERY_POSITION
+ _ after the query was sent upstream
+ _ if the upstream query returned TRUE
+*/
+
+static gboolean
+translate_incoming_position_query (NleObject * object, GstQuery * query)
+{
+ GstFormat format;
+ gint64 cur, cur2;
+
+ gst_query_parse_position (query, &format, &cur);
+ if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
+ GST_WARNING_OBJECT (object,
+ "position query is in a format different from time, returning without modifying values");
+ goto beach;
+ }
+
+ nle_media_to_object_time (object, (guint64) cur, (guint64 *) & cur2);
+
+ GST_DEBUG_OBJECT (object,
+ "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
+ gst_query_set_position (query, GST_FORMAT_TIME, cur2);
+
+beach:
+ return TRUE;
+}
+
+static gboolean
+translate_outgoing_position_query (NleObject * object, GstQuery * query)
+{
+ GstFormat format;
+ gint64 cur, cur2;
+
+ gst_query_parse_position (query, &format, &cur);
+ if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
+ GST_WARNING_OBJECT (object,
+ "position query is in a format different from time, returning without modifying values");
+ goto beach;
+ }
+
+ if (G_UNLIKELY (!(nle_object_to_media_time (object, (guint64) cur,
+ (guint64 *) & cur2)))) {
+ GST_WARNING_OBJECT (object,
+ "Couldn't get media time for %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
+ goto beach;
+ }
+
+ GST_DEBUG_OBJECT (object,
+ "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
+ gst_query_set_position (query, GST_FORMAT_TIME, cur2);
+
+beach:
+ return TRUE;
+}
+
+static gboolean
+translate_incoming_duration_query (NleObject * object, GstQuery * query)
+{
+ GstFormat format;
+ gint64 cur;
+
+ gst_query_parse_duration (query, &format, &cur);
+ if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
+ GST_WARNING_OBJECT (object,
+ "We can only handle duration queries in GST_FORMAT_TIME");
+ return FALSE;
+ }
+
+ gst_query_set_duration (query, GST_FORMAT_TIME, object->duration);
+
+ return TRUE;
+}
+
+static gboolean
+internalpad_query_function (GstPad * internal, GstObject * parent,
+ GstQuery * query)
+{
+ NlePadPrivate *priv = gst_pad_get_element_private (internal);
+ NleObject *object = priv->object;
+ gboolean ret;
+
+ GST_DEBUG_OBJECT (internal, "querytype:%s",
+ gst_query_type_get_name (GST_QUERY_TYPE (query)));
+
+ if (!(priv->queryfunc)) {
+ GST_WARNING_OBJECT (internal,
+ "priv->queryfunc == NULL !! What is going on ?");
+ return FALSE;
+ }
+
+ if ((ret = priv->queryfunc (internal, parent, query))) {
+
+ switch (priv->dir) {
+ case GST_PAD_SRC:
+ break;
+ case GST_PAD_SINK:
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ ret = translate_outgoing_position_query (object, query);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return ret;
+}
+
+static gboolean
+ghostpad_event_function (GstPad * ghostpad, GstObject * parent,
+ GstEvent * event)
+{
+ NlePadPrivate *priv;
+ NleObject *object;
+ gboolean ret = FALSE;
+
+ priv = gst_pad_get_element_private (ghostpad);
+ object = priv->object;
+
+ GST_DEBUG_OBJECT (ghostpad, "event:%s", GST_EVENT_TYPE_NAME (event));
+
+ if (G_UNLIKELY (priv->eventfunc == NULL))
+ goto no_function;
+
+ switch (priv->dir) {
+ case GST_PAD_SRC:
+ {
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ GstPad *target;
+
+ event = nle_object_translate_incoming_seek (object, event);
+ if (!(target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad)))) {
+ g_assert ("Seeked a pad with not target SHOULD NOT HAPPEND");
+ ret = FALSE;
+ gst_event_unref (event);
+ event = NULL;
+ } else {
+ gst_object_unref (target);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case GST_PAD_SINK:{
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEGMENT:
+ event = translate_incoming_segment (object, event);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (event) {
+ GST_DEBUG_OBJECT (ghostpad, "Calling priv->eventfunc");
+ ret = priv->eventfunc (ghostpad, parent, event);
+ GST_DEBUG_OBJECT (ghostpad, "Returned from calling priv->eventfunc : %d",
+ ret);
+ }
+
+ return ret;
+
+ /* ERRORS */
+no_function:
+ {
+ GST_WARNING_OBJECT (ghostpad,
+ "priv->eventfunc == NULL !! What's going on ?");
+ return FALSE;
+ }
+}
+
+static gboolean
+ghostpad_query_function (GstPad * ghostpad, GstObject * parent,
+ GstQuery * query)
+{
+ NlePadPrivate *priv = gst_pad_get_element_private (ghostpad);
+ NleObject *object = NLE_OBJECT (parent);
+ gboolean pret = TRUE;
+
+ GST_DEBUG_OBJECT (ghostpad, "querytype:%s", GST_QUERY_TYPE_NAME (query));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_DURATION:
+ /* skip duration upstream query, we'll fill it in ourselves */
+ break;
+ default:
+ pret = priv->queryfunc (ghostpad, parent, query);
+ }
+
+ if (pret) {
+ /* translate result */
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ pret = translate_incoming_position_query (object, query);
+ break;
+ case GST_QUERY_DURATION:
+ pret = translate_incoming_duration_query (object, query);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return pret;
+}
+
+/* internal pad going away */
+static void
+internal_pad_finalizing (NlePadPrivate * priv, GObject * pad G_GNUC_UNUSED)
+{
+ g_slice_free (NlePadPrivate, priv);
+}
+
+static inline GstPad *
+get_proxy_pad (GstPad * ghostpad)
+{
+ GValue item = { 0, };
+ GstIterator *it;
+ GstPad *ret = NULL;
+
+ it = gst_pad_iterate_internal_links (ghostpad);
+ g_assert (it);
+ gst_iterator_next (it, &item);
+ ret = g_value_dup_object (&item);
+ g_value_unset (&item);
+ g_assert (ret);
+ gst_iterator_free (it);
+
+ return ret;
+}
+
+static void
+control_internal_pad (GstPad * ghostpad, NleObject * object)
+{
+ NlePadPrivate *priv;
+ NlePadPrivate *privghost;
+ GstPad *internal;
+
+ if (!ghostpad) {
+ GST_DEBUG_OBJECT (object, "We don't have a valid ghostpad !");
+ return;
+ }
+ privghost = gst_pad_get_element_private (ghostpad);
+
+ GST_LOG_OBJECT (ghostpad, "overriding ghostpad's internal pad function");
+
+ internal = get_proxy_pad (ghostpad);
+
+ if (G_UNLIKELY (!(priv = gst_pad_get_element_private (internal)))) {
+ GST_DEBUG_OBJECT (internal,
+ "Creating a NlePadPrivate to put in element_private");
+ priv = g_slice_new0 (NlePadPrivate);
+
+ /* Remember existing pad functions */
+ priv->eventfunc = GST_PAD_EVENTFUNC (internal);
+ priv->queryfunc = GST_PAD_QUERYFUNC (internal);
+ gst_pad_set_element_private (internal, priv);
+
+ g_object_weak_ref ((GObject *) internal,
+ (GWeakNotify) internal_pad_finalizing, priv);
+
+ /* add query/event function overrides on internal pad */
+ gst_pad_set_event_function (internal,
+ GST_DEBUG_FUNCPTR (internalpad_event_function));
+ gst_pad_set_query_function (internal,
+ GST_DEBUG_FUNCPTR (internalpad_query_function));
+ }
+
+ priv->object = object;
+ priv->ghostpriv = privghost;
+ priv->dir = GST_PAD_DIRECTION (ghostpad);
+ gst_object_unref (internal);
+
+ GST_DEBUG_OBJECT (ghostpad, "Done with pad %s:%s",
+ GST_DEBUG_PAD_NAME (ghostpad));
+}
+
+
+/**
+ * nle_object_ghost_pad:
+ * @object: #NleObject to add the ghostpad to
+ * @name: Name for the new pad
+ * @target: Target #GstPad to ghost
+ *
+ * Adds a #GstGhostPad overridding the correct pad [query|event]_function so
+ * that time shifting is done correctly
+ * The #GstGhostPad is added to the #NleObject
+ *
+ * /!\ This function doesn't check if the existing [src|sink] pad was removed
+ * first, so you might end up with more pads than wanted
+ *
+ * Returns: The #GstPad if everything went correctly, else NULL.
+ */
+GstPad *
+nle_object_ghost_pad (NleObject * object, const gchar * name, GstPad * target)
+{
+ GstPadDirection dir = GST_PAD_DIRECTION (target);
+ GstPad *ghost;
+
+ GST_DEBUG_OBJECT (object, "name:%s, target:%p", name, target);
+
+ g_return_val_if_fail (target, FALSE);
+ g_return_val_if_fail ((dir != GST_PAD_UNKNOWN), FALSE);
+
+ ghost = nle_object_ghost_pad_no_target (object, name, dir, NULL);
+ if (!ghost) {
+ GST_WARNING_OBJECT (object, "Couldn't create ghostpad");
+ return NULL;
+ }
+
+ if (!(nle_object_ghost_pad_set_target (object, ghost, target))) {
+ GST_WARNING_OBJECT (object,
+ "Couldn't set the target pad... removing ghostpad");
+ gst_object_unref (ghost);
+ return NULL;
+ }
+
+ GST_DEBUG_OBJECT (object, "activating ghostpad");
+ /* activate pad */
+ gst_pad_set_active (ghost, TRUE);
+ /* add it to element */
+ if (!(gst_element_add_pad (GST_ELEMENT (object), ghost))) {
+ GST_WARNING ("couldn't add newly created ghostpad");
+ return NULL;
+ }
+
+ return ghost;
+}
+
+/*
+ * nle_object_ghost_pad_no_target:
+ * /!\ Doesn't add the pad to the NleObject....
+ */
+GstPad *
+nle_object_ghost_pad_no_target (NleObject * object, const gchar * name,
+ GstPadDirection dir, GstPadTemplate * template)
+{
+ GstPad *ghost;
+ NlePadPrivate *priv;
+
+ /* create a no_target ghostpad */
+ if (template)
+ ghost = gst_ghost_pad_new_no_target_from_template (name, template);
+ else
+ ghost = gst_ghost_pad_new_no_target (name, dir);
+ if (!ghost)
+ return NULL;
+
+
+ /* remember the existing ghostpad event/query/link/unlink functions */
+ priv = g_slice_new0 (NlePadPrivate);
+ priv->dir = dir;
+ priv->object = object;
+
+ /* grab/replace event/query functions */
+ GST_DEBUG_OBJECT (ghost, "Setting priv->eventfunc to %p",
+ GST_PAD_EVENTFUNC (ghost));
+ priv->eventfunc = GST_PAD_EVENTFUNC (ghost);
+ priv->queryfunc = GST_PAD_QUERYFUNC (ghost);
+
+ gst_pad_set_event_function (ghost,
+ GST_DEBUG_FUNCPTR (ghostpad_event_function));
+ gst_pad_set_query_function (ghost,
+ GST_DEBUG_FUNCPTR (ghostpad_query_function));
+
+ gst_pad_set_element_private (ghost, priv);
+ control_internal_pad (ghost, object);
+
+ return ghost;
+}
+
+
+
+void
+nle_object_remove_ghost_pad (NleObject * object, GstPad * ghost)
+{
+ NlePadPrivate *priv;
+
+ GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost));
+
+ priv = gst_pad_get_element_private (ghost);
+ gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL);
+ gst_element_remove_pad (GST_ELEMENT (object), ghost);
+ if (priv)
+ g_slice_free (NlePadPrivate, priv);
+}
+
+gboolean
+nle_object_ghost_pad_set_target (NleObject * object, GstPad * ghost,
+ GstPad * target)
+{
+ NlePadPrivate *priv = gst_pad_get_element_private (ghost);
+
+ g_return_val_if_fail (priv, FALSE);
+ g_return_val_if_fail (GST_IS_PAD (ghost), FALSE);
+
+ if (target) {
+ GST_DEBUG_OBJECT (object, "setting target %s:%s on %s:%s",
+ GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (ghost));
+ } else {
+ GST_DEBUG_OBJECT (object, "removing target from ghostpad");
+ priv->pending_seek = NULL;
+ }
+
+ /* set target */
+ if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target))) {
+ GST_WARNING_OBJECT (priv->object, "Could not set ghost %s:%s "
+ "target to: %s:%s", GST_DEBUG_PAD_NAME (ghost),
+ GST_DEBUG_PAD_NAME (target));
+ return FALSE;
+ }
+
+ if (target && priv->pending_seek) {
+ gboolean res = gst_pad_send_event (ghost, priv->pending_seek);
+
+ GST_INFO_OBJECT (object, "Sending our pending seek event: %" GST_PTR_FORMAT
+ " -- Result is %i", priv->pending_seek, res);
+
+ priv->pending_seek = NULL;
+ }
+
+ return TRUE;
+}
+
+void
+nle_init_ghostpad_category (void)
+{
+ GST_DEBUG_CATEGORY_INIT (nleghostpad, "nleghostpad",
+ GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin GhostPad");
+
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nleghostpad.h: Header for helper ghostpad
+ *
+ * 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 __NLE_GHOSTPAD_H__
+#define __NLE_GHOSTPAD_H__
+
+#include <gst/gst.h>
+
+#include "nletypes.h"
+
+G_BEGIN_DECLS
+
+GstPad *nle_object_ghost_pad (NleObject * object,
+ const gchar * name, GstPad * target) G_GNUC_INTERNAL;
+
+GstPad *nle_object_ghost_pad_no_target (NleObject * object,
+ const gchar * name, GstPadDirection dir, GstPadTemplate *templ) G_GNUC_INTERNAL;
+
+gboolean nle_object_ghost_pad_set_target (NleObject * object,
+ GstPad * ghost, GstPad * target) G_GNUC_INTERNAL;
+
+void nle_object_remove_ghost_pad (NleObject * object, GstPad * ghost) G_GNUC_INTERNAL;
+GstEvent * nle_object_translate_incoming_seek (NleObject * object, GstEvent * event) G_GNUC_INTERNAL;
+
+void nle_init_ghostpad_category (void) G_GNUC_INTERNAL;
+
+G_END_DECLS
+
+#endif /* __NLE_GHOSTPAD_H__ */
--- /dev/null
+/* Gnonlin
+ * Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
+ * <2004-2008> Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "nle.h"
+
+/**
+ * SECTION:nleobject
+ * @short_description: Base class for GNonLin elements
+ *
+ * <refsect2>
+ * <para>
+ * NleObject encapsulates default behaviour and implements standard
+ * properties provided by all the GNonLin elements.
+ * </para>
+ * </refsect2>
+ *
+ */
+
+
+GST_DEBUG_CATEGORY_STATIC (nleobject_debug);
+#define GST_CAT_DEFAULT nleobject_debug
+
+static GObjectClass *parent_class = NULL;
+
+/****************************************************
+ * Helper macros *
+ ****************************************************/
+#define CHECK_AND_SET(PROPERTY, property, prop_str, print_format) \
+{ \
+if (object->pending_##property != object->property) { \
+ object->property = object->pending_##property; \
+ GST_DEBUG_OBJECT(object, "Setting " prop_str " to %" \
+ print_format, object->property); \
+} else \
+ GST_DEBUG_OBJECT(object, "Nothing to do for " prop_str); \
+}
+
+#define SET_PENDING_VALUE(property, property_str, type, print_format) \
+nleobject->pending_##property = g_value_get_##type (value); \
+if (nleobject->property != nleobject->pending_##property) { \
+ GST_DEBUG_OBJECT(object, "Setting pending " property_str " to %" \
+ print_format, nleobject->pending_##property); \
+ nle_object_set_commit_needed (nleobject); \
+} else \
+ GST_DEBUG_OBJECT(object, "Pending " property_str " did not change");
+
+enum
+{
+ PROP_0,
+ PROP_START,
+ PROP_DURATION,
+ PROP_STOP,
+ PROP_INPOINT,
+ PROP_PRIORITY,
+ PROP_ACTIVE,
+ PROP_CAPS,
+ PROP_EXPANDABLE,
+ PROP_MEDIA_DURATION_FACTOR,
+ PROP_LAST
+};
+
+enum
+{
+ COMMIT_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint _signals[LAST_SIGNAL] = { 0 };
+
+static GParamSpec *properties[PROP_LAST];
+
+static void nle_object_dispose (GObject * object);
+
+static void nle_object_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void nle_object_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void nle_object_constructed (GObject * object);
+
+static GstStateChangeReturn nle_object_change_state (GstElement * element,
+ GstStateChange transition);
+
+static gboolean nle_object_prepare_func (NleObject * object);
+static gboolean nle_object_cleanup_func (NleObject * object);
+static gboolean nle_object_commit_func (NleObject * object, gboolean recurse);
+
+static GstStateChangeReturn nle_object_prepare (NleObject * object);
+
+static void
+nle_object_class_init (NleObjectClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ NleObjectClass *nleobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ nleobject_class = (NleObjectClass *) klass;
+ GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject",
+ GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object");
+ parent_class = g_type_class_ref (GST_TYPE_BIN);
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_object_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property);
+ gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed);
+ gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose);
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_object_change_state);
+
+ nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_object_prepare_func);
+ nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func);
+ nleobject_class->commit_signal_handler =
+ GST_DEBUG_FUNCPTR (nle_object_commit);
+ nleobject_class->commit = GST_DEBUG_FUNCPTR (nle_object_commit_func);
+
+ /**
+ * NleObject:start
+ *
+ * The start position relative to the parent in nanoseconds.
+ */
+ properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
+ "The start position relative to the parent (in nanoseconds)",
+ 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_START,
+ properties[PROP_START]);
+
+ /**
+ * NleObject:duration
+ *
+ * The outgoing duration in nanoseconds.
+ */
+ properties[PROP_DURATION] = g_param_spec_int64 ("duration", "Duration",
+ "Outgoing duration (in nanoseconds)", 0, G_MAXINT64, 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_DURATION,
+ properties[PROP_DURATION]);
+
+ /**
+ * NleObject:stop
+ *
+ * The stop position relative to the parent in nanoseconds.
+ *
+ * This value is computed based on the values of start and duration.
+ */
+ properties[PROP_STOP] = g_param_spec_uint64 ("stop", "Stop",
+ "The stop position relative to the parent (in nanoseconds)",
+ 0, G_MAXUINT64, 0, G_PARAM_READABLE);
+ g_object_class_install_property (gobject_class, PROP_STOP,
+ properties[PROP_STOP]);
+
+ /**
+ * NleObject:inpoint
+ *
+ * The media start position in nanoseconds.
+ *
+ * Also called 'in-point' in video-editing, this corresponds to
+ * what position in the 'contained' object we should start outputting from.
+ */
+ properties[PROP_INPOINT] =
+ g_param_spec_uint64 ("inpoint", "Media start",
+ "The media start position (in nanoseconds)", 0, G_MAXUINT64,
+ GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_INPOINT,
+ properties[PROP_INPOINT]);
+
+ /**
+ * NleObject:priority
+ *
+ * The priority of the object in the container.
+ *
+ * The highest priority is 0, meaning this object will be selected over
+ * any other between start and stop.
+ *
+ * The lowest priority is G_MAXUINT32.
+ *
+ * Objects whose priority is (-1) will be considered as 'default' objects
+ * in NleComposition and their start/stop values will be modified as to
+ * fit the whole duration of the composition.
+ */
+ properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
+ "The priority of the object (0 = highest priority)", 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_PRIORITY,
+ properties[PROP_PRIORITY]);
+
+ /**
+ * NleObject:active
+ *
+ * Indicates whether this object should be used by its container.
+ *
+ * Set to #TRUE to temporarily disable this object in a #NleComposition.
+ */
+ properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active",
+ "Use this object in the NleComposition", TRUE, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_ACTIVE,
+ properties[PROP_ACTIVE]);
+
+ /**
+ * NleObject:caps
+ *
+ * Caps used to filter/choose the output stream.
+ *
+ * If the controlled object produces several stream, you can set this
+ * property to choose a specific stream.
+ *
+ * If nothing is specified then a source pad will be chosen at random.
+ */
+ properties[PROP_CAPS] = g_param_spec_boxed ("caps", "Caps",
+ "Caps used to filter/choose the output stream",
+ GST_TYPE_CAPS, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CAPS,
+ properties[PROP_CAPS]);
+
+ /**
+ * NleObject:expandable
+ *
+ * Indicates whether this object should expand to the full duration of its
+ * container #NleComposition.
+ */
+ properties[PROP_EXPANDABLE] =
+ g_param_spec_boolean ("expandable", "Expandable",
+ "Expand to the full duration of the container composition", FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_EXPANDABLE,
+ properties[PROP_EXPANDABLE]);
+
+ /**
+ * NleObject:media-duration-factor
+ *
+ * Indicates the relative rate caused by this object, in other words, the
+ * relation between the rate of media entering and leaving this object. I.e.
+ * if object pulls data at twice the speed it sends it (e.g. `pitch
+ * tempo=2.0`), this value is set to 2.0.
+ */
+ properties[PROP_MEDIA_DURATION_FACTOR] =
+ g_param_spec_double ("media-duration-factor", "Media duration factor",
+ "The relative rate caused by this object", 0.01, G_MAXDOUBLE,
+ 1.0, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_MEDIA_DURATION_FACTOR,
+ properties[PROP_MEDIA_DURATION_FACTOR]);
+
+ /**
+ * NleObject::commit
+ * @object: a #NleObject
+ * @recurse: Whether to commit recursiverly into (NleComposition) children of
+ * @object. This is used in case we have composition inside
+ * a nlesource composition, telling it to commit the included
+ * composition state.
+ *
+ * Action signal to commit all the pending changes of the composition and
+ * its children timing properties
+ *
+ * Returns: %TRUE if changes have been commited, %FALSE if nothing had to
+ * be commited
+ */
+ _signals[COMMIT_SIGNAL] = g_signal_new ("commit", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (NleObjectClass, commit_signal_handler), NULL, NULL, NULL,
+ G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+nle_object_init (NleObject * object, NleObjectClass * klass)
+{
+ object->start = object->pending_start = 0;
+ object->duration = object->pending_duration = 0;
+ object->stop = 0;
+
+ object->inpoint = object->pending_inpoint = GST_CLOCK_TIME_NONE;
+ object->priority = object->pending_priority = 0;
+ object->active = object->pending_active = TRUE;
+
+ object->caps = gst_caps_new_any ();
+
+ object->segment_rate = 1.0;
+ object->segment_start = -1;
+ object->segment_stop = -1;
+ object->media_duration_factor = 1.0;
+ object->recursive_media_duration_factor = 1.0;
+
+ object->srcpad = nle_object_ghost_pad_no_target (object,
+ "src", GST_PAD_SRC,
+ gst_element_class_get_pad_template ((GstElementClass *) klass, "src"));
+
+ gst_element_add_pad (GST_ELEMENT (object), object->srcpad);
+}
+
+static void
+nle_object_dispose (GObject * object)
+{
+ NleObject *nle = (NleObject *) object;
+
+ if (nle->caps) {
+ gst_caps_unref (nle->caps);
+ nle->caps = NULL;
+ }
+
+ if (nle->srcpad) {
+ nle_object_remove_ghost_pad (nle, nle->srcpad);
+ nle->srcpad = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/**
+ * nle_object_to_media_time:
+ * @object: a #NleObject
+ * @objecttime: The #GstClockTime we want to convert
+ * @mediatime: A pointer on a #GstClockTime to fill
+ *
+ * Converts a #GstClockTime from the object (container) context to the media context
+ *
+ * Returns: TRUE if @objecttime was within the limits of the @object start/stop time,
+ * FALSE otherwise
+ */
+gboolean
+nle_object_to_media_time (NleObject * object, GstClockTime otime,
+ GstClockTime * mtime)
+{
+ g_return_val_if_fail (mtime, FALSE);
+
+ GST_DEBUG_OBJECT (object, "ObjectTime : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (otime));
+
+ GST_DEBUG_OBJECT (object,
+ "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] "
+ "Media start: %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start),
+ GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint));
+
+ /* limit check */
+ if (G_UNLIKELY ((otime < object->start))) {
+ GST_DEBUG_OBJECT (object, "ObjectTime is before start");
+ *mtime = (object->inpoint == GST_CLOCK_TIME_NONE) ? 0 : object->inpoint;
+ return FALSE;
+ }
+
+ if (G_UNLIKELY ((otime >= object->stop))) {
+ GST_DEBUG_OBJECT (object, "ObjectTime is after stop");
+ if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint)))
+ *mtime =
+ object->inpoint +
+ object->duration * object->recursive_media_duration_factor;
+ else
+ *mtime =
+ (object->stop -
+ object->start) * object->recursive_media_duration_factor;
+ return FALSE;
+ }
+
+ if (G_UNLIKELY (object->inpoint == GST_CLOCK_TIME_NONE)) {
+ /* no time shifting, for live sources ? */
+ *mtime = (otime - object->start) * object->recursive_media_duration_factor;
+ } else {
+ *mtime =
+ (otime - object->start) * object->recursive_media_duration_factor +
+ object->inpoint;
+ }
+
+ GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (*mtime));
+
+ return TRUE;
+}
+
+/**
+ * nle_media_to_object_time:
+ * @object: The #NleObject
+ * @mediatime: The #GstClockTime we want to convert
+ * @objecttime: A pointer on a #GstClockTime to fill
+ *
+ * Converts a #GstClockTime from the media context to the object (container) context
+ *
+ * Returns: TRUE if @objecttime was within the limits of the @object media start/stop time,
+ * FALSE otherwise
+ */
+
+gboolean
+nle_media_to_object_time (NleObject * object, GstClockTime mtime,
+ GstClockTime * otime)
+{
+ g_return_val_if_fail (otime, FALSE);
+
+ GST_DEBUG_OBJECT (object, "MediaTime : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (mtime));
+
+ GST_DEBUG_OBJECT (object,
+ "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] "
+ "inpoint %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start),
+ GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint));
+
+
+ /* limit check */
+ if (G_UNLIKELY ((object->inpoint != GST_CLOCK_TIME_NONE)
+ && (mtime < object->inpoint))) {
+ GST_DEBUG_OBJECT (object, "media time is before inpoint, forcing to start");
+ *otime = object->start;
+ return FALSE;
+ }
+
+ if (G_LIKELY (object->inpoint != GST_CLOCK_TIME_NONE)) {
+ *otime = mtime - object->inpoint + object->start;
+ } else
+ *otime = mtime + object->start;
+
+ GST_DEBUG_OBJECT (object, "Returning ObjectTime : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (*otime));
+ return TRUE;
+}
+
+static gboolean
+nle_object_prepare_func (NleObject * object)
+{
+ GST_DEBUG_OBJECT (object, "default prepare function, returning TRUE");
+
+ return TRUE;
+}
+
+static GstStateChangeReturn
+nle_object_prepare (NleObject * object)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ GST_DEBUG_OBJECT (object, "preparing");
+
+ if (!(NLE_OBJECT_GET_CLASS (object)->prepare (object)))
+ ret = GST_STATE_CHANGE_FAILURE;
+
+ GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret);
+
+ return ret;
+}
+
+static gboolean
+nle_object_cleanup_func (NleObject * object)
+{
+ GST_DEBUG_OBJECT (object, "default cleanup function, returning TRUE");
+
+ return TRUE;
+}
+
+GstStateChangeReturn
+nle_object_cleanup (NleObject * object)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ GST_DEBUG_OBJECT (object, "cleaning-up");
+
+ if (!(NLE_OBJECT_GET_CLASS (object)->cleanup (object)))
+ ret = GST_STATE_CHANGE_FAILURE;
+
+ GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret);
+
+ return ret;
+}
+
+void
+nle_object_set_caps (NleObject * object, const GstCaps * caps)
+{
+ if (object->caps)
+ gst_caps_unref (object->caps);
+
+ object->caps = gst_caps_copy (caps);
+}
+
+static inline void
+_update_stop (NleObject * nleobject)
+{
+ /* check if start/duration has changed */
+
+ if ((nleobject->pending_start + nleobject->pending_duration) !=
+ nleobject->stop) {
+ nleobject->stop = nleobject->pending_start + nleobject->pending_duration;
+
+ GST_LOG_OBJECT (nleobject,
+ "Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT
+ ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (nleobject->stop),
+ GST_TIME_ARGS (nleobject->pending_start),
+ GST_TIME_ARGS (nleobject->pending_duration));
+ g_object_notify_by_pspec (G_OBJECT (nleobject), properties[PROP_STOP]);
+ }
+}
+
+static void
+update_values (NleObject * object)
+{
+ CHECK_AND_SET (START, start, "start", G_GUINT64_FORMAT);
+ CHECK_AND_SET (INPOINT, inpoint, "inpoint", G_GUINT64_FORMAT);
+ CHECK_AND_SET (DURATION, duration, "duration", G_GINT64_FORMAT);
+ CHECK_AND_SET (PRIORITY, priority, "priority", G_GUINT32_FORMAT);
+ CHECK_AND_SET (ACTIVE, active, "active", G_GUINT32_FORMAT);
+
+ _update_stop (object);
+}
+
+static gboolean
+nle_object_commit_func (NleObject * object, gboolean recurse)
+{
+ GST_DEBUG_OBJECT (object, "Commiting object changed");
+
+ if (object->commit_needed == FALSE) {
+ GST_INFO_OBJECT (object, "No changes to commit");
+
+ return FALSE;
+ }
+
+ update_values (object);
+
+ GST_DEBUG_OBJECT (object, "Done commiting");
+
+ return TRUE;
+}
+
+static void
+nle_object_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ NleObject *nleobject = (NleObject *) object;
+
+ g_return_if_fail (NLE_IS_OBJECT (object));
+
+ GST_OBJECT_LOCK (object);
+ switch (prop_id) {
+ case PROP_START:
+ SET_PENDING_VALUE (start, "start", uint64, G_GUINT64_FORMAT);
+ break;
+ case PROP_DURATION:
+ SET_PENDING_VALUE (duration, "duration", int64, G_GINT64_FORMAT);
+ break;
+ case PROP_INPOINT:
+ SET_PENDING_VALUE (inpoint, "inpoint", uint64, G_GUINT64_FORMAT);
+ break;
+ case PROP_PRIORITY:
+ SET_PENDING_VALUE (priority, "priority", uint, G_GUINT32_FORMAT);
+ break;
+ case PROP_ACTIVE:
+ SET_PENDING_VALUE (active, "active", boolean, G_GUINT32_FORMAT);
+ break;
+ case PROP_CAPS:
+ nle_object_set_caps (nleobject, gst_value_get_caps (value));
+ break;
+ case PROP_EXPANDABLE:
+ if (g_value_get_boolean (value))
+ GST_OBJECT_FLAG_SET (nleobject, NLE_OBJECT_EXPANDABLE);
+ else
+ GST_OBJECT_FLAG_UNSET (nleobject, NLE_OBJECT_EXPANDABLE);
+ break;
+ case PROP_MEDIA_DURATION_FACTOR:
+ nleobject->media_duration_factor = g_value_get_double (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK (object);
+}
+
+static void
+nle_object_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ NleObject *nleobject = (NleObject *) object;
+
+ switch (prop_id) {
+ case PROP_START:
+ g_value_set_uint64 (value, nleobject->pending_start);
+ break;
+ case PROP_DURATION:
+ g_value_set_int64 (value, nleobject->pending_duration);
+ break;
+ case PROP_STOP:
+ g_value_set_uint64 (value, nleobject->stop);
+ break;
+ case PROP_INPOINT:
+ g_value_set_uint64 (value, nleobject->pending_inpoint);
+ break;
+ case PROP_PRIORITY:
+ g_value_set_uint (value, nleobject->pending_priority);
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, nleobject->pending_active);
+ break;
+ case PROP_CAPS:
+ gst_value_set_caps (value, nleobject->caps);
+ break;
+ case PROP_EXPANDABLE:
+ g_value_set_boolean (value, NLE_OBJECT_IS_EXPANDABLE (object));
+ break;
+ case PROP_MEDIA_DURATION_FACTOR:
+ g_value_set_double (value, nleobject->media_duration_factor);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nle_object_constructed (GObject * object)
+{
+ NleObject *nleobject = (NleObject *) object;
+
+ _update_stop (nleobject);
+}
+
+static GstStateChangeReturn
+nle_object_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ {
+ GstObject *parent = gst_object_get_parent (GST_OBJECT (element));
+
+ /* Going to READY and if we are not in a composition, we need to make
+ * sure that the object positioning state is properly commited */
+ if (parent) {
+ if (g_strcmp0 (GST_ELEMENT_NAME (GST_ELEMENT (parent)), "current-bin")
+ && !NLE_OBJECT_IS_COMPOSITION (NLE_OBJECT (element))) {
+ GST_INFO ("Adding nleobject to something that is not a composition,"
+ " commiting ourself");
+ nle_object_commit (NLE_OBJECT (element), FALSE);
+ }
+
+ gst_object_unref (parent);
+ }
+ }
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ if (nle_object_prepare (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) {
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto beach;
+ }
+ break;
+ default:
+ break;
+ }
+
+ GST_DEBUG_OBJECT (element, "Calling parent change_state");
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ GST_DEBUG_OBJECT (element, "Return from parent change_state was %d", ret);
+
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto beach;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ /* cleanup nleobject */
+ if (nle_object_cleanup (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE)
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ default:
+ break;
+ }
+
+beach:
+ return ret;
+}
+
+void
+nle_object_set_commit_needed (NleObject * object)
+{
+ if (G_UNLIKELY (object->commiting)) {
+ GST_WARNING_OBJECT (object,
+ "Trying to set 'commit-needed' while commiting");
+
+ return;
+ }
+
+ GST_DEBUG_OBJECT (object, "Setting 'commit_needed'");
+ object->commit_needed = TRUE;
+}
+
+gboolean
+nle_object_commit (NleObject * object, gboolean recurse)
+{
+ gboolean ret;
+
+ GST_DEBUG_OBJECT (object, "Commiting object state");
+
+ object->commiting = TRUE;
+ ret = NLE_OBJECT_GET_CLASS (object)->commit (object, recurse);
+ object->commiting = FALSE;
+
+ return ret;
+
+}
+
+static void
+_send_seek_event (const GValue * item, gpointer seek_event)
+{
+ GstElement *child = g_value_get_object (item);
+
+ gst_element_send_event (child, gst_event_ref (seek_event));
+}
+
+void
+nle_object_seek_all_children (NleObject * object, GstEvent * seek_event)
+{
+ GstIterator *it = gst_bin_iterate_recurse (GST_BIN (object));
+
+ while (gst_iterator_foreach (it, _send_seek_event,
+ seek_event) == GST_ITERATOR_RESYNC)
+ gst_iterator_resync (it);
+
+ gst_iterator_free (it);
+ gst_event_unref (seek_event);
+}
+
+void
+nle_object_reset (NleObject * object)
+{
+ GST_INFO_OBJECT (object, "Resetting child timing values to default");
+
+ object->start = 0;
+ object->duration = 0;
+ object->stop = 0;
+ object->inpoint = GST_CLOCK_TIME_NONE;
+ object->priority = 0;
+ object->active = TRUE;
+}
+
+GType
+nle_object_get_type (void)
+{
+ static volatile gsize type = 0;
+
+ if (g_once_init_enter (&type)) {
+ GType _type;
+ static const GTypeInfo info = {
+ sizeof (NleObjectClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) nle_object_class_init,
+ NULL,
+ NULL,
+ sizeof (NleObject),
+ 0,
+ (GInstanceInitFunc) nle_object_init,
+ };
+
+ _type = g_type_register_static (GST_TYPE_BIN,
+ "NleObject", &info, G_TYPE_FLAG_ABSTRACT);
+ g_once_init_leave (&type, _type);
+ }
+ return type;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
+ * 2004-2008 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nleobject.h: Header for base NleObject
+ *
+ * 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 __NLE_OBJECT_H__
+#define __NLE_OBJECT_H__
+
+#include <gst/gst.h>
+
+#include "nletypes.h"
+
+G_BEGIN_DECLS
+#define NLE_TYPE_OBJECT \
+ (nle_object_get_type())
+#define NLE_OBJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_OBJECT,NleObject))
+#define NLE_OBJECT_CAST(obj) ((NleObject*) (obj))
+#define NLE_OBJECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_OBJECT,NleObjectClass))
+#define NLE_OBJECT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NLE_TYPE_OBJECT, NleObjectClass))
+#define NLE_IS_OBJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_OBJECT))
+#define NLE_IS_OBJECT_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_OBJECT))
+
+#define NLE_OBJECT_SRC(obj) (((NleObject *) obj)->srcpad)
+
+/**
+ * NleObjectFlags:
+ * @NLE_OBJECT_IS_SOURCE:
+ * @NLE_OBJECT_IS_OPERATION:
+ * @NLE_OBJECT_IS_EXPANDABLE: The #NleObject start/stop will extend accross the full composition.
+ * @NLE_OBJECT_LAST_FLAG:
+*/
+
+typedef enum
+{
+ NLE_OBJECT_SOURCE = (GST_BIN_FLAG_LAST << 0),
+ NLE_OBJECT_OPERATION = (GST_BIN_FLAG_LAST << 1),
+ NLE_OBJECT_EXPANDABLE = (GST_BIN_FLAG_LAST << 2),
+ NLE_OBJECT_COMPOSITION = (GST_BIN_FLAG_LAST << 3),
+ /* padding */
+ NLE_OBJECT_LAST_FLAG = (GST_BIN_FLAG_LAST << 5)
+} NleObjectFlags;
+
+
+#define NLE_OBJECT_IS_SOURCE(obj) \
+ (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_SOURCE))
+#define NLE_OBJECT_IS_OPERATION(obj) \
+ (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_OPERATION))
+#define NLE_OBJECT_IS_EXPANDABLE(obj) \
+ (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_EXPANDABLE))
+#define NLE_OBJECT_IS_COMPOSITION(obj) \
+ (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_COMPOSITION))
+
+/* For internal usage only */
+#define NLE_OBJECT_START(obj) (NLE_OBJECT_CAST (obj)->start)
+#define NLE_OBJECT_STOP(obj) (NLE_OBJECT_CAST (obj)->stop)
+#define NLE_OBJECT_DURATION(obj) (NLE_OBJECT_CAST (obj)->duration)
+#define NLE_OBJECT_INPOINT(obj) (NLE_OBJECT_CAST (obj)->inpoint)
+#define NLE_OBJECT_PRIORITY(obj) (NLE_OBJECT_CAST (obj)->priority)
+#define NLE_OBJECT_ACTIVE(obj) (NLE_OBJECT_DURATION(obj) > 0 && NLE_OBJECT_CAST (obj)->active)
+
+#define NLE_OBJECT_IS_COMMITING(obj) (NLE_OBJECT_CAST (obj)->commiting)
+
+struct _NleObject
+{
+ GstBin parent;
+
+ GstPad *srcpad;
+
+ /* Time positionning */
+ GstClockTime start;
+ GstClockTime inpoint;
+ GstClockTimeDiff duration;
+
+ /* Pending time positionning
+ * Should be == GST_CLOCK_TIME_NONE when nothing to do
+ */
+ GstClockTime pending_start;
+ GstClockTime pending_inpoint;
+ GstClockTimeDiff pending_duration;
+ guint32 pending_priority;
+ gboolean pending_active;
+
+ gboolean commit_needed;
+ gboolean commiting; /* Set to TRUE during the commiting time only */
+
+ gboolean expandable;
+
+ /* read-only */
+ GstClockTime stop;
+
+ /* priority in parent */
+ guint32 priority;
+
+ /* active in parent */
+ gboolean active;
+
+ /* Filtering caps */
+ GstCaps *caps;
+
+ /* current segment seek <RO> */
+ gdouble segment_rate;
+ GstSeekFlags segment_flags;
+ gint64 segment_start;
+ gint64 segment_stop;
+
+ /* the rate at which this object speeds up or slows down media */
+ gdouble media_duration_factor;
+ /* the rate at which this object and all of its children speed up or slow
+ * down media */
+ gdouble recursive_media_duration_factor;
+
+ gboolean in_composition;
+};
+
+struct _NleObjectClass
+{
+ GstBinClass parent_class;
+
+ /* Signal method handler */
+ gboolean (*commit_signal_handler) (NleObject * object, gboolean recurse);
+
+ /* virtual methods for subclasses */
+ gboolean (*prepare) (NleObject * object);
+ gboolean (*cleanup) (NleObject * object);
+ gboolean (*commit) (NleObject * object, gboolean recurse);
+};
+
+GType nle_object_get_type (void)G_GNUC_INTERNAL;
+
+gboolean
+nle_object_to_media_time (NleObject * object, GstClockTime otime,
+ GstClockTime * mtime) G_GNUC_INTERNAL;
+
+gboolean
+nle_media_to_object_time (NleObject * object, GstClockTime mtime,
+ GstClockTime * otime) G_GNUC_INTERNAL;
+
+void
+nle_object_set_caps (NleObject * object, const GstCaps * caps) G_GNUC_INTERNAL;
+
+void
+nle_object_set_commit_needed (NleObject *object) G_GNUC_INTERNAL;
+
+gboolean
+nle_object_commit (NleObject *object, gboolean recurse) G_GNUC_INTERNAL;
+
+void
+nle_object_reset (NleObject *object) G_GNUC_INTERNAL;
+
+GstStateChangeReturn
+nle_object_cleanup (NleObject * object) G_GNUC_INTERNAL;
+
+void nle_object_seek_all_children (NleObject *object, GstEvent *seek_event) G_GNUC_INTERNAL;
+
+G_END_DECLS
+#endif /* __NLE_OBJECT_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
+ * 2004-2008 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "nle.h"
+
+/**
+ * SECTION:element-nleoperation
+ *
+ * <refsect2>
+ * <para>
+ * A NleOperation performs a transformation or mixing operation on the
+ * data from one or more #NleSources, which is used to implement filters or
+ * effects.
+ * </para>
+ * </refsect2>
+ */
+
+static GstStaticPadTemplate nle_operation_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate nle_operation_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink%d",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (nleoperation);
+#define GST_CAT_DEFAULT nleoperation
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (nleoperation, "nleoperation", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Operation element");
+#define nle_operation_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (NleOperation, nle_operation, NLE_TYPE_OBJECT,
+ _do_init);
+
+enum
+{
+ ARG_0,
+ ARG_SINKS,
+};
+
+enum
+{
+ INPUT_PRIORITY_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint nle_operation_signals[LAST_SIGNAL] = { 0 };
+
+static void nle_operation_dispose (GObject * object);
+
+static void nle_operation_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void nle_operation_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean nle_operation_prepare (NleObject * object);
+static gboolean nle_operation_cleanup (NleObject * object);
+
+static gboolean nle_operation_add_element (GstBin * bin, GstElement * element);
+static gboolean nle_operation_remove_element (GstBin * bin,
+ GstElement * element);
+
+static GstPad *nle_operation_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
+static void nle_operation_release_pad (GstElement * element, GstPad * pad);
+
+static void synchronize_sinks (NleOperation * operation);
+static gboolean remove_sink_pad (NleOperation * operation, GstPad * sinkpad);
+
+
+static gboolean
+nle_operation_send_event (GstElement * element, GstEvent * event)
+{
+ gboolean res = TRUE;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ nle_object_seek_all_children (NLE_OBJECT (element), event);
+ break;
+ default:
+ res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
+ break;
+ }
+
+ return res;
+}
+
+static void
+nle_operation_class_init (NleOperationClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstBinClass *gstbin_class = (GstBinClass *) klass;
+
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
+ NleObjectClass *nleobject_class = (NleObjectClass *) klass;
+
+ gst_element_class_set_static_metadata (gstelement_class, "GNonLin Operation",
+ "Filter/Editor",
+ "Encapsulates filters/effects for use with NLE Objects",
+ "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>");
+
+ gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_operation_dispose);
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_operation_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_operation_get_property);
+
+ /**
+ * NleOperation:sinks:
+ *
+ * Specifies the number of sink pads the operation should provide.
+ * If the sinks property is -1 (the default) pads are only created as
+ * demanded via get_request_pad() calls on the element.
+ */
+ g_object_class_install_property (gobject_class, ARG_SINKS,
+ g_param_spec_int ("sinks", "Sinks",
+ "Number of input sinks (-1 for automatic handling)", -1, G_MAXINT, -1,
+ G_PARAM_READWRITE));
+
+ /**
+ * NleOperation:input-priority-changed:
+ * @pad: The operation's input pad whose priority changed.
+ * @priority: The new priority
+ *
+ * Signals that the @priority of the stream being fed to the given @pad
+ * might have changed.
+ */
+ nle_operation_signals[INPUT_PRIORITY_CHANGED] =
+ g_signal_new ("input-priority-changed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NleOperationClass,
+ input_priority_changed), NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2, GST_TYPE_PAD, G_TYPE_UINT);
+
+ gstelement_class->request_new_pad =
+ GST_DEBUG_FUNCPTR (nle_operation_request_new_pad);
+ gstelement_class->release_pad = GST_DEBUG_FUNCPTR (nle_operation_release_pad);
+ gstelement_class->send_event = GST_DEBUG_FUNCPTR (nle_operation_send_event);
+
+ gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_operation_add_element);
+ gstbin_class->remove_element =
+ GST_DEBUG_FUNCPTR (nle_operation_remove_element);
+
+ nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_operation_prepare);
+ nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_operation_cleanup);
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &nle_operation_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &nle_operation_sink_template);
+}
+
+static void
+nle_operation_dispose (GObject * object)
+{
+ NleOperation *oper = (NleOperation *) object;
+
+ GST_DEBUG_OBJECT (object, "Disposing of source pad");
+
+ nle_object_ghost_pad_set_target (NLE_OBJECT (object),
+ NLE_OBJECT (object)->srcpad, NULL);
+
+ GST_DEBUG_OBJECT (object, "Disposing of sink pad(s)");
+ while (oper->sinks) {
+ GstPad *ghost = (GstPad *) oper->sinks->data;
+ remove_sink_pad (oper, ghost);
+ }
+
+ GST_DEBUG_OBJECT (object, "Done, calling parent class ::dispose()");
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+nle_operation_reset (NleOperation * operation)
+{
+ operation->num_sinks = 1;
+ operation->realsinks = 0;
+ operation->next_base_time = 0;
+}
+
+static void
+nle_operation_init (NleOperation * operation)
+{
+ GST_OBJECT_FLAG_SET (operation, NLE_OBJECT_OPERATION);
+ nle_operation_reset (operation);
+ operation->element = NULL;
+}
+
+static gboolean
+element_is_valid_filter (GstElement * element, gboolean * isdynamic)
+{
+ gboolean havesink = FALSE;
+ gboolean havesrc = FALSE;
+ gboolean done = FALSE;
+ GstIterator *pads;
+ GValue item = { 0, };
+
+ if (isdynamic)
+ *isdynamic = FALSE;
+
+ pads = gst_element_iterate_pads (element);
+
+ while (!done) {
+ switch (gst_iterator_next (pads, &item)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *pad = g_value_get_object (&item);
+
+ if (gst_pad_get_direction (pad) == GST_PAD_SRC)
+ havesrc = TRUE;
+ else if (gst_pad_get_direction (pad) == GST_PAD_SINK)
+ havesink = TRUE;
+
+ g_value_reset (&item);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ havesrc = FALSE;
+ havesink = FALSE;
+ break;
+ default:
+ /* ERROR and DONE */
+ done = TRUE;
+ break;
+ }
+ }
+
+ g_value_unset (&item);
+ gst_iterator_free (pads);
+
+ /* just look at the element's class, not the factory, since there might
+ * not be a factory (in case of python elements) or the factory is the
+ * wrong one (in case of a GstBin sub-class) and doesn't have complete
+ * information. */
+ {
+ GList *tmp =
+ gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS
+ (element));
+
+ while (tmp) {
+ GstPadTemplate *template = (GstPadTemplate *) tmp->data;
+
+ if (template->direction == GST_PAD_SRC)
+ havesrc = TRUE;
+ else if (template->direction == GST_PAD_SINK) {
+ if (!havesink && (template->presence == GST_PAD_REQUEST) && isdynamic)
+ *isdynamic = TRUE;
+ havesink = TRUE;
+ }
+ tmp = tmp->next;
+ }
+ }
+ return (havesink && havesrc);
+}
+
+/*
+ * get_src_pad:
+ * element: a #GstElement
+ *
+ * Returns: The src pad for the given element. A reference was added to the
+ * returned pad, remove it when you don't need that pad anymore.
+ * Returns NULL if there's no source pad.
+ */
+
+static GstPad *
+get_src_pad (GstElement * element)
+{
+ GstIterator *it;
+ GstIteratorResult itres;
+ GValue item = { 0, };
+ GstPad *srcpad = NULL;
+
+ it = gst_element_iterate_src_pads (element);
+ itres = gst_iterator_next (it, &item);
+ if (itres != GST_ITERATOR_OK) {
+ GST_DEBUG ("%s doesn't have a src pad !", GST_ELEMENT_NAME (element));
+ } else {
+ srcpad = g_value_get_object (&item);
+ gst_object_ref (srcpad);
+ }
+ g_value_reset (&item);
+ gst_iterator_free (it);
+
+ return srcpad;
+}
+
+/* get_nb_static_sinks:
+ *
+ * Returns : The number of static sink pads of the controlled element.
+ */
+static guint
+get_nb_static_sinks (NleOperation * oper)
+{
+ GstIterator *sinkpads;
+ gboolean done = FALSE;
+ guint nbsinks = 0;
+ GValue item = { 0, };
+
+ sinkpads = gst_element_iterate_sink_pads (oper->element);
+
+ while (!done) {
+ switch (gst_iterator_next (sinkpads, &item)) {
+ case GST_ITERATOR_OK:{
+ nbsinks++;
+ g_value_unset (&item);
+ }
+ break;
+ case GST_ITERATOR_RESYNC:
+ nbsinks = 0;
+ gst_iterator_resync (sinkpads);
+ break;
+ default:
+ /* ERROR and DONE */
+ done = TRUE;
+ break;
+ }
+ }
+
+ g_value_reset (&item);
+ gst_iterator_free (sinkpads);
+
+ GST_DEBUG ("We found %d static sinks", nbsinks);
+
+ return nbsinks;
+}
+
+static gboolean
+nle_operation_add_element (GstBin * bin, GstElement * element)
+{
+ NleOperation *operation = (NleOperation *) bin;
+ gboolean res = FALSE;
+ gboolean isdynamic;
+
+ GST_DEBUG_OBJECT (bin, "element:%s", GST_ELEMENT_NAME (element));
+
+ if (operation->element) {
+ GST_WARNING_OBJECT (operation,
+ "We already control an element : %s , remove it first",
+ GST_OBJECT_NAME (operation->element));
+ } else {
+ if (!element_is_valid_filter (element, &isdynamic)) {
+ GST_WARNING_OBJECT (operation,
+ "Element %s is not a valid filter element",
+ GST_ELEMENT_NAME (element));
+ } else {
+ if ((res = GST_BIN_CLASS (parent_class)->add_element (bin, element))) {
+ GstPad *srcpad;
+
+ srcpad = get_src_pad (element);
+ if (!srcpad)
+ return FALSE;
+
+ operation->element = element;
+ operation->dynamicsinks = isdynamic;
+
+ nle_object_ghost_pad_set_target (NLE_OBJECT (operation),
+ NLE_OBJECT (operation)->srcpad, srcpad);
+
+ /* Remove the reference get_src_pad gave us */
+ gst_object_unref (srcpad);
+
+ /* Figure out number of static sink pads */
+ operation->num_sinks = get_nb_static_sinks (operation);
+
+ /* Finally sync the ghostpads with the real pads */
+ synchronize_sinks (operation);
+ }
+ }
+ }
+
+ return res;
+}
+
+static gboolean
+nle_operation_remove_element (GstBin * bin, GstElement * element)
+{
+ NleOperation *operation = (NleOperation *) bin;
+ gboolean res = FALSE;
+
+ if (operation->element) {
+ if ((res = GST_BIN_CLASS (parent_class)->remove_element (bin, element)))
+ operation->element = NULL;
+ } else {
+ GST_WARNING_OBJECT (bin,
+ "Element %s is not the one controlled by this operation",
+ GST_ELEMENT_NAME (element));
+ }
+ return res;
+}
+
+static void
+nle_operation_set_sinks (NleOperation * operation, guint sinks)
+{
+ /* FIXME : Check if sinkpad of element is on-demand .... */
+
+ operation->num_sinks = sinks;
+ synchronize_sinks (operation);
+}
+
+static void
+nle_operation_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ NleOperation *operation = (NleOperation *) object;
+
+ switch (prop_id) {
+ case ARG_SINKS:
+ nle_operation_set_sinks (operation, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nle_operation_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ NleOperation *operation = (NleOperation *) object;
+
+ switch (prop_id) {
+ case ARG_SINKS:
+ g_value_set_int (value, operation->num_sinks);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+/*
+ * Returns the first unused sink pad of the controlled element.
+ * Only use with static element. Unref after usage.
+ * Returns NULL if there's no more unused sink pads.
+ */
+static GstPad *
+get_unused_static_sink_pad (NleOperation * operation)
+{
+ GstIterator *pads;
+ gboolean done = FALSE;
+ GValue item = { 0, };
+ GstPad *ret = NULL;
+
+ if (!operation->element)
+ return NULL;
+
+ pads = gst_element_iterate_pads (operation->element);
+
+ while (!done) {
+ switch (gst_iterator_next (pads, &item)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *pad = g_value_get_object (&item);
+
+ if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
+ GList *tmp;
+ gboolean istaken = FALSE;
+
+ /* 1. figure out if one of our sink ghostpads has this pad as target */
+ for (tmp = operation->sinks; tmp; tmp = tmp->next) {
+ GstGhostPad *gpad = (GstGhostPad *) tmp->data;
+ GstPad *target = gst_ghost_pad_get_target (gpad);
+
+ GST_LOG ("found ghostpad with target %s:%s",
+ GST_DEBUG_PAD_NAME (target));
+
+ if (target) {
+ if (target == pad)
+ istaken = TRUE;
+ gst_object_unref (target);
+ }
+ }
+
+ /* 2. if not taken, return that pad */
+ if (!istaken) {
+ gst_object_ref (pad);
+ ret = pad;
+ done = TRUE;
+ }
+ }
+ g_value_reset (&item);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ default:
+ /* ERROR and DONE */
+ done = TRUE;
+ break;
+ }
+ }
+
+ g_value_unset (&item);
+ gst_iterator_free (pads);
+
+ if (ret)
+ GST_DEBUG_OBJECT (operation, "found free sink pad %s:%s",
+ GST_DEBUG_PAD_NAME (ret));
+ else
+ GST_DEBUG_OBJECT (operation, "Couldn't find an unused sink pad");
+
+ return ret;
+}
+
+GstPad *
+get_unlinked_sink_ghost_pad (NleOperation * operation)
+{
+ GstIterator *pads;
+ gboolean done = FALSE;
+ GValue item = { 0, };
+ GstPad *ret = NULL;
+
+ if (!operation->element)
+ return NULL;
+
+ pads = gst_element_iterate_sink_pads ((GstElement *) operation);
+
+ while (!done) {
+ switch (gst_iterator_next (pads, &item)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *pad = g_value_get_object (&item);
+ GstPad *peer = gst_pad_get_peer (pad);
+
+ if (peer == NULL) {
+ ret = pad;
+ gst_object_ref (ret);
+ done = TRUE;
+ } else {
+ gst_object_unref (peer);
+ }
+ g_value_reset (&item);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ default:
+ /* ERROR and DONE */
+ done = TRUE;
+ break;
+ }
+ }
+
+ g_value_unset (&item);
+ gst_iterator_free (pads);
+
+ if (ret)
+ GST_DEBUG_OBJECT (operation, "found unlinked ghost sink pad %s:%s",
+ GST_DEBUG_PAD_NAME (ret));
+ else
+ GST_DEBUG_OBJECT (operation, "Couldn't find an unlinked ghost sink pad");
+
+ return ret;
+
+}
+
+static GstPad *
+get_request_sink_pad (NleOperation * operation)
+{
+ GstPad *pad = NULL;
+ GList *templates;
+
+ if (!operation->element)
+ return NULL;
+
+ templates = gst_element_class_get_pad_template_list
+ (GST_ELEMENT_GET_CLASS (operation->element));
+
+ for (; templates; templates = templates->next) {
+ GstPadTemplate *templ = (GstPadTemplate *) templates->data;
+
+ GST_LOG_OBJECT (operation->element, "Trying template %s",
+ GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
+
+ if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SINK) &&
+ (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST)) {
+ pad =
+ gst_element_get_request_pad (operation->element,
+ GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
+ if (pad)
+ break;
+ }
+ }
+
+ return pad;
+}
+
+static GstPad *
+add_sink_pad (NleOperation * operation)
+{
+ GstPad *gpad = NULL;
+ GstPad *ret = NULL;
+
+ if (!operation->element)
+ return NULL;
+
+ /* FIXME : implement */
+ GST_LOG_OBJECT (operation, "element:%s , dynamicsinks:%d",
+ GST_ELEMENT_NAME (operation->element), operation->dynamicsinks);
+
+
+ if (!operation->dynamicsinks) {
+ /* static sink pads */
+ ret = get_unused_static_sink_pad (operation);
+ if (ret) {
+ gpad = nle_object_ghost_pad ((NleObject *) operation, GST_PAD_NAME (ret),
+ ret);
+ gst_object_unref (ret);
+ }
+ }
+
+ if (!gpad) {
+ /* request sink pads */
+ ret = get_request_sink_pad (operation);
+ if (ret) {
+ gpad = nle_object_ghost_pad ((NleObject *) operation, GST_PAD_NAME (ret),
+ ret);
+ gst_object_unref (ret);
+ }
+ }
+
+ if (gpad) {
+ operation->sinks = g_list_append (operation->sinks, gpad);
+ operation->realsinks++;
+ GST_DEBUG ("Created new pad %s:%s ghosting %s:%s",
+ GST_DEBUG_PAD_NAME (gpad), GST_DEBUG_PAD_NAME (ret));
+ } else {
+ GST_WARNING ("Couldn't find a usable sink pad!");
+ }
+
+ return gpad;
+}
+
+static gboolean
+remove_sink_pad (NleOperation * operation, GstPad * sinkpad)
+{
+ gboolean ret = TRUE;
+ gboolean need_unref = FALSE;
+
+ GST_DEBUG ("sinkpad %s:%s", GST_DEBUG_PAD_NAME (sinkpad));
+
+ /*
+ We can't remove any random pad.
+ We should remove an unused pad ... which is hard to figure out in a
+ thread-safe way.
+ */
+
+ if ((sinkpad == NULL) && operation->dynamicsinks) {
+ /* Find an unlinked sinkpad */
+ if ((sinkpad = get_unlinked_sink_ghost_pad (operation)) == NULL) {
+ ret = FALSE;
+ goto beach;
+ }
+ need_unref = TRUE;
+ }
+
+ if (sinkpad) {
+ GstPad *target = gst_ghost_pad_get_target ((GstGhostPad *) sinkpad);
+
+ if (target) {
+ /* release the target pad */
+ nle_object_ghost_pad_set_target ((NleObject *) operation, sinkpad, NULL);
+ if (operation->dynamicsinks)
+ gst_element_release_request_pad (operation->element, target);
+ gst_object_unref (target);
+ }
+ operation->sinks = g_list_remove (operation->sinks, sinkpad);
+ nle_object_remove_ghost_pad ((NleObject *) operation, sinkpad);
+ if (need_unref)
+ gst_object_unref (sinkpad);
+ operation->realsinks--;
+ }
+
+beach:
+ return ret;
+}
+
+static void
+synchronize_sinks (NleOperation * operation)
+{
+
+ GST_DEBUG_OBJECT (operation, "num_sinks:%d , realsinks:%d, dynamicsinks:%d",
+ operation->num_sinks, operation->realsinks, operation->dynamicsinks);
+
+ if (operation->num_sinks == operation->realsinks)
+ return;
+
+ if (operation->num_sinks > operation->realsinks) {
+ while (operation->num_sinks > operation->realsinks) /* Add pad */
+ if (!(add_sink_pad (operation))) {
+ break;
+ }
+ } else {
+ /* Remove pad */
+ /* FIXME, which one do we remove ? :) */
+ while (operation->num_sinks < operation->realsinks)
+ if (!remove_sink_pad (operation, NULL))
+ break;
+ }
+}
+
+static gboolean
+nle_operation_prepare (NleObject * object)
+{
+ /* Prepare the pads */
+ synchronize_sinks ((NleOperation *) object);
+
+ return TRUE;
+}
+
+static gboolean
+nle_operation_cleanup (NleObject * object)
+{
+ NleOperation *oper = (NleOperation *) object;
+
+ if (oper->dynamicsinks) {
+ GST_DEBUG ("Resetting dynamic sinks");
+ nle_operation_set_sinks (oper, 0);
+ }
+
+ return TRUE;
+}
+
+void
+nle_operation_hard_cleanup (NleOperation * operation)
+{
+ gboolean done = FALSE;
+
+ GValue item = { 0, };
+ GstIterator *pads;
+
+ GST_INFO_OBJECT (operation, "Hard reset of the operation");
+
+ pads = gst_element_iterate_sink_pads (GST_ELEMENT (operation));
+ while (!done) {
+ switch (gst_iterator_next (pads, &item)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *sinkpad = g_value_get_object (&item);
+ GstPad *srcpad = gst_pad_get_peer (sinkpad);
+
+ if (srcpad) {
+ GST_ERROR ("Unlinking %" GST_PTR_FORMAT " and %"
+ GST_PTR_FORMAT, srcpad, sinkpad);
+ gst_pad_unlink (srcpad, sinkpad);
+ gst_object_unref (srcpad);
+ }
+
+ g_value_reset (&item);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ default:
+ /* ERROR and DONE */
+ done = TRUE;
+ break;
+ }
+ }
+ nle_object_cleanup (NLE_OBJECT (operation));
+
+ gst_iterator_free (pads);
+}
+
+
+static GstPad *
+nle_operation_request_new_pad (GstElement * element, GstPadTemplate * templ,
+ const gchar * name, const GstCaps * caps)
+{
+ NleOperation *operation = (NleOperation *) element;
+ GstPad *ret;
+
+ GST_DEBUG ("template:%s name:%s", templ->name_template, name);
+
+ if (operation->num_sinks == operation->realsinks) {
+ GST_WARNING_OBJECT (element,
+ "We already have the maximum number of pads : %d",
+ operation->num_sinks);
+ return NULL;
+ }
+
+ ret = add_sink_pad ((NleOperation *) element);
+
+ return ret;
+}
+
+static void
+nle_operation_release_pad (GstElement * element, GstPad * pad)
+{
+ GST_DEBUG ("pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ remove_sink_pad ((NleOperation *) element, pad);
+}
+
+void
+nle_operation_signal_input_priority_changed (NleOperation * operation,
+ GstPad * pad, guint32 priority)
+{
+ GST_DEBUG_OBJECT (operation, "pad:%s:%s, priority:%d",
+ GST_DEBUG_PAD_NAME (pad), priority);
+ g_signal_emit (operation, nle_operation_signals[INPUT_PRIORITY_CHANGED],
+ 0, pad, priority);
+}
+
+void
+nle_operation_update_base_time (NleOperation * operation,
+ GstClockTime timestamp)
+{
+ if (!nle_object_to_media_time (NLE_OBJECT (operation),
+ timestamp, &operation->next_base_time)) {
+ GST_WARNING_OBJECT (operation, "Trying to set a basetime outside of "
+ "ourself");
+
+ return;
+ }
+
+ GST_INFO_OBJECT (operation, "Setting next_basetime to %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (operation->next_base_time));
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@chello.be>
+ * 2004 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nleoperation.h: Header for base NleOperation
+ *
+ * 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 __NLE_OPERATION_H__
+#define __NLE_OPERATION_H__
+
+#include <gst/gst.h>
+#include "nleobject.h"
+
+G_BEGIN_DECLS
+#define NLE_TYPE_OPERATION \
+ (nle_operation_get_type())
+#define NLE_OPERATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_OPERATION,NleOperation))
+#define NLE_OPERATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_OPERATION,NleOperationClass))
+#define NLE_IS_OPERATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_OPERATION))
+#define NLE_IS_OPERATION_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_OPERATION))
+ struct _NleOperation
+{
+ NleObject parent;
+
+ /* <private> */
+
+ /* num_sinks:
+ * Number of sink inputs of the controlled element.
+ * -1 if the sink pads are dynamic */
+ gint num_sinks;
+
+ /* TRUE if element has request pads */
+ gboolean dynamicsinks;
+
+ /* realsinks:
+ * Number of sink pads currently used on the contolled element. */
+ gint realsinks;
+
+ /* FIXME : We might need to use a lock to access this list */
+ GList * sinks; /* The sink ghostpads */
+
+ GstElement *element; /* controlled element */
+
+ GstClockTime next_base_time;
+};
+
+struct _NleOperationClass
+{
+ NleObjectClass parent_class;
+
+ void (*input_priority_changed) (NleOperation * operation, GstPad *pad, guint32 priority);
+};
+
+GstPad * get_unlinked_sink_ghost_pad (NleOperation * operation) G_GNUC_INTERNAL;
+
+void
+nle_operation_signal_input_priority_changed(NleOperation * operation, GstPad *pad,
+ guint32 priority) G_GNUC_INTERNAL;
+
+void nle_operation_update_base_time (NleOperation *operation,
+ GstClockTime timestamp) G_GNUC_INTERNAL;
+
+void nle_operation_hard_cleanup (NleOperation *operation) G_GNUC_INTERNAL;
+
+
+/* normal GOperation stuff */
+GType nle_operation_get_type (void) G_GNUC_INTERNAL;
+
+G_END_DECLS
+#endif /* __NLE_OPERATION_H__ */
--- /dev/null
+/* Gnonlin
+ * Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
+ * <2004-2008> Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "nle.h"
+
+/**
+ * SECTION:element-nlesource
+ *
+ * The NleSource encapsulates a pipeline which produces data for processing
+ * in a #NleComposition.
+ */
+
+static GstStaticPadTemplate nle_source_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (nlesource);
+#define GST_CAT_DEFAULT nlesource
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (nlesource, "nlesource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Source Element");
+#define nle_source_parent_class parent_class
+struct _NleSourcePrivate
+{
+ gboolean dispose_has_run;
+
+ gboolean dynamicpads; /* TRUE if the controlled element has dynamic pads */
+
+ gulong padremovedid; /* signal handler for element pad-removed signal */
+ gulong padaddedid; /* signal handler for element pad-added signal */
+
+ gboolean pendingblock; /* We have a pending pad_block */
+ gboolean areblocked; /* We already got blocked */
+ GstPad *ghostedpad; /* Pad (to be) ghosted */
+ GstPad *staticpad; /* The only pad. We keep an extra ref */
+
+ GMutex seek_lock;
+ GstEvent *seek_event;
+ gulong probeid;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NleSource, nle_source, NLE_TYPE_OBJECT,
+ G_ADD_PRIVATE (NleSource)
+ _do_init);
+
+
+static gboolean nle_source_prepare (NleObject * object);
+static gboolean nle_source_send_event (GstElement * element, GstEvent * event);
+static gboolean nle_source_add_element (GstBin * bin, GstElement * element);
+static gboolean nle_source_remove_element (GstBin * bin, GstElement * element);
+static void nle_source_dispose (GObject * object);
+
+static gboolean
+nle_source_control_element_func (NleSource * source, GstElement * element);
+static GstStateChangeReturn nle_source_change_state (GstElement * element,
+ GstStateChange transition);
+
+static void
+nle_source_class_init (NleSourceClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBinClass *gstbin_class;
+ NleObjectClass *nleobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbin_class = (GstBinClass *) klass;
+ nleobject_class = (NleObjectClass *) klass;
+
+ gst_element_class_set_static_metadata (gstelement_class, "GNonLin Source",
+ "Filter/Editor",
+ "Manages source elements",
+ "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>");
+
+ gstelement_class->send_event = GST_DEBUG_FUNCPTR (nle_source_send_event);
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_source_change_state);
+
+ parent_class = g_type_class_ref (NLE_TYPE_OBJECT);
+
+ klass->control_element = GST_DEBUG_FUNCPTR (nle_source_control_element_func);
+
+ nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_source_prepare);
+
+ gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_source_add_element);
+ gstbin_class->remove_element = GST_DEBUG_FUNCPTR (nle_source_remove_element);
+
+ gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_source_dispose);
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &nle_source_src_template);
+
+}
+
+
+static void
+nle_source_init (NleSource * source)
+{
+ GST_OBJECT_FLAG_SET (source, NLE_OBJECT_SOURCE);
+ source->element = NULL;
+ source->priv = nle_source_get_instance_private (source);
+ g_mutex_init (&source->priv->seek_lock);
+
+ GST_DEBUG_OBJECT (source, "Setting GstBin async-handling to TRUE");
+ g_object_set (G_OBJECT (source), "async-handling", TRUE, NULL);
+}
+
+static void
+nle_source_dispose (GObject * object)
+{
+ NleObject *nleobject = (NleObject *) object;
+ NleSource *source = (NleSource *) object;
+ NleSourcePrivate *priv = source->priv;
+
+ GST_DEBUG_OBJECT (object, "dispose");
+
+ if (priv->dispose_has_run)
+ return;
+
+ GST_OBJECT_LOCK (object);
+ if (priv->probeid) {
+ GST_DEBUG_OBJECT (source, "Removing blocking probe! %lu", priv->probeid);
+ priv->areblocked = FALSE;
+ gst_pad_remove_probe (priv->ghostedpad, priv->probeid);
+ priv->probeid = 0;
+ }
+ GST_OBJECT_UNLOCK (object);
+
+
+ if (source->element) {
+ gst_object_unref (source->element);
+ source->element = NULL;
+ }
+
+ priv->dispose_has_run = TRUE;
+ if (priv->ghostedpad)
+ nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, NULL);
+
+ if (priv->staticpad) {
+ gst_object_unref (priv->staticpad);
+ priv->staticpad = NULL;
+ }
+
+ g_mutex_lock (&priv->seek_lock);
+ if (priv->seek_event) {
+ gst_event_unref (priv->seek_event);
+ priv->seek_event = NULL;
+ }
+ g_mutex_unlock (&priv->seek_lock);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+element_pad_added_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
+ NleSource * source)
+{
+ GstCaps *srccaps;
+ NleSourcePrivate *priv = source->priv;
+ NleObject *nleobject = (NleObject *) source;
+
+ GST_DEBUG_OBJECT (source, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ if (priv->ghostedpad) {
+ GST_DEBUG_OBJECT (source,
+ "We already have a target, not doing anything with %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+
+ return;
+ }
+
+ /* FIXME: pass filter caps to query_caps directly */
+ srccaps = gst_pad_query_caps (pad, NULL);
+ if (nleobject->caps && !gst_caps_can_intersect (srccaps, nleobject->caps)) {
+ gst_caps_unref (srccaps);
+ GST_DEBUG_OBJECT (source, "Pad doesn't have valid caps, ignoring");
+ return;
+ }
+ gst_caps_unref (srccaps);
+
+ priv->ghostedpad = pad;
+ GST_DEBUG_OBJECT (nleobject, "SET target %" GST_PTR_FORMAT, pad);
+ nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, pad);
+
+ GST_DEBUG_OBJECT (source, "Using pad pad %s:%s as a target now!",
+ GST_DEBUG_PAD_NAME (pad));
+}
+
+static void
+element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
+ NleSource * source)
+{
+ NleSourcePrivate *priv = source->priv;
+ NleObject *nleobject = (NleObject *) source;
+
+ GST_DEBUG_OBJECT (source, "pad %s:%s (controlled pad %s:%s)",
+ GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->ghostedpad));
+
+ if (pad == priv->ghostedpad) {
+ GST_DEBUG_OBJECT (source,
+ "The removed pad is the controlled pad, clearing up");
+
+ GST_DEBUG_OBJECT (source, "Clearing up ghostpad");
+
+ if (nleobject->srcpad)
+ nle_object_ghost_pad_set_target (NLE_OBJECT (source), nleobject->srcpad,
+ NULL);
+ priv->ghostedpad = NULL;
+ } else {
+ GST_DEBUG_OBJECT (source, "The removed pad is NOT our controlled pad");
+ }
+}
+
+static gint
+compare_src_pad (GValue * item, GstCaps * caps)
+{
+ gint ret = 1;
+ GstPad *pad = g_value_get_object (item);
+ GstCaps *padcaps;
+
+ GST_DEBUG_OBJECT (pad, "Trying pad for caps %" GST_PTR_FORMAT, caps);
+
+ /* FIXME: can pass the filter caps right away.. */
+ padcaps = gst_pad_query_caps (pad, NULL);
+
+ if (gst_caps_can_intersect (padcaps, caps))
+ ret = 0;
+
+ gst_caps_unref (padcaps);
+
+ return ret;
+}
+
+/*
+ get_valid_src_pad
+
+ Returns True if there's a src pad compatible with the NleObject caps in the
+ given element. Fills in pad if so. The returned pad has an incremented refcount
+*/
+
+static gboolean
+get_valid_src_pad (NleSource * source, GstElement * element, GstPad ** pad)
+{
+ gboolean res = FALSE;
+ GstIterator *srcpads;
+ GValue item = { 0, };
+
+ g_return_val_if_fail (pad, FALSE);
+
+ srcpads = gst_element_iterate_src_pads (element);
+ if (gst_iterator_find_custom (srcpads, (GCompareFunc) compare_src_pad, &item,
+ NLE_OBJECT (source)->caps)) {
+ *pad = g_value_get_object (&item);
+ gst_object_ref (*pad);
+ g_value_reset (&item);
+ res = TRUE;
+ }
+ gst_iterator_free (srcpads);
+
+ return res;
+}
+
+/*
+ * has_dynamic_pads
+ * Returns TRUE if the element has only dynamic pads.
+ */
+
+static gboolean
+has_dynamic_srcpads (GstElement * element)
+{
+ gboolean ret = TRUE;
+ GList *templates;
+ GstPadTemplate *template;
+
+ templates =
+ gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (element));
+
+ while (templates) {
+ template = (GstPadTemplate *) templates->data;
+
+ if ((GST_PAD_TEMPLATE_DIRECTION (template) == GST_PAD_SRC)
+ && (GST_PAD_TEMPLATE_PRESENCE (template) == GST_PAD_ALWAYS)) {
+ ret = FALSE;
+ break;
+ }
+
+ templates = templates->next;
+ }
+
+ return ret;
+}
+
+static gboolean
+nle_source_control_element_func (NleSource * source, GstElement * element)
+{
+ NleSourcePrivate *priv = source->priv;
+ GstPad *pad = NULL;
+
+ g_return_val_if_fail (source->element == NULL, FALSE);
+
+ GST_DEBUG_OBJECT (source, "element:%s, source->element:%p",
+ GST_ELEMENT_NAME (element), source->element);
+
+ source->element = element;
+ gst_object_ref (element);
+
+ if (get_valid_src_pad (source, source->element, &pad)) {
+ priv->staticpad = pad;
+ nle_object_ghost_pad_set_target (NLE_OBJECT (source),
+ NLE_OBJECT_SRC (source), pad);
+ priv->dynamicpads = FALSE;
+ } else {
+ priv->dynamicpads = has_dynamic_srcpads (element);
+ GST_DEBUG_OBJECT (source, "No valid source pad yet, dynamicpads:%d",
+ priv->dynamicpads);
+ if (priv->dynamicpads) {
+ /* connect to pad-added/removed signals */
+ priv->padremovedid = g_signal_connect
+ (G_OBJECT (element), "pad-removed",
+ G_CALLBACK (element_pad_removed_cb), source);
+ priv->padaddedid =
+ g_signal_connect (G_OBJECT (element), "pad-added",
+ G_CALLBACK (element_pad_added_cb), source);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+nle_source_add_element (GstBin * bin, GstElement * element)
+{
+ NleSource *source = (NleSource *) bin;
+ gboolean pret;
+
+ GST_DEBUG_OBJECT (source, "Adding element %s", GST_ELEMENT_NAME (element));
+
+ if (source->element) {
+ GST_WARNING_OBJECT (bin, "NleSource can only handle one element at a time");
+ return FALSE;
+ }
+
+ /* call parent add_element */
+ pret = GST_BIN_CLASS (parent_class)->add_element (bin, element);
+
+ if (pret) {
+ nle_source_control_element_func (source, element);
+ }
+ return pret;
+}
+
+static gboolean
+nle_source_remove_element (GstBin * bin, GstElement * element)
+{
+ NleSource *source = (NleSource *) bin;
+ NleObject *nleobject = (NleObject *) element;
+ NleSourcePrivate *priv = source->priv;
+ gboolean pret;
+
+ GST_DEBUG_OBJECT (source, "Removing element %s", GST_ELEMENT_NAME (element));
+
+ /* try to remove it */
+ pret = GST_BIN_CLASS (parent_class)->remove_element (bin, element);
+
+ if ((!source->element) || (source->element != element)) {
+ return TRUE;
+ }
+
+ if (pret) {
+ nle_object_ghost_pad_set_target (NLE_OBJECT (source), nleobject->srcpad,
+ NULL);
+
+ /* remove signal handlers */
+ if (priv->padremovedid) {
+ g_signal_handler_disconnect (source->element, priv->padremovedid);
+ priv->padremovedid = 0;
+ }
+ if (priv->padaddedid) {
+ g_signal_handler_disconnect (source->element, priv->padaddedid);
+ priv->padaddedid = 0;
+ }
+
+ priv->dynamicpads = FALSE;
+ gst_object_unref (element);
+ source->element = NULL;
+ }
+ return pret;
+}
+
+static gboolean
+nle_source_send_event (GstElement * element, GstEvent * event)
+{
+ gboolean res = TRUE;
+ NleSource *source = (NleSource *) element;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ g_mutex_lock (&source->priv->seek_lock);
+ source->priv->seek_event = event;
+ g_mutex_unlock (&source->priv->seek_lock);
+ break;
+ default:
+ res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
+ break;
+ }
+
+ return res;
+}
+
+static void
+ghost_seek_pad (GstElement * source, gpointer user_data)
+{
+ NleSourcePrivate *priv = NLE_SOURCE (source)->priv;
+
+ g_mutex_lock (&priv->seek_lock);
+ if (priv->seek_event) {
+ GstEvent *seek_event = priv->seek_event;
+ priv->seek_event = NULL;
+
+ if (!(gst_pad_send_event (priv->ghostedpad, seek_event)))
+ GST_ELEMENT_ERROR (source, RESOURCE, SEEK,
+ (NULL), ("Sending initial seek to upstream element failed"));
+ }
+ g_mutex_unlock (&priv->seek_lock);
+
+ GST_OBJECT_LOCK (source);
+ if (priv->probeid) {
+ GST_DEBUG_OBJECT (source, "Removing blocking probe! %lu", priv->probeid);
+ priv->areblocked = FALSE;
+ gst_pad_remove_probe (priv->ghostedpad, priv->probeid);
+ priv->probeid = 0;
+ }
+ GST_OBJECT_UNLOCK (source);
+}
+
+static GstPadProbeReturn
+pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, NleSource * source)
+{
+ GST_OBJECT_LOCK (source);
+ if (!source->priv->areblocked) {
+ GST_INFO_OBJECT (pad, "Blocked now, launching seek");
+ gst_element_call_async (GST_ELEMENT (source), ghost_seek_pad, NULL, NULL);
+ source->priv->areblocked = TRUE;
+ }
+ GST_OBJECT_UNLOCK (source);
+
+ return GST_PAD_PROBE_OK;
+}
+
+static gboolean
+nle_source_prepare (NleObject * object)
+{
+ GstPad *pad;
+ NleSource *source = NLE_SOURCE (object);
+ NleSourcePrivate *priv = source->priv;
+ GstElement *parent =
+ (GstElement *) gst_element_get_parent ((GstElement *) object);
+
+ if (!source->element) {
+ GST_WARNING_OBJECT (source,
+ "NleSource doesn't have an element to control !");
+ if (parent)
+ gst_object_unref (parent);
+ return FALSE;
+ }
+
+ if (object->in_composition == FALSE) {
+ gst_element_send_event (GST_ELEMENT_CAST (parent),
+ gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, object->start, GST_SEEK_TYPE_SET, object->stop));
+ }
+
+ GST_LOG_OBJECT (source, "srcpad:%p, dynamicpads:%d",
+ object->srcpad, priv->dynamicpads);
+
+ if (!priv->staticpad && !(get_valid_src_pad (source, source->element, &pad))) {
+ GST_DEBUG_OBJECT (source, "Couldn't find a valid source pad");
+ gst_object_unref (parent);
+ return FALSE;
+ } else {
+ if (priv->staticpad)
+ pad = gst_object_ref (priv->staticpad);
+ priv->ghostedpad = pad;
+ GST_OBJECT_LOCK (source);
+ priv->probeid = gst_pad_add_probe (pad,
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ (GstPadProbeCallback) pad_blocked_cb, source, NULL);
+ GST_OBJECT_UNLOCK (source);
+ gst_object_unref (pad);
+ }
+
+ gst_object_unref (parent);
+
+ return TRUE;
+}
+
+static GstStateChangeReturn
+nle_source_change_state (GstElement * element, GstStateChange transition)
+{
+ NleSourcePrivate *priv = NLE_SOURCE (element)->priv;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ g_mutex_lock (&priv->seek_lock);
+ gst_clear_event (&priv->seek_event);
+ g_mutex_unlock (&priv->seek_lock);
+ break;
+ default:
+ break;
+ }
+
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
+ * 2004-2008 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nlesource.h: Header for base NleSource
+ *
+ * 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 __NLE_SOURCE_H__
+#define __NLE_SOURCE_H__
+
+#include <gst/gst.h>
+#include "nleobject.h"
+
+G_BEGIN_DECLS
+#define NLE_TYPE_SOURCE \
+ (nle_source_get_type())
+#define NLE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_SOURCE,NleSource))
+#define NLE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_SOURCE,NleSourceClass))
+#define NLE_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NLE_TYPE_SOURCE, NleSourceClass))
+#define NLE_IS_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_SOURCE))
+#define NLE_IS_SOURCE_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_SOURCE))
+typedef struct _NleSourcePrivate NleSourcePrivate;
+
+struct _NleSource
+{
+ NleObject parent;
+
+ /* controlled source element, acces with gst_bin_[add|remove]_element */
+ GstElement *element;
+
+ NleSourcePrivate *priv;
+};
+
+struct _NleSourceClass
+{
+ NleObjectClass parent_class;
+
+ /* control_element() takes care of controlling the given element */
+ gboolean (*control_element) (NleSource * source, GstElement * element);
+};
+
+GType nle_source_get_type (void) G_GNUC_INTERNAL;
+
+G_END_DECLS
+#endif /* __NLE_SOURCE_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nletypes.h: Header for class definition
+ *
+ * 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 __NLE_TYPES_H__
+#define __NLE_TYPES_H__
+
+#include <glib.h>
+
+typedef struct _NleObject NleObject;
+typedef struct _NleObjectClass NleObjectClass;
+
+typedef struct _NleComposition NleComposition;
+typedef struct _NleCompositionClass NleCompositionClass;
+
+typedef struct _NleOperation NleOperation;
+typedef struct _NleOperationClass NleOperationClass;
+
+typedef struct _NleSource NleSource;
+typedef struct _NleSourceClass NleSourceClass;
+
+typedef struct _NleURISource NleURISource;
+typedef struct _NleURISourceClass NleURISourceClass;
+
+#endif
--- /dev/null
+/* Gnonlin
+ * Copyright (C) <2005-2008> Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "nle.h"
+#include "nleurisource.h"
+
+/**
+ * SECTION:element-nleurisource
+ *
+ * NleURISource is a #NleSource which reads and decodes the contents
+ * of a given file. The data in the file is decoded using any available
+ * GStreamer plugins.
+ */
+
+static GstStaticPadTemplate nle_urisource_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (nleurisource);
+#define GST_CAT_DEFAULT nleurisource
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (nleurisource, "nleurisource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin URI Source Element");
+#define nle_urisource_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (NleURISource, nle_urisource, NLE_TYPE_SOURCE,
+ _do_init);
+
+enum
+{
+ ARG_0,
+ ARG_URI,
+};
+
+static gboolean nle_urisource_prepare (NleObject * object);
+
+static void
+nle_urisource_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+
+static void
+nle_urisource_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void
+nle_urisource_class_init (NleURISourceClass * klass)
+{
+ GObjectClass *gobject_class;
+ NleObjectClass *nleobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ nleobject_class = (NleObjectClass *) klass;
+ parent_class = g_type_class_ref (NLE_TYPE_SOURCE);
+
+ gst_element_class_set_static_metadata (gstelement_class, "GNonLin URI Source",
+ "Filter/Editor",
+ "High-level URI Source element", "Edward Hervey <bilboed@bilboed.com>");
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_urisource_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_urisource_get_property);
+
+ g_object_class_install_property (gobject_class, ARG_URI,
+ g_param_spec_string ("uri", "Uri",
+ "Uri of the file to use", NULL, G_PARAM_READWRITE));
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &nle_urisource_src_template);
+
+ nleobject_class->prepare = nle_urisource_prepare;
+}
+
+static void
+pad_added_cb (GstElement * element, GstPad * srcpad, GstPad * ghostpad)
+{
+ gst_ghost_pad_set_target (GST_GHOST_PAD (ghostpad), srcpad);
+}
+
+static void
+nle_urisource_init (NleURISource * urisource)
+{
+ GstElement *bin, *decodebin = NULL;
+ GstPad *ghostpad;
+
+ GST_OBJECT_FLAG_SET (urisource, NLE_OBJECT_SOURCE);
+
+ /* We create a bin with source and decodebin within */
+ urisource->decodebin = decodebin =
+ gst_element_factory_make ("uridecodebin", "internal-uridecodebin");
+ g_object_set (decodebin, "expose-all-streams", FALSE, NULL);
+
+ bin = gst_bin_new ("internal-bin");
+ gst_bin_add (GST_BIN (bin), decodebin);
+
+ ghostpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
+ gst_element_add_pad (bin, ghostpad);
+
+ gst_bin_add (GST_BIN (urisource), bin);
+
+ g_signal_connect (decodebin, "pad-added", G_CALLBACK (pad_added_cb),
+ ghostpad);
+}
+
+static inline void
+nle_urisource_set_uri (NleURISource * fs, const gchar * uri)
+{
+ g_object_set (fs->decodebin, "uri", uri, NULL);
+}
+
+static void
+nle_urisource_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ NleURISource *fs = (NleURISource *) object;
+
+ switch (prop_id) {
+ case ARG_URI:
+ nle_urisource_set_uri (fs, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nle_urisource_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ NleURISource *fs = (NleURISource *) object;
+
+ switch (prop_id) {
+ case ARG_URI:
+ g_object_get_property ((GObject *) fs->decodebin, "uri", value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+}
+
+static gboolean
+nle_urisource_prepare (NleObject * object)
+{
+ NleURISource *fs = (NleURISource *) object;
+
+ GST_DEBUG ("prepare");
+
+ /* Set the caps on uridecodebin */
+ if (!gst_caps_is_any (object->caps)) {
+ GST_DEBUG_OBJECT (object, "Setting uridecodebin caps to %" GST_PTR_FORMAT,
+ object->caps);
+ g_object_set (fs->decodebin, "caps", object->caps, NULL);
+ }
+
+ return NLE_OBJECT_CLASS (parent_class)->prepare (object);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
+ * 2004-2008 Edward Hervey <bilboed@bilboed.com>
+ *
+ * nleurisource.h: Header for NleURISource
+ *
+ * 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 __NLE_URI_SOURCE_H__
+#define __NLE_URI_SOURCE_H__
+
+#include <gst/gst.h>
+#include "nlesource.h"
+
+G_BEGIN_DECLS
+#define NLE_TYPE_URI_SOURCE \
+ (nle_urisource_get_type())
+#define NLE_URI_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_URI_SOURCE,NleURIsource))
+#define NLE_URI_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_URI_SOURCE,NleURIsourceClass))
+#define NLE_IS_URI_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_URI_SOURCE))
+#define NLE_IS_URI_SOURCE_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_URI_SOURCE))
+
+struct _NleURISource
+{
+ NleSource parent;
+
+ gchar *uri;
+ GstElement *decodebin;
+};
+
+struct _NleURISourceClass
+{
+ NleSourceClass parent_class;
+};
+
+GType nle_urisource_get_type (void) G_GNUC_INTERNAL;
+
+G_END_DECLS
+#endif /* __NLE_URI_SOURCE_H__ */
--- /dev/null
+if HAVE_GST_CHECK
+CHECK_SUBDIRS= check
+else
+CHECK_SUBDIRS=
+endif
+
+if BUILD_BENCHMARKS
+BENCHMARKS_SUBDIR=benchmarks
+else
+BENCHMARKS_SUBDIR=
+endif
+
+if HAVE_GST_VALIDATE
+VALIDATE_SUBDIRS= validate
+else
+VALIDATE_SUBDIRS=
+endif
+
+SUBDIRS= $(CHECK_SUBDIRS) $(BENCHMARKS_SUBDIR) $(VALIDATE_SUBDIRS)
+
+DIST_SUBDIRS = check benchmarks validate
+
--- /dev/null
+noinst_PROGRAMS = timeline
+
+AM_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_CFLAGS)
+AM_LDFLAGS = -export-dynamic
+LDADD = $(top_builddir)/ges/libges-@GST_API_VERSION@.la $(GST_PBUTILS_LIBS) $(GST_LIBS)
--- /dev/null
+ges_benchmarks = ['timeline']
+
+foreach b : ges_benchmarks
+ fname = '@0@.c'.format(b)
+ executable('benchmark-' + b, fname,
+ c_args : ges_c_args,
+ include_directories : [configinc],
+ dependencies : ges_dep
+ )
+endforeach
--- /dev/null
+/* Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+
+#include <ges/ges.h>
+
+
+#define NUM_OBJECTS 1000
+
+gint
+main (gint argc, gchar * argv[])
+{
+ guint i;
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESContainer *container;
+ GstClockTime start, start_ripple, end, end_ripple, max_rippling_time = 0,
+ min_rippling_time = GST_CLOCK_TIME_NONE;
+
+ gst_init (&argc, &argv);
+ ges_init ();
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ layer = ges_layer_new ();
+ timeline = ges_timeline_new_audio_video ();
+ ges_timeline_add_layer (timeline, layer);
+
+ start = gst_util_get_timestamp ();
+ container = GES_CONTAINER (ges_layer_add_asset (layer, asset, 0,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+
+ for (i = 1; i < NUM_OBJECTS; i++)
+ ges_layer_add_asset (layer, asset, i * 1000, 0,
+ 1000, GES_TRACK_TYPE_UNKNOWN);
+ end = gst_util_get_timestamp ();
+ g_print ("%" GST_TIME_FORMAT " - adding %d clip to the timeline\n",
+ GST_TIME_ARGS (end - start), i);
+
+ start_ripple = gst_util_get_timestamp ();
+ for (i = 1; i < 501; i++) {
+ start = gst_util_get_timestamp ();
+ ges_container_edit (container, NULL, 0, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, i * 1000);
+ end = gst_util_get_timestamp ();
+ max_rippling_time = MAX (max_rippling_time, end - start);
+ min_rippling_time = MIN (min_rippling_time, end - start);
+ }
+ end_ripple = gst_util_get_timestamp ();
+ g_print ("%" GST_TIME_FORMAT " - rippling %d times, max: %"
+ GST_TIME_FORMAT " min: %" GST_TIME_FORMAT "\n",
+ GST_TIME_ARGS (end_ripple - start_ripple), i - 1,
+ GST_TIME_ARGS (max_rippling_time), GST_TIME_ARGS (min_rippling_time));
+
+
+ min_rippling_time = GST_CLOCK_TIME_NONE;
+ max_rippling_time = 0;
+ ges_layer_set_auto_transition (layer, TRUE);
+ start_ripple = gst_util_get_timestamp ();
+ for (i = 1; i < 501; i++) {
+ start = gst_util_get_timestamp ();
+ ges_container_edit (container, NULL, 0, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, i * 1000);
+ end = gst_util_get_timestamp ();
+ max_rippling_time = MAX (max_rippling_time, end - start);
+ min_rippling_time = MIN (min_rippling_time, end - start);
+ }
+ end_ripple = gst_util_get_timestamp ();
+ g_print ("%" GST_TIME_FORMAT " - rippling %d times, max: %"
+ GST_TIME_FORMAT " min: %" GST_TIME_FORMAT " (with auto-transition on)\n",
+ GST_TIME_ARGS (end_ripple - start_ripple), i - 1,
+ GST_TIME_ARGS (max_rippling_time), GST_TIME_ARGS (min_rippling_time));
+
+ start = gst_util_get_timestamp ();
+ gst_object_unref (timeline);
+ end = gst_util_get_timestamp ();
+ g_print ("%" GST_TIME_FORMAT " - freeing the timeline\n",
+ GST_TIME_ARGS (end - start));
+
+ return 0;
+}
--- /dev/null
+include $(top_srcdir)/common/check.mak
+
+CHECK_REGISTRY = $(top_builddir)/tests/check/test-registry.reg
+TEST_FILES_DIRECTORY = $(top_srcdir)/tests/check/ges
+
+REGISTRY_ENVIRONMENT = \
+ GST_REGISTRY_1_0=$(CHECK_REGISTRY)
+
+# GST_PLUGINS_XYZ_DIR is only set in an uninstalled setup
+AM_TESTS_ENVIRONMENT += \
+ $(REGISTRY_ENVIRONMENT) \
+ GST_PLUGIN_SYSTEM_PATH_1_0= \
+ GST_PLUGIN_PATH_1_0=$(top_builddir)/plugins:$(GST_PLUGINS_BAD_DIR):$(GST_PLUGINS_LIBAV_DIR):$(GST_PLUGINS_UGLY_DIR):$(GST_PLUGINS_GOOD_DIR):$(GST_PLUGINS_BASE_DIR):$(GST_PLUGINS_DIR)
+
+plugindir = $(libdir)/gstreamer-@GST_API_VERSION@
+
+# override to _not_ install the test plugins
+install-pluginLTLIBRARIES:
+
+# the core dumps of some machines have PIDs appended
+CLEANFILES = core.* test-registry.* *.gcno *.gcda
+
+common_cflags=-I$(top_srcdir) $(GST_PLUGINS_BASE_CFLAGS) $(GST_OBJ_CFLAGS) \
+ -DGES_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" \
+ $(GST_CHECK_CFLAGS) $(GST_OPTION_CFLAGS) $(GST_CFLAGS)
+common_ldadd=$(top_builddir)/ges/libges-@GST_API_VERSION@.la \
+ $(GST_PLUGINS_BASE_LIBS) -lgstpbutils-$(GST_API_VERSION) \
+ $(GST_OBJ_LIBS) $(GST_CHECK_LIBS)
+
+testutils_noisnt_libraries=libtestutils.la
+testutils_noinst_headers=ges/test-utils.h nle/common.h
+libtestutils_la_LIBADD=$(common_ldadd)
+libtestutils_la_CFLAGS=$(common_cflags)
+libtestutils_la_SOURCES=ges/test-utils.c nle/common.c
+
+SUPPRESSIONS = $(top_srcdir)/common/gst.supp # $(srcdir)/gst-plugins-bad.supp
+
+
+clean-local: clean-local-check
+
+check_PROGRAMS = \
+ ges/asset \
+ ges/backgroundsource\
+ ges/basic \
+ ges/layer \
+ ges/effects \
+ ges/uriclip \
+ ges/clip \
+ ges/timelineedition \
+ ges/titles\
+ ges/transition \
+ ges/overlays\
+ ges/mixers\
+ ges/group\
+ ges/project\
+ ges/track\
+ ges/tempochange \
+ ges/negative\
+ nle/simple \
+ nle/complex \
+ nle/nleoperation \
+ nle/nlecomposition \
+ nle/tempochange
+
+# We do not support sources outside composition
+# nle/nlesource
+
+noinst_LTLIBRARIES=$(testutils_noisnt_libraries)
+noinst_HEADERS=$(testutils_noinst_headers)
+
+TESTS = $(check_PROGRAMS)
+
+AM_CFLAGS = $(common_cflags) -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS
+LDADD = $(common_ldadd) libtestutils.la
+
+EXTRA_DIST = \
+ ges/test-project.xges \
+ ges/test-auto-transition.xges \
+ ges/audio_only.ogg \
+ ges/test-properties.xges \
+ ges/image.png \
+ ges/audio_video.ogg
+
+COVERAGE_DIRS = \
+ ges
+COVERAGE_FILES = $(foreach dir,$(COVERAGE_DIRS),$(wildcard $(top_builddir)/$(dir)/*.gcov))
+COVERAGE_FILES_REL = $(subst $(top_builddir)/,,$(COVERAGE_FILES))
+COVERAGE_OUT_FILES = $(foreach dir,$(COVERAGE_DIRS),$(wildcard $(top_builddir)/$(dir)/*.gcov.out))
+COVERAGE_OUT_FILES_REL = $(subst $(top_builddir)/,,$(COVERAGE_OUT_FILES))
+
+debug:
+ echo $(COVERAGE_FILES)
+ echo $(COVERAGE_FILES_REL)
+
+.PHONY: coverage
+if GST_GCOV_ENABLED
+# we rebuild a registry and do gst-inspect so that all the get/set codepaths
+# are also covered
+coverage:
+ make check
+ make coverage-report
+else
+coverage:
+ echo "You need to configure with --enable-gcov to get coverage data"
+ exit 1
+endif
+
+coverage-report:
+ if test ! -e coverage; then
+ rm -r coverage
+ fi
+ for dir in $(COVERAGE_DIRS); do \
+ mkdir -p coverage/$$dir; \
+ make -C $(top_builddir)/$$dir gcov; \
+ done
+ for dir in $(COVERAGE_DIRS); do \
+ files="`ls $(top_builddir)/$$dir/*.gcov.out 2> /dev/null`"; \
+ if test ! -z "$$files"; then \
+ perl $(top_srcdir)/common/coverage/coverage-report.pl \
+ $(top_builddir)/$$dir/*.gcov.out > \
+ coverage/$$dir/index.xml; \
+ xsltproc $(top_srcdir)/common/coverage/coverage-report.xsl \
+ coverage/$$dir/index.xml > coverage/$$dir/index.html; \
+ fi; \
+ done
+ for file in $(COVERAGE_FILES_REL); do \
+ echo Generating coverage/$$file.html; \
+ perl $(top_srcdir)/common/coverage/coverage-report-entry.pl \
+ $(top_builddir)/$$file > coverage/$$file.html; \
+ done
+
+check:
+if HAVE_GST_VALIDATE_LAUNCHER
+if WITH_PYTHON
+ GI_TYPELIB_PATH=$(top_srcdir)/ges/:${GI_TYPELIB_PATH} LD_LIBRARY_PATH=$(top_srcdir)/ges/.libs:${LD_LIBRARY_PATH} gst-validate-launcher --pyunittest-dir $(top_srcdir)/tests/check/ pyunittest --dump-on-failure
+
+check-python:
+ GI_TYPELIB_PATH=$(top_srcdir)/ges/:${GI_TYPELIB_PATH} LD_LIBRARY_PATH=$(top_srcdir)/ges/.libs:${LD_LIBRARY_PATH} gst-validate-launcher --pyunittest-dir $(top_srcdir)/tests/check/ pyunittest --dump-on-failure
+endif
+endif
--- /dev/null
+backgroundsource
+basic
+effects
+filesource
+layer
+overlays
+save_and_load
+simplelayer
+text_properties
+clip
+timelineedition
+titles
+transition
+track
+asset
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "test-utils.h"
+#include "../../../ges/ges-internal.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+static GMainLoop *mainloop;
+
+static void
+source_asset_created (GObject * source, GAsyncResult * res,
+ gpointer expected_ok)
+{
+ GError *error = NULL;
+
+ GESAsset *a = ges_asset_request_finish (res, &error);
+
+ if (GPOINTER_TO_INT (expected_ok)) {
+ fail_unless (a != NULL);
+ fail_unless (error == NULL);
+ g_object_unref (a);
+ } else {
+ fail_unless (a == NULL);
+ assert_equals_int (error->domain, GST_RESOURCE_ERROR);
+ }
+
+ g_clear_error (&error);
+ g_main_loop_quit (mainloop);
+}
+
+GST_START_TEST (test_basic)
+{
+ ges_init ();
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ ges_asset_request_async (GES_TYPE_URI_CLIP,
+ "file:///this/is/not/for/real", NULL, source_asset_created,
+ GINT_TO_POINTER (FALSE));
+
+ g_main_loop_run (mainloop);
+ g_main_loop_unref (mainloop);
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+typedef struct _CustomContextData
+{
+ GMutex lock;
+ GCond cond;
+ gboolean finish;
+ gboolean expected_ok;
+ gchar *uri;
+} CustomContextData;
+
+static gpointer
+custom_context_thread_func (CustomContextData * data)
+{
+ GMainContext *context;
+
+ context = g_main_context_new ();
+ mainloop = g_main_loop_new (context, FALSE);
+
+ g_main_context_push_thread_default (context);
+
+ /* To use custom context, we need to call ges_init() in the thread */
+ fail_unless (ges_init ());
+
+ ges_asset_request_async (GES_TYPE_URI_CLIP,
+ data->uri, NULL, source_asset_created,
+ GINT_TO_POINTER (data->expected_ok));
+ g_main_loop_run (mainloop);
+
+ g_main_context_pop_thread_default (context);
+ ges_deinit ();
+ g_main_context_unref (context);
+ g_main_loop_unref (mainloop);
+
+ data->finish = TRUE;
+ g_cond_signal (&data->cond);
+
+ return NULL;
+}
+
+GST_START_TEST (test_custom_context)
+{
+ GThread *thread;
+ CustomContextData data;
+
+ mainloop = NULL;
+
+ g_mutex_init (&data.lock);
+ g_cond_init (&data.cond);
+ /* ensure default context here, but we will not use the default context */
+ g_main_context_default ();
+
+ /* first run with invalid uri */
+ data.finish = FALSE;
+ data.expected_ok = FALSE;
+ data.uri = g_strdup ("file:///this/is/not/for/real");
+
+ thread = g_thread_new ("test-custom-context-thread",
+ (GThreadFunc) custom_context_thread_func, &data);
+
+ g_mutex_lock (&data.lock);
+ while (data.finish)
+ g_cond_wait (&data.cond, &data.lock);
+ g_mutex_unlock (&data.lock);
+
+ g_thread_join (thread);
+ g_free (data.uri);
+
+ /* second run with valid uri */
+ data.finish = FALSE;
+ data.expected_ok = TRUE;
+ data.uri = ges_test_file_uri ("audio_video.ogg");
+
+ thread = g_thread_new ("test-custom-context-thread",
+ (GThreadFunc) custom_context_thread_func, &data);
+
+ g_mutex_lock (&data.lock);
+ while (data.finish)
+ g_cond_wait (&data.cond, &data.lock);
+ g_mutex_unlock (&data.lock);
+
+ g_thread_join (thread);
+ g_free (data.uri);
+
+ g_mutex_clear (&data.lock);
+ g_cond_clear (&data.cond);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_transition_change_asset)
+{
+ gchar *id;
+ GESAsset *a;
+ GESExtractable *extractable;
+
+ ges_init ();
+
+ a = ges_asset_request (GES_TYPE_TRANSITION_CLIP, "box-wipe-lc", NULL);
+
+ fail_unless (GES_IS_ASSET (a));
+ fail_unless_equals_string (ges_asset_get_id (a), "box-wipe-lc");
+
+ extractable = ges_asset_extract (a, NULL);
+ fail_unless (ges_extractable_get_asset (extractable) == a);
+
+ id = ges_extractable_get_id (extractable);
+ fail_unless_equals_string (id, "box-wipe-lc");
+ g_free (id);
+
+ g_object_set (extractable, "vtype", 2, NULL);
+
+ id = ges_extractable_get_id (extractable);
+ fail_unless_equals_string (id, "bar-wipe-tb");
+ g_free (id);
+
+ fail_if (ges_extractable_get_asset (extractable) == a);
+ gst_object_unref (a);
+
+ a = ges_extractable_get_asset (extractable);
+ fail_unless_equals_string (ges_asset_get_id (a), "bar-wipe-tb");
+
+ /* Now try to set the a and see if the vtype is properly updated */
+ a = ges_asset_request (GES_TYPE_TRANSITION_CLIP, "box-wipe-lc", NULL);
+ ges_extractable_set_asset (extractable, a);
+ gst_object_unref (a);
+
+ fail_unless_equals_int (GES_TRANSITION_CLIP (extractable)->vtype, 26);
+
+ gst_object_unref (extractable);
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_uri_clip_change_asset)
+{
+ GESAsset *asset, *asset1;
+ GESExtractable *extractable;
+ GESLayer *layer;
+ gchar *uri;
+ gchar *uri1;
+ GESTimeline *timeline;
+
+ ges_init ();
+
+ layer = ges_layer_new ();
+ uri = ges_test_file_uri ("audio_video.ogg");
+ uri1 = ges_test_file_uri ("audio_only.ogg");
+ timeline = ges_timeline_new_audio_video ();
+
+ ges_timeline_add_layer (timeline, layer);
+
+ asset = GES_ASSET (ges_uri_clip_asset_request_sync (uri, NULL));
+
+ fail_unless (GES_IS_ASSET (asset));
+ fail_unless_equals_string (ges_asset_get_id (asset), uri);
+
+ extractable = GES_EXTRACTABLE (ges_layer_add_asset (layer,
+ asset, 0, 0, GST_CLOCK_TIME_NONE, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (ges_extractable_get_asset (extractable) == asset);
+ gst_object_unref (asset);
+
+ /* Now try to set the a and see if the vtype is properly updated */
+ asset1 = GES_ASSET (ges_uri_clip_asset_request_sync (uri1, NULL));
+ fail_unless_equals_int (g_list_length (GES_CONTAINER_CHILDREN (extractable)),
+ 2);
+ fail_if (ges_extractable_set_asset (extractable, asset1));
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (extractable),
+ ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset1)));
+ fail_unless (ges_extractable_set_asset (extractable, asset1));
+ fail_unless_equals_int (g_list_length (GES_CONTAINER_CHILDREN (extractable)),
+ 1);
+
+ gst_object_unref (extractable);
+
+ g_free (uri);
+ g_free (uri1);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_list_asset)
+{
+ GList *assets;
+ GEnumClass *enum_class;
+
+ ges_init ();
+
+ enum_class = g_type_class_peek (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+
+ fail_unless (ges_init ());
+ fail_if (ges_list_assets (GES_TYPE_OVERLAY_CLIP));
+
+ assets = ges_list_assets (GES_TYPE_TRANSITION_CLIP);
+ /* note: we do not have a a for value=0 "Transition not set" */
+ assert_equals_int (g_list_length (assets), enum_class->n_values - 1);
+ g_list_free (assets);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_proxy_asset)
+{
+ GESAsset *identity, *nothing, *nothing_at_all;
+
+ fail_unless (ges_init ());
+
+ identity = ges_asset_request (GES_TYPE_EFFECT, "video identity", NULL);
+ fail_unless (identity != NULL);
+
+ nothing = ges_asset_request (GES_TYPE_EFFECT, "nothing", NULL);
+ fail_if (nothing);
+
+ nothing = ges_asset_cache_lookup (GES_TYPE_EFFECT, "nothing");
+ fail_unless (nothing != NULL);
+
+ fail_unless (ges_asset_try_proxy (nothing, "video identity"));
+ fail_unless (ges_asset_set_proxy (NULL, identity));
+
+ nothing_at_all = ges_asset_request (GES_TYPE_EFFECT, "nothing_at_all", NULL);
+ fail_if (nothing_at_all);
+
+ nothing_at_all = ges_asset_cache_lookup (GES_TYPE_EFFECT, "nothing_at_all");
+ fail_unless (nothing_at_all != NULL);
+
+ /* Now we proxy nothing_at_all to nothing which is itself proxied to identity */
+ fail_unless (ges_asset_try_proxy (nothing_at_all, "nothing"));
+ fail_unless (ges_asset_set_proxy (NULL, nothing));
+ fail_unless_equals_int (g_list_length (ges_asset_list_proxies
+ (nothing_at_all)), 1);
+
+ fail_unless_equals_pointer (ges_asset_get_proxy_target (nothing),
+ nothing_at_all);
+
+ /* If we request nothing_at_all we should get the good proxied identity */
+ nothing_at_all = ges_asset_request (GES_TYPE_EFFECT, "nothing_at_all", NULL);
+ fail_unless (nothing_at_all == identity);
+
+ gst_object_unref (identity);
+ gst_object_unref (nothing_at_all);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges");
+ TCase *tc_chain = tcase_create ("a");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_basic);
+ tcase_add_test (tc_chain, test_custom_context);
+ tcase_add_test (tc_chain, test_transition_change_asset);
+ tcase_add_test (tc_chain, test_uri_clip_change_asset);
+ tcase_add_test (tc_chain, test_list_asset);
+ tcase_add_test (tc_chain, test_proxy_asset);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+#define DEFAULT_VOLUME 1.0
+
+GST_START_TEST (test_test_source_basic)
+{
+ GESTestClip *source;
+
+ ges_init ();
+
+ source = ges_test_clip_new ();
+ fail_unless (source != NULL);
+
+ gst_object_unref (source);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_test_source_properties)
+{
+ GESClip *clip;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ track = ges_track_new (GES_TRACK_TYPE_AUDIO, gst_caps_ref (GST_CAPS_ANY));
+ fail_unless (track != NULL);
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip = (GESClip *) ges_test_clip_new ();
+ fail_unless (clip != NULL);
+
+ /* Set some properties */
+ GST_DEBUG ("Setting start duration and inpoint to %" GST_PTR_FORMAT, clip);
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 51,
+ "in-point", (guint64) 12, NULL);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
+ GES_TIMELINE_ELEMENT (clip));
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 51);
+ assert_equals_uint64 (_INPOINT (trackelement), 12);
+
+ fail_unless (ges_timeline_commit (timeline));
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change more properties, see if they propagate */
+ g_object_set (clip, "start", (guint64) 420, "duration", (guint64) 510,
+ "in-point", (guint64) 120, NULL);
+ assert_equals_uint64 (_START (clip), 420);
+ assert_equals_uint64 (_DURATION (clip), 510);
+ assert_equals_uint64 (_INPOINT (clip), 120);
+ assert_equals_uint64 (_START (trackelement), 420);
+ assert_equals_uint64 (_DURATION (trackelement), 510);
+ assert_equals_uint64 (_INPOINT (trackelement), 120);
+
+ fail_unless (ges_timeline_commit (timeline));
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Test mute support */
+ g_object_set (clip, "mute", TRUE, NULL);
+ fail_unless (ges_timeline_commit (timeline));
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, FALSE);
+ g_object_set (clip, "mute", FALSE, NULL);
+ fail_unless (ges_timeline_commit (timeline));
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (trackelement));
+ gst_object_unref (clip);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_test_source_in_layer)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *a, *v;
+ GESTrackElement *track_element;
+ GESTestClip *source;
+ GESVideoTestPattern ptrn;
+ gdouble freq, volume;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ a = GES_TRACK (ges_audio_track_new ());
+ v = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, a);
+ ges_timeline_add_track (timeline, v);
+ ges_timeline_add_layer (timeline, layer);
+
+ source = ges_test_clip_new_for_nick ((gchar *) "red");
+ g_object_get (source, "vpattern", &ptrn, NULL);
+ assert_equals_int (ptrn, GES_VIDEO_TEST_PATTERN_RED);
+
+ g_object_set (source, "duration", (guint64) GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) source);
+
+ /* specifically test the vpattern property */
+ g_object_set (source, "vpattern", (gint) GES_VIDEO_TEST_PATTERN_WHITE, NULL);
+ g_object_get (source, "vpattern", &ptrn, NULL);
+ assert_equals_int (ptrn, GES_VIDEO_TEST_PATTERN_WHITE);
+
+ track_element =
+ ges_clip_find_track_element (GES_CLIP (source), v,
+ GES_TYPE_VIDEO_TEST_SOURCE);
+
+ g_assert (GES_IS_VIDEO_TEST_SOURCE (track_element));
+
+ ptrn = (ges_video_test_source_get_pattern ((GESVideoTestSource *)
+ track_element));
+ assert_equals_int (ptrn, GES_VIDEO_TEST_PATTERN_WHITE);
+ gst_object_unref (track_element);
+
+ /* test audio properties as well */
+
+ track_element = ges_clip_find_track_element (GES_CLIP (source),
+ a, GES_TYPE_AUDIO_TEST_SOURCE);
+ g_assert (GES_IS_AUDIO_TEST_SOURCE (track_element));
+ assert_equals_float (ges_test_clip_get_frequency (source), 440);
+ assert_equals_float (ges_test_clip_get_volume (source), DEFAULT_VOLUME);
+
+ g_object_get (source, "freq", &freq, "volume", &volume, NULL);
+ assert_equals_float (freq, 440);
+ assert_equals_float (volume, DEFAULT_VOLUME);
+
+
+ freq = ges_audio_test_source_get_freq (GES_AUDIO_TEST_SOURCE (track_element));
+ volume =
+ ges_audio_test_source_get_volume (GES_AUDIO_TEST_SOURCE (track_element));
+ g_assert (freq == 440);
+ g_assert (volume == DEFAULT_VOLUME);
+
+ g_object_set (source, "freq", (gdouble) 2000, "volume", (gdouble) 0.5, NULL);
+ g_object_get (source, "freq", &freq, "volume", &volume, NULL);
+ assert_equals_float (freq, 2000);
+ assert_equals_float (volume, 0.5);
+
+ freq = ges_audio_test_source_get_freq (GES_AUDIO_TEST_SOURCE (track_element));
+ volume =
+ ges_audio_test_source_get_volume (GES_AUDIO_TEST_SOURCE (track_element));
+ g_assert (freq == 2000);
+ g_assert (volume == 0.5);
+
+ gst_object_unref (track_element);
+
+ ges_layer_remove_clip (layer, (GESClip *) source);
+
+ GST_DEBUG ("removing the layer");
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+#if 0
+static gint
+find_composition_func (const GValue * velement)
+{
+ GstElement *element = g_value_get_object (velement);
+ GstElementFactory *fac = gst_element_get_factory (element);
+ const gchar *name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (fac));
+
+ if (g_strcmp0 (name, "nlecomposition") == 0)
+ return 0;
+
+ return 1;
+}
+
+static GstElement *
+find_composition (GESTrack * track)
+{
+ GstIterator *it = gst_bin_iterate_recurse (GST_BIN (track));
+ GValue val = { 0, };
+ GstElement *ret = NULL;
+
+ if (gst_iterator_find_custom (it, (GCompareFunc) find_composition_func, &val,
+ NULL))
+ ret = g_value_get_object (&val);
+
+ g_value_unset (&val);
+ gst_iterator_free (it);
+
+ return ret;
+}
+
+#define gap_object_check(nleobj, start, duration, priority) \
+{ \
+ guint64 pstart, pdur, pprio; \
+ g_object_get (nleobj, "start", &pstart, "duration", &pdur, \
+ "priority", &pprio, NULL); \
+ assert_equals_uint64 (pstart, start); \
+ assert_equals_uint64 (pdur, duration); \
+ assert_equals_int (pprio, priority); \
+}
+
+GST_START_TEST (test_gap_filling_basic)
+{
+ GList *tmp;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GstElement *composition;
+ GESLayer *layer;
+ GESClip *clip, *clip1, *clip2;
+
+ GstElement *nlesrc, *nlesrc1, *gap = NULL;
+ GESTrackElement *trackelement, *trackelement1, *trackelement2;
+
+ track = GES_TRACK (ges_audio_track_new ());
+ fail_unless (track != NULL);
+
+ composition = find_composition (track);
+ fail_unless (composition != NULL);
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ fail_unless (clip != NULL);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 0, "duration", (guint64) 5, NULL);
+ assert_equals_uint64 (_START (clip), 0);
+ assert_equals_uint64 (_DURATION (clip), 5);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+
+ nlesrc = ges_track_element_get_nleobject (trackelement);
+ fail_unless (nlesrc != NULL);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 0);
+ assert_equals_uint64 (_DURATION (trackelement), 5);
+
+ /* Check no gap were wrongly added
+ * 2: 1 for the trackelement and 1 for the mixer */
+ assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 2);
+
+ clip1 = GES_CLIP (ges_test_clip_new ());
+ fail_unless (clip1 != NULL);
+
+ g_object_set (clip1, "start", (guint64) 15, "duration", (guint64) 5, NULL);
+ assert_equals_uint64 (_START (clip1), 15);
+ assert_equals_uint64 (_DURATION (clip1), 5);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip1));
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip1)), 1);
+ trackelement1 = GES_CONTAINER_CHILDREN (clip1)->data;
+ fail_unless (trackelement1 != NULL);
+ fail_unless (ges_track_element_get_track (trackelement1) == track);
+ nlesrc1 = ges_track_element_get_nleobject (trackelement1);
+ fail_unless (nlesrc1 != NULL);
+
+ /* Check that trackelement1 has the same properties */
+ assert_equals_uint64 (_START (trackelement1), 15);
+ assert_equals_uint64 (_DURATION (trackelement1), 5);
+
+ /* Check the gap as properly been added */
+ assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 4);
+
+ for (tmp = GST_BIN_CHILDREN (composition); tmp; tmp = tmp->next) {
+ guint prio;
+ GstElement *tmp_nleobj = GST_ELEMENT (tmp->data);
+
+ g_object_get (tmp_nleobj, "priority", &prio, NULL);
+ if (tmp_nleobj != nlesrc && tmp_nleobj != nlesrc1 && prio == 1) {
+ gap = tmp_nleobj;
+ }
+ }
+ fail_unless (gap != NULL);
+ gap_object_check (gap, 5, 10, 1);
+
+ clip2 = GES_CLIP (ges_test_clip_new ());
+ fail_unless (clip2 != NULL);
+ g_object_set (clip2, "start", (guint64) 35, "duration", (guint64) 5, NULL);
+ ges_layer_add_clip (layer, GES_CLIP (clip2));
+ fail_unless (ges_timeline_commit (timeline));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip2)), 1);
+ trackelement2 = GES_CONTAINER_CHILDREN (clip2)->data;
+ fail_unless (trackelement2 != NULL);
+ fail_unless (ges_track_element_get_track (trackelement2) == track);
+ assert_equals_uint64 (_START (trackelement2), 35);
+ assert_equals_uint64 (_DURATION (trackelement2), 5);
+ assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 6);
+
+ gst_object_unref (timeline);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_gap_filling_empty_track)
+{
+ GESAsset *asset;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GstElement *gap;
+ GstElement *composition;
+ GESLayer *layer;
+ GESClip *clip;
+
+ track = GES_TRACK (ges_audio_track_new ());
+
+ layer = ges_layer_new ();
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+ fail_unless (ges_timeline_add_track (timeline,
+ GES_TRACK (ges_video_track_new ())));
+
+ /* Set some properties */
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ clip = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_VIDEO);
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+
+ /* We should not have created any TrackElement in the audio track */
+ fail_unless (ges_track_get_elements (track) == NULL);
+
+ /* Check that a gap was properly added */
+ composition = find_composition (track);
+ /* We also have an adder in that composition */
+ assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 2);
+
+ gap = GST_BIN_CHILDREN (composition)->data;
+ fail_unless (gap != NULL);
+ gap_object_check (gap, 0, 10, 1);
+ fail_unless (ges_timeline_commit (timeline));
+
+ gst_object_unref (timeline);
+}
+
+GST_END_TEST;
+#endif
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-backgroundsource");
+ TCase *tc_chain = tcase_create ("backgroundsource");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_test_source_basic);
+ tcase_add_test (tc_chain, test_test_source_properties);
+ tcase_add_test (tc_chain, test_test_source_in_layer);
+
+#if 0
+ tcase_add_test (tc_chain, test_gap_filling_basic);
+ tcase_add_test (tc_chain, test_gap_filling_empty_track);
+#endif
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ * 2012 Collabora Ltd.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+
+GST_START_TEST (test_ges_scenario)
+{
+ GESTimeline *timeline;
+ GESLayer *layer, *tmp_layer;
+ GESTrack *track;
+ GESTestClip *source;
+ GESTrackElement *trackelement;
+ GList *trackelements, *layers, *tracks;
+
+ ges_init ();
+ /* This is the simplest scenario ever */
+
+ /* Timeline and 1 Layer */
+ GST_DEBUG ("Create a timeline");
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ GST_DEBUG ("Create a layer");
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+
+ GST_DEBUG ("Add the layer to the timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ /* The timeline steals our reference to the layer */
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ fail_unless (layer->timeline == timeline);
+
+ layers = ges_timeline_get_layers (timeline);
+ fail_unless (g_list_find (layers, layer) != NULL);
+ g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
+ g_list_free (layers);
+
+ /* Give the Timeline a Track */
+ GST_DEBUG ("Create a Track");
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ GST_DEBUG ("Add the track to the timeline");
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ /* The timeline steals the reference to the track */
+ ASSERT_OBJECT_REFCOUNT (track, "track", 1);
+ fail_unless (ges_track_get_timeline (track) == timeline);
+ fail_unless ((gpointer) GST_ELEMENT_PARENT (track) == (gpointer) timeline);
+
+ /* Create a source and add it to the Layer */
+ GST_DEBUG ("Creating a source");
+ source = ges_test_clip_new ();
+ fail_unless (source != NULL);
+
+ /* The source will be floating before added to the layer... */
+ fail_unless (g_object_is_floating (source));
+ GST_DEBUG ("Adding the source to the timeline layer");
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (source)));
+ fail_if (g_object_is_floating (source));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (source));
+ fail_unless (tmp_layer == layer);
+ /* The timeline stole our reference */
+ ASSERT_OBJECT_REFCOUNT (source, "source + 1 timeline", 2);
+ gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+
+ /* Make sure the associated TrackElement is in the Track */
+ trackelements = GES_CONTAINER_CHILDREN (source);
+ fail_unless (trackelements != NULL);
+ trackelement = GES_TRACK_ELEMENT (trackelements->data);
+ /* There are 3 references:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "trackelement", 3);
+ /* There are 3 references:
+ * 1 by the clip
+ * 3 by the timeline
+ * 1 by the track */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "trackelement", 3);
+
+ GST_DEBUG ("Remove the Clip from the layer");
+
+ /* Now remove the clip */
+ gst_object_ref (source);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ fail_unless (ges_layer_remove_clip (layer, GES_CLIP (source)));
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ tmp_layer = ges_clip_get_layer (GES_CLIP (source));
+ fail_unless (tmp_layer == NULL);
+ gst_object_unref (source);
+
+ GST_DEBUG ("Removing track from the timeline");
+ /* Remove the track from the timeline */
+ gst_object_ref (track);
+ fail_unless (ges_timeline_remove_track (timeline, track));
+ fail_unless (ges_track_get_timeline (track) == NULL);
+
+ tracks = ges_timeline_get_tracks (timeline);
+ fail_unless (tracks == NULL);
+ ASSERT_OBJECT_REFCOUNT (track, "track", 1);
+ gst_object_unref (track);
+
+ GST_DEBUG ("Removing layer from the timeline");
+ /* Remove the layer from the timeline */
+ gst_object_ref (layer);
+ fail_unless (ges_timeline_remove_layer (timeline, layer));
+ fail_unless (layer->timeline == NULL);
+
+ layers = ges_timeline_get_layers (timeline);
+ fail_unless (layers == NULL);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ gst_object_unref (layer);
+
+ /* Finally clean up our object */
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+/* very similar to the above, except we add the clip to the layer
+ * and then add it to the timeline.
+ */
+
+GST_START_TEST (test_ges_timeline_add_layer)
+{
+ GESTimeline *timeline;
+ GESLayer *layer, *tmp_layer;
+ GESTrack *track;
+ GESTestClip *s1, *s2, *s3;
+ GList *trackelements, *layers;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ /* Timeline and 1 Layer */
+ GST_DEBUG ("Create a timeline");
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ GST_DEBUG ("Create a layer");
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ /* Give the Timeline a Track */
+ GST_DEBUG ("Create a Track");
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ GST_DEBUG ("Add the track to the timeline");
+ fail_unless (ges_timeline_add_track (timeline, track));
+ /* The timeline steals the reference to the track */
+ ASSERT_OBJECT_REFCOUNT (track, "track", 1);
+ fail_unless (ges_track_get_timeline (track) == timeline);
+ fail_unless ((gpointer) GST_ELEMENT_PARENT (track) == (gpointer) timeline);
+
+ /* Create a source and add it to the Layer */
+ GST_DEBUG ("Creating a source");
+ s1 = ges_test_clip_new ();
+ fail_unless (s1 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
+ fail_unless (tmp_layer == layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 2);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Creating a source");
+ s2 = ges_test_clip_new ();
+ fail_unless (s2 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
+ fail_unless (tmp_layer == layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 2);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Creating a source");
+ s3 = ges_test_clip_new ();
+ fail_unless (s3 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
+ fail_unless (tmp_layer == layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 2);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Add the layer to the timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ /* The timeline steals our reference to the layer */
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ fail_unless (layer->timeline == timeline);
+ layers = ges_timeline_get_layers (timeline);
+ fail_unless (g_list_find (layers, layer) != NULL);
+ g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
+ g_list_free (layers);
+
+ /* Make sure the associated TrackElements are in the Track */
+ trackelements = GES_CONTAINER_CHILDREN (s1);
+ fail_unless (trackelements != NULL);
+ trackelement = GES_TRACK_ELEMENT (trackelements->data);
+ /* There are 3 references:
+ * 1 by the clip
+ * 1 by the trackelement
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "trackelement", 3);
+ /* There are 3 references:
+ * 1 by the clip
+ * 1 by the timeline
+ * 1 by the trackelement */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "trackelement", 3);
+
+ trackelements = GES_CONTAINER_CHILDREN (s2);
+ trackelement = GES_TRACK_ELEMENT (trackelements->data);
+ fail_unless (trackelements != NULL);
+
+ /* There are 3 references:
+ * 1 by the clip
+ * 1 by the timeline
+ * 1 by the trackelement */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (trackelement), "trackelement", 3);
+
+ trackelements = GES_CONTAINER_CHILDREN (s3);
+ trackelement = GES_TRACK_ELEMENT (trackelements->data);
+ fail_unless (trackelements != NULL);
+
+ /* There are 3 references:
+ * 1 by the clip
+ * 1 by the timeline
+ * 2 by the trackelement */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "trackelement", 3);
+
+ /* theoretically this is all we need to do to ensure cleanup */
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+/* this time we add the layer before we add the track. */
+
+GST_START_TEST (test_ges_timeline_add_layer_first)
+{
+ GESTimeline *timeline;
+ GESLayer *layer, *tmp_layer;
+ GESTrack *track;
+ GESTestClip *s1, *s2, *s3;
+ GList *trackelements, *tmp, *layers;
+
+ ges_init ();
+
+ /* Timeline and 1 Layer */
+ GST_DEBUG ("Create a timeline");
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ GST_DEBUG ("Create a layer");
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ /* Give the Timeline a Track */
+ GST_DEBUG ("Create a Track");
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ /* Create a source and add it to the Layer */
+ GST_DEBUG ("Creating a source");
+ s1 = ges_test_clip_new ();
+ fail_unless (s1 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Creating a source");
+ s2 = ges_test_clip_new ();
+ fail_unless (s2 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Creating a source");
+ s3 = ges_test_clip_new ();
+ fail_unless (s3 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Add the layer to the timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ /* The timeline steals our reference to the layer */
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ fail_unless (layer->timeline == timeline);
+ layers = ges_timeline_get_layers (timeline);
+ fail_unless (g_list_find (layers, layer) != NULL);
+ g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
+ g_list_free (layers);
+
+ GST_DEBUG ("Add the track to the timeline");
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (track, "track", 1);
+ fail_unless (ges_track_get_timeline (track) == timeline);
+ fail_unless ((gpointer) GST_ELEMENT_PARENT (track) == (gpointer) timeline);
+
+ /* Make sure the associated TrackElements are in the Track */
+ trackelements = GES_CONTAINER_CHILDREN (s1);
+ fail_unless (trackelements != NULL);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* Each object has 3 references:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ }
+
+ trackelements = GES_CONTAINER_CHILDREN (s2);
+ fail_unless (trackelements != NULL);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* Each object has 3 references:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ }
+
+ trackelements = GES_CONTAINER_CHILDREN (s3);
+ fail_unless (trackelements != NULL);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* Each object has 3 references:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ }
+
+ /* theoretically this is all we need to do to ensure cleanup */
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_ges_timeline_remove_track)
+{
+ GESTimeline *timeline;
+ GESLayer *layer, *tmp_layer;
+ GESTrack *track;
+ GESTestClip *s1, *s2, *s3;
+ GESTrackElement *t1, *t2, *t3;
+ GList *trackelements, *tmp, *layers;
+
+ ges_init ();
+
+ /* Timeline and 1 Layer */
+ GST_DEBUG ("Create a timeline");
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ GST_DEBUG ("Create a layer");
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ /* Give the Timeline a Track */
+ GST_DEBUG ("Create a Track");
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ /* Create a source and add it to the Layer */
+ GST_DEBUG ("Creating a source");
+ s1 = ges_test_clip_new ();
+ fail_unless (s1 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
+
+ GST_DEBUG ("Creating a source");
+ s2 = ges_test_clip_new ();
+ fail_unless (s2 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
+
+ GST_DEBUG ("Creating a source");
+ s3 = ges_test_clip_new ();
+ fail_unless (s3 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
+
+ GST_DEBUG ("Add the layer to the timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ /* The timeline steals our reference to the layer */
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ fail_unless (layer->timeline == timeline);
+
+ layers = ges_timeline_get_layers (timeline);
+ fail_unless (g_list_find (layers, layer) != NULL);
+ g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
+ g_list_free (layers);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
+
+ GST_DEBUG ("Add the track to the timeline");
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (track, "track", 1);
+ fail_unless (ges_track_get_timeline (track) == timeline);
+ fail_unless ((gpointer) GST_ELEMENT_PARENT (track) == (gpointer) timeline);
+
+ /* Make sure the associated TrackElements are in the Track */
+ trackelements = GES_CONTAINER_CHILDREN (s1);
+ fail_unless (trackelements != NULL);
+ t1 = GES_TRACK_ELEMENT ((trackelements)->data);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* There are 3 references held:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ }
+ /* There are 3 references held:
+ * 1 by the container
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (t1, "trackelement", 3);
+
+ trackelements = GES_CONTAINER_CHILDREN (s2);
+ fail_unless (trackelements != NULL);
+ t2 = GES_TRACK_ELEMENT (trackelements->data);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* There are 3 references held:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ }
+ /* There are 3 references held:
+ * 1 by the container
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (t2, "t2", 3);
+
+ trackelements = GES_CONTAINER_CHILDREN (s3);
+ fail_unless (trackelements != NULL);
+ t3 = GES_TRACK_ELEMENT (trackelements->data);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* There are 3 references held:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ }
+ /* There are 3 references held:
+ * 1 by the container
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (t3, "t3", 3);
+
+ /* remove the track and check that the track elements have been released */
+ fail_unless (ges_timeline_remove_track (timeline, track));
+
+ ASSERT_OBJECT_REFCOUNT (t1, "trackelement", 1);
+ ASSERT_OBJECT_REFCOUNT (t2, "trackelement", 1);
+ ASSERT_OBJECT_REFCOUNT (t3, "trackelement", 1);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
+ ASSERT_OBJECT_REFCOUNT (timeline, "1 for the us", 1);
+ tmp = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (tmp), 3);
+ g_list_free_full (tmp, (GDestroyNotify) gst_object_unref);
+
+ gst_check_objects_destroyed_on_unref (G_OBJECT (timeline),
+ G_OBJECT (layer), t1, t2, t3, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+typedef struct
+{
+ GESTestClip **o1, **o2, **o3;
+ GESTrack **tr1, **tr2;
+} SelectTracksData;
+
+static GPtrArray *
+select_tracks_cb (GESTimeline * timeline, GESClip * clip,
+ GESTrackElement * track_element, SelectTracksData * st_data)
+{
+ GESTrack *track;
+
+ GPtrArray *ret = g_ptr_array_new ();
+ track = (clip == (GESClip *) * st_data->o2) ? *st_data->tr2 : *st_data->tr1;
+
+ gst_object_ref (track);
+
+ g_ptr_array_add (ret, track);
+
+ return ret;
+}
+
+GST_START_TEST (test_ges_timeline_multiple_tracks)
+{
+ GESTimeline *timeline;
+ GESLayer *layer, *tmp_layer;
+ GESTrack *track1, *track2;
+ GESTestClip *s1, *s2, *s3;
+ GESTrackElement *t1, *t2, *t3;
+ GList *trackelements, *tmp, *layers;
+ SelectTracksData st_data = { &s1, &s2, &s3, &track1, &track2 };
+
+ ges_init ();
+
+ /* Timeline and 1 Layer */
+ GST_DEBUG ("Create a timeline");
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ g_signal_connect (timeline, "select-tracks-for-object",
+ G_CALLBACK (select_tracks_cb), &st_data);
+
+ GST_DEBUG ("Create a layer");
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ /* Give the Timeline a Track */
+ GST_DEBUG ("Create Track 1");
+ track1 = GES_TRACK (ges_video_track_new ());
+ fail_unless (track1 != NULL);
+ GST_DEBUG ("Create Track 2");
+ track2 = GES_TRACK (ges_video_track_new ());
+ fail_unless (track2 != NULL);
+
+ GST_DEBUG ("Add the track 1 to the timeline");
+ fail_unless (ges_timeline_add_track (timeline, track1));
+ ASSERT_OBJECT_REFCOUNT (track1, "track", 1);
+ fail_unless (ges_track_get_timeline (track1) == timeline);
+ fail_unless ((gpointer) GST_ELEMENT_PARENT (track1) == (gpointer) timeline);
+
+ GST_DEBUG ("Add the track 2 to the timeline");
+ fail_unless (ges_timeline_add_track (timeline, track2));
+ ASSERT_OBJECT_REFCOUNT (track2, "track", 1);
+ fail_unless (ges_track_get_timeline (track2) == timeline);
+ fail_unless ((gpointer) GST_ELEMENT_PARENT (track2) == (gpointer) timeline);
+
+ /* Create a source and add it to the Layer */
+ GST_DEBUG ("Creating a source");
+ s1 = ges_test_clip_new ();
+ fail_unless (s1 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Creating a source");
+ s2 = ges_test_clip_new ();
+ fail_unless (s2 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Creating a source");
+ s3 = ges_test_clip_new ();
+ fail_unless (s3 != NULL);
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
+ tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
+ fail_unless (tmp_layer == layer);
+ gst_object_unref (tmp_layer);
+
+ GST_DEBUG ("Add the layer to the timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ /* The timeline steals our reference to the layer */
+ ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
+ fail_unless (layer->timeline == timeline);
+
+ layers = ges_timeline_get_layers (timeline);
+ fail_unless (g_list_find (layers, layer) != NULL);
+ g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
+ g_list_free (layers);
+
+ /* Make sure the associated TrackElements are in the Track */
+ trackelements = GES_CONTAINER_CHILDREN (s1);
+ fail_unless (trackelements != NULL);
+ t1 = GES_TRACK_ELEMENT ((trackelements)->data);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* There are 3 references held:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ fail_unless (ges_track_element_get_track (tmp->data) == track1);
+ }
+ gst_object_ref (t1);
+ /* There are 3 references held:
+ * 1 by the container
+ * 1 by the track
+ * 1 by the timeline
+ * 1 added by ourselves above (gst_object_ref (t1)) */
+ ASSERT_OBJECT_REFCOUNT (t1, "trackelement", 4);
+
+ trackelements = GES_CONTAINER_CHILDREN (s2);
+ fail_unless (trackelements != NULL);
+ t2 = GES_TRACK_ELEMENT (trackelements->data);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* There are 3 references held:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ fail_unless (ges_track_element_get_track (tmp->data) == track2);
+ }
+ gst_object_ref (t2);
+ /* There are 3 references held:
+ * 1 by the container
+ * 1 by the track
+ * 1 by the timeline
+ * 1 added by ourselves above (gst_object_ref (t2)) */
+ ASSERT_OBJECT_REFCOUNT (t2, "t2", 4);
+
+ trackelements = GES_CONTAINER_CHILDREN (s3);
+ fail_unless (trackelements != NULL);
+ t3 = GES_TRACK_ELEMENT (trackelements->data);
+ for (tmp = trackelements; tmp; tmp = tmp->next) {
+ /* There are 3 references held:
+ * 1 by the clip
+ * 1 by the track
+ * 1 by the timeline */
+ ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
+ fail_unless (ges_track_element_get_track (tmp->data) == track1);
+ }
+ gst_object_ref (t3);
+ /* There are 3 references held:
+ * 1 by the container
+ * 1 by the track
+ * 1 by the timeline
+ * 1 added by ourselves above (gst_object_ref (t3)) */
+ ASSERT_OBJECT_REFCOUNT (t3, "t3", 4);
+
+ gst_object_unref (t1);
+ gst_object_unref (t2);
+ gst_object_unref (t3);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_ges_pipeline_change_state)
+{
+ GstState state;
+ GESAsset *asset;
+ GESLayer *layer;
+ GESTimeline *timeline;
+ GESPipeline *pipeline;
+
+ ges_init ();
+
+ layer = ges_layer_new ();
+ timeline = ges_timeline_new_audio_video ();
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ pipeline = ges_test_create_pipeline (timeline);
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ gst_object_unref (asset);
+
+ ges_timeline_commit (timeline);
+ ASSERT_SET_STATE (GST_ELEMENT (pipeline), GST_STATE_PLAYING,
+ GST_STATE_CHANGE_ASYNC);
+ fail_unless (gst_element_get_state (GST_ELEMENT (pipeline), &state, NULL,
+ GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_SUCCESS);
+ fail_unless (state == GST_STATE_PLAYING);
+ ASSERT_SET_STATE (GST_ELEMENT (pipeline), GST_STATE_NULL,
+ GST_STATE_CHANGE_SUCCESS);
+
+ gst_object_unref (pipeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_ges_timeline_element_name)
+{
+ GESClip *clip, *clip1, *clip2, *clip3, *clip4, *clip5;
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESLayer *layer;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ clip = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip), "testclip0");
+
+
+ clip1 = GES_CLIP (ges_test_clip_new ());
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip1), "testclip1");
+
+ ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip1), "testclip1");
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip1), "testclip1");
+
+ /* Check that trying to set to a name that is already used leads to
+ * a change in the name */
+ ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), "testclip1");
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip), "testclip2");
+
+ ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip1), "testclip4");
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip1), "testclip4");
+
+ clip2 = GES_CLIP (ges_test_clip_new ());
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip2), "testclip5");
+ ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip2), NULL);
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip2), "testclip6");
+
+ clip3 = GES_CLIP (ges_test_clip_new ());
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip3), "testclip7");
+ ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip3), "testclip5");
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip3), "testclip8");
+
+ clip4 = GES_CLIP (ges_test_clip_new ());
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip4), "testclip9");
+
+
+ clip5 = GES_CLIP (ges_test_clip_new ());
+ ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip5),
+ "Something I want!");
+ fail_unless_equals_string (GES_TIMELINE_ELEMENT_NAME (clip5),
+ "Something I want!");
+
+ gst_object_unref (asset);
+
+ gst_object_unref (clip1);
+ gst_object_unref (clip2);
+ gst_object_unref (clip3);
+ gst_object_unref (clip4);
+ gst_object_unref (clip5);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-basic");
+ TCase *tc_chain = tcase_create ("basic");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_ges_scenario);
+ tcase_add_test (tc_chain, test_ges_timeline_add_layer);
+ tcase_add_test (tc_chain, test_ges_timeline_add_layer_first);
+ tcase_add_test (tc_chain, test_ges_timeline_remove_track);
+ tcase_add_test (tc_chain, test_ges_timeline_multiple_tracks);
+ tcase_add_test (tc_chain, test_ges_pipeline_change_state);
+ tcase_add_test (tc_chain, test_ges_timeline_element_name);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include "../../../ges/ges-structured-interface.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+GST_START_TEST (test_object_properties)
+{
+ GESClip *clip;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip = (GESClip *) ges_test_clip_new ();
+ fail_unless (clip != NULL);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 51,
+ "in-point", (guint64) 12, NULL);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
+ GES_TIMELINE_ELEMENT (clip));
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 51);
+ assert_equals_uint64 (_INPOINT (trackelement), 12);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change more properties, see if they propagate */
+ g_object_set (clip, "start", (guint64) 420, "duration", (guint64) 510,
+ "in-point", (guint64) 120, NULL);
+ assert_equals_uint64 (_START (clip), 420);
+ assert_equals_uint64 (_DURATION (clip), 510);
+ assert_equals_uint64 (_INPOINT (clip), 120);
+ assert_equals_uint64 (_START (trackelement), 420);
+ assert_equals_uint64 (_DURATION (trackelement), 510);
+ assert_equals_uint64 (_INPOINT (trackelement), 120);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+
+ /* This time, we move the trackelement to see if the changes move
+ * along to the parent and the gnonlin clip */
+ g_object_set (trackelement, "start", (guint64) 400, NULL);
+ ges_timeline_commit (timeline);
+ assert_equals_uint64 (_START (clip), 400);
+ assert_equals_uint64 (_START (trackelement), 400);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 400, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (trackelement));
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_split_direct_bindings)
+{
+ GList *values;
+ GstControlSource *source;
+ GESTimeline *timeline;
+ GESClip *clip, *splitclip;
+ GstControlBinding *binding = NULL, *splitbinding;
+ GstTimedValueControlSource *splitsource;
+ GESLayer *layer;
+ GESAsset *asset;
+ GValue *tmpvalue;
+
+ GESTrackElement *element;
+
+ ges_init ();
+
+ fail_unless ((timeline = ges_timeline_new ()));
+ fail_unless ((layer = ges_layer_new ()));
+ fail_unless (ges_timeline_add_track (timeline,
+ GES_TRACK (ges_video_track_new ())));
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ clip = ges_layer_add_asset (layer, asset, 0, 10 * GST_SECOND, 10 * GST_SECOND,
+ GES_TRACK_TYPE_UNKNOWN);
+ g_object_unref (asset);
+
+ CHECK_OBJECT_PROPS (clip, 0 * GST_SECOND, 10 * GST_SECOND, 10 * GST_SECOND);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ check_layer (clip, 0);
+
+ source = gst_interpolation_control_source_new ();
+ g_object_set (source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+ element = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (ges_track_element_set_control_source (element,
+ source, "alpha", "direct"));
+
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE (source),
+ 10 * GST_SECOND, 0.0);
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE (source),
+ 20 * GST_SECOND, 1.0);
+
+ binding = ges_track_element_get_control_binding (element, "alpha");
+ tmpvalue = gst_control_binding_get_value (binding, 10 * GST_SECOND);
+ assert_equals_int (g_value_get_double (tmpvalue), 0.0);
+ g_value_unset (tmpvalue);
+ g_free (tmpvalue);
+
+ tmpvalue = gst_control_binding_get_value (binding, 20 * GST_SECOND);
+ assert_equals_int (g_value_get_double (tmpvalue), 1.0);
+ g_value_unset (tmpvalue);
+ g_free (tmpvalue);
+
+ splitclip = ges_clip_split (clip, 5 * GST_SECOND);
+ CHECK_OBJECT_PROPS (splitclip, 5 * GST_SECOND, 15 * GST_SECOND,
+ 5 * GST_SECOND);
+ check_layer (splitclip, 0);
+
+ splitbinding =
+ ges_track_element_get_control_binding (GES_CONTAINER_CHILDREN
+ (splitclip)->data, "alpha");
+ g_object_get (splitbinding, "control_source", &splitsource, NULL);
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (splitsource));
+ assert_equals_int (g_list_length (values), 2);
+ assert_equals_uint64 (((GstTimedValue *) values->data)->timestamp,
+ 15 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->data)->value, 0.5);
+
+ assert_equals_uint64 (((GstTimedValue *) values->next->data)->timestamp,
+ 20 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->next->data)->value, 1.0);
+ g_list_free (values);
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+ assert_equals_int (g_list_length (values), 2);
+ assert_equals_uint64 (((GstTimedValue *) values->data)->timestamp,
+ 10 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->data)->value, 0.0);
+
+ assert_equals_uint64 (((GstTimedValue *) values->next->data)->timestamp,
+ 15 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->next->data)->value, 0.50);
+ g_list_free (values);
+
+ CHECK_OBJECT_PROPS (clip, 0 * GST_SECOND, 10 * GST_SECOND, 5 * GST_SECOND);
+ check_layer (clip, 0);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_split_direct_absolute_bindings)
+{
+ GList *values;
+ GstControlSource *source;
+ GESTimeline *timeline;
+ GESClip *clip, *splitclip;
+ GstControlBinding *binding = NULL, *splitbinding;
+ GstTimedValueControlSource *splitsource;
+ GESLayer *layer;
+ GESAsset *asset;
+ GValue *tmpvalue;
+
+ GESTrackElement *element;
+
+ ges_init ();
+
+ fail_unless ((timeline = ges_timeline_new ()));
+ fail_unless ((layer = ges_layer_new ()));
+ fail_unless (ges_timeline_add_track (timeline,
+ GES_TRACK (ges_video_track_new ())));
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ clip = ges_layer_add_asset (layer, asset, 0, 10 * GST_SECOND, 10 * GST_SECOND,
+ GES_TRACK_TYPE_UNKNOWN);
+ g_object_unref (asset);
+
+ CHECK_OBJECT_PROPS (clip, 0 * GST_SECOND, 10 * GST_SECOND, 10 * GST_SECOND);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ check_layer (clip, 0);
+
+ source = gst_interpolation_control_source_new ();
+ g_object_set (source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+ element = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (ges_track_element_set_control_source (element,
+ source, "posx", "direct-absolute"));
+
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE (source),
+ 10 * GST_SECOND, 0);
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE (source),
+ 20 * GST_SECOND, 500);
+
+ binding = ges_track_element_get_control_binding (element, "posx");
+ tmpvalue = gst_control_binding_get_value (binding, 10 * GST_SECOND);
+ assert_equals_int (g_value_get_int (tmpvalue), 0);
+ g_value_unset (tmpvalue);
+ g_free (tmpvalue);
+
+ tmpvalue = gst_control_binding_get_value (binding, 20 * GST_SECOND);
+ assert_equals_int (g_value_get_int (tmpvalue), 500);
+ g_value_unset (tmpvalue);
+ g_free (tmpvalue);
+
+ splitclip = ges_clip_split (clip, 5 * GST_SECOND);
+ CHECK_OBJECT_PROPS (splitclip, 5 * GST_SECOND, 15 * GST_SECOND,
+ 5 * GST_SECOND);
+ check_layer (splitclip, 0);
+
+ splitbinding =
+ ges_track_element_get_control_binding (GES_CONTAINER_CHILDREN
+ (splitclip)->data, "posx");
+ g_object_get (splitbinding, "control_source", &splitsource, NULL);
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (splitsource));
+ assert_equals_int (g_list_length (values), 2);
+ assert_equals_uint64 (((GstTimedValue *) values->data)->timestamp,
+ 15 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->data)->value, 250.0);
+
+ assert_equals_uint64 (((GstTimedValue *) values->next->data)->timestamp,
+ 20 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->next->data)->value, 500.0);
+ g_list_free (values);
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+ assert_equals_int (g_list_length (values), 2);
+ assert_equals_uint64 (((GstTimedValue *) values->data)->timestamp,
+ 10 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->data)->value, 0.0);
+
+ assert_equals_uint64 (((GstTimedValue *) values->next->data)->timestamp,
+ 15 * GST_SECOND);
+ assert_equals_float (((GstTimedValue *) values->next->data)->value, 250.0);
+ g_list_free (values);
+
+ CHECK_OBJECT_PROPS (clip, 0 * GST_SECOND, 10 * GST_SECOND, 5 * GST_SECOND);
+ check_layer (clip, 0);
+
+ gst_object_unref (timeline);
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_split_object)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESClip *clip, *splitclip;
+ GList *splittrackelements;
+ GESTrackElement *trackelement, *splittrackelement;
+
+ ges_init ();
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ timeline = ges_timeline_new_audio_video ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ fail_unless (clip != NULL);
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 50,
+ "in-point", (guint64) 12, NULL);
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 50);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 2);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
+ GES_TIMELINE_ELEMENT (clip));
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 50);
+ assert_equals_uint64 (_INPOINT (trackelement), 12);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 50, 12,
+ 50, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ splitclip = ges_clip_split (clip, 67);
+ fail_unless (GES_IS_CLIP (splitclip));
+
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 25);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ assert_equals_uint64 (_START (splitclip), 67);
+ assert_equals_uint64 (_DURATION (splitclip), 25);
+ assert_equals_uint64 (_INPOINT (splitclip), 37);
+
+ splittrackelements = GES_CONTAINER_CHILDREN (splitclip);
+ fail_unless_equals_int (g_list_length (splittrackelements), 2);
+
+ splittrackelement = GES_TRACK_ELEMENT (splittrackelements->data);
+ fail_unless (GES_IS_TRACK_ELEMENT (splittrackelement));
+ assert_equals_uint64 (_START (splittrackelement), 67);
+ assert_equals_uint64 (_DURATION (splittrackelement), 25);
+ assert_equals_uint64 (_INPOINT (splittrackelement), 37);
+
+ fail_unless (splittrackelement != trackelement);
+ fail_unless (splitclip != clip);
+
+ splittrackelement = GES_TRACK_ELEMENT (splittrackelements->next->data);
+ fail_unless (GES_IS_TRACK_ELEMENT (splittrackelement));
+ assert_equals_uint64 (_START (splittrackelement), 67);
+ assert_equals_uint64 (_DURATION (splittrackelement), 25);
+ assert_equals_uint64 (_INPOINT (splittrackelement), 37);
+
+ fail_unless (splittrackelement != trackelement);
+ fail_unless (splitclip != clip);
+
+ /* We own the only ref */
+ ASSERT_OBJECT_REFCOUNT (splitclip, "1 ref for us + 1 for the timeline", 2);
+ /* 1 ref for the Clip, 1 ref for the Track and 2 ref for the timeline
+ * (1 for the "all_element" hashtable, another for the sequence of TrackElement*/
+ ASSERT_OBJECT_REFCOUNT (splittrackelement,
+ "1 ref for the Clip, 1 ref for the Track and 1 ref for the timeline", 3);
+
+ check_destroyed (G_OBJECT (timeline), G_OBJECT (splitclip), clip,
+ splittrackelement, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_clip_group_ungroup)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESClip *clip, *clip2;
+ GList *containers, *tmp;
+ GESLayer *layer;
+ GESContainer *regrouped_clip;
+ GESTrack *audio_track, *video_track;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ audio_track = GES_TRACK (ges_audio_track_new ());
+ video_track = GES_TRACK (ges_video_track_new ());
+
+ fail_unless (ges_timeline_add_track (timeline, audio_track));
+ fail_unless (ges_timeline_add_track (timeline, video_track));
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ assert_is_type (asset, GES_TYPE_ASSET);
+
+ clip = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ ASSERT_OBJECT_REFCOUNT (clip, "1 layer + 1 timeline.all_elements", 2);
+ assert_equals_uint64 (_START (clip), 0);
+ assert_equals_uint64 (_INPOINT (clip), 0);
+ assert_equals_uint64 (_DURATION (clip), 10);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 2);
+
+ containers = ges_container_ungroup (GES_CONTAINER (clip), FALSE);
+ assert_equals_int (g_list_length (containers), 2);
+ fail_unless (clip == containers->data);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ assert_equals_uint64 (_START (clip), 0);
+ assert_equals_uint64 (_INPOINT (clip), 0);
+ assert_equals_uint64 (_DURATION (clip), 10);
+ ASSERT_OBJECT_REFCOUNT (clip, "1 for the layer + 1 for the timeline + "
+ "1 in containers list", 3);
+
+ clip2 = containers->next->data;
+ fail_if (clip2 == clip);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (clip2) != NULL);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip2)), 1);
+ assert_equals_uint64 (_START (clip2), 0);
+ assert_equals_uint64 (_INPOINT (clip2), 0);
+ assert_equals_uint64 (_DURATION (clip2), 10);
+ ASSERT_OBJECT_REFCOUNT (clip2, "1 for the layer + 1 for the timeline +"
+ " 1 in containers list", 3);
+
+ tmp = ges_track_get_elements (audio_track);
+ assert_equals_int (g_list_length (tmp), 1);
+ ASSERT_OBJECT_REFCOUNT (tmp->data, "1 for the track + 1 for the container "
+ "+ 1 for the timeline + 1 in tmp list", 4);
+ assert_equals_int (ges_track_element_get_track_type (tmp->data),
+ GES_TRACK_TYPE_AUDIO);
+ assert_equals_int (ges_clip_get_supported_formats (GES_CLIP
+ (ges_timeline_element_get_parent (tmp->data))), GES_TRACK_TYPE_AUDIO);
+ g_list_free_full (tmp, gst_object_unref);
+ tmp = ges_track_get_elements (video_track);
+ assert_equals_int (g_list_length (tmp), 1);
+ ASSERT_OBJECT_REFCOUNT (tmp->data, "1 for the track + 1 for the container "
+ "+ 1 for the timeline + 1 in tmp list", 4);
+ assert_equals_int (ges_track_element_get_track_type (tmp->data),
+ GES_TRACK_TYPE_VIDEO);
+ assert_equals_int (ges_clip_get_supported_formats (GES_CLIP
+ (ges_timeline_element_get_parent (tmp->data))), GES_TRACK_TYPE_VIDEO);
+ g_list_free_full (tmp, gst_object_unref);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 10);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ assert_equals_uint64 (_START (clip), 10);
+ assert_equals_uint64 (_INPOINT (clip), 0);
+ assert_equals_uint64 (_DURATION (clip), 10);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip2)), 1);
+ assert_equals_uint64 (_START (clip2), 0);
+ assert_equals_uint64 (_INPOINT (clip2), 0);
+ assert_equals_uint64 (_DURATION (clip2), 10);
+
+ regrouped_clip = ges_container_group (containers);
+ fail_unless (GES_IS_GROUP (regrouped_clip));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (regrouped_clip)),
+ 2);
+ tmp = ges_container_ungroup (regrouped_clip, FALSE);
+ g_list_free_full (tmp, gst_object_unref);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 0);
+ regrouped_clip = ges_container_group (containers);
+ assert_is_type (regrouped_clip, GES_TYPE_CLIP);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (regrouped_clip)),
+ 2);
+ assert_equals_int (ges_clip_get_supported_formats (GES_CLIP (regrouped_clip)),
+ GES_TRACK_TYPE_VIDEO | GES_TRACK_TYPE_AUDIO);
+ g_list_free_full (containers, gst_object_unref);
+
+ GST_DEBUG ("Check clips in the layer");
+ tmp = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (tmp), 1);
+ g_list_free_full (tmp, gst_object_unref);
+
+ GST_DEBUG ("Check TrackElement in audio track");
+ tmp = ges_track_get_elements (audio_track);
+ assert_equals_int (g_list_length (tmp), 1);
+ assert_equals_int (ges_track_element_get_track_type (tmp->data),
+ GES_TRACK_TYPE_AUDIO);
+ fail_unless (GES_CONTAINER (ges_timeline_element_get_parent (tmp->data)) ==
+ regrouped_clip);
+ g_list_free_full (tmp, gst_object_unref);
+
+ GST_DEBUG ("Check TrackElement in video track");
+ tmp = ges_track_get_elements (video_track);
+ assert_equals_int (g_list_length (tmp), 1);
+ ASSERT_OBJECT_REFCOUNT (tmp->data, "1 for the track + 1 for the container "
+ "+ 1 for the timeline + 1 in tmp list", 4);
+ assert_equals_int (ges_track_element_get_track_type (tmp->data),
+ GES_TRACK_TYPE_VIDEO);
+ fail_unless (GES_CONTAINER (ges_timeline_element_get_parent (tmp->data)) ==
+ regrouped_clip);
+ g_list_free_full (tmp, gst_object_unref);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+static void
+child_removed_cb (GESClip * clip, GESTimelineElement * effect,
+ gboolean * called)
+{
+ ASSERT_OBJECT_REFCOUNT (effect, "Keeping alive ref + emission ref", 2);
+ *called = TRUE;
+}
+
+GST_START_TEST (test_clip_refcount_remove_child)
+{
+ GESClip *clip;
+ GESTrack *track;
+ gboolean called;
+ GESTrackElement *effect;
+
+ ges_init ();
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ track = GES_TRACK (ges_audio_track_new ());
+ effect = GES_TRACK_ELEMENT (ges_effect_new ("identity"));
+
+ fail_unless (ges_track_add_element (track, effect));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect)));
+ ASSERT_OBJECT_REFCOUNT (effect, "1 for the container + 1 for the track", 2);
+
+ fail_unless (ges_track_remove_element (track, effect));
+ ASSERT_OBJECT_REFCOUNT (effect, "1 for the container + 1 for the track", 1);
+
+ g_signal_connect (clip, "child-removed", G_CALLBACK (child_removed_cb),
+ &called);
+ fail_unless (ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (called == TRUE);
+
+ check_destroyed (G_OBJECT (track), NULL, NULL);
+ check_destroyed (G_OBJECT (clip), NULL, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_clip_find_track_element)
+{
+ GESClip *clip;
+ GList *foundelements;
+ GESTimeline *timeline;
+ GESTrack *track, *track1, *track2;
+
+ GESTrackElement *effect, *effect1, *effect2, *foundelem;
+
+ ges_init ();
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ track = GES_TRACK (ges_audio_track_new ());
+ track1 = GES_TRACK (ges_audio_track_new ());
+ track2 = GES_TRACK (ges_video_track_new ());
+
+ timeline = ges_timeline_new ();
+ fail_unless (ges_timeline_add_track (timeline, track));
+ fail_unless (ges_timeline_add_track (timeline, track1));
+ fail_unless (ges_timeline_add_track (timeline, track2));
+
+ effect = GES_TRACK_ELEMENT (ges_effect_new ("identity"));
+ fail_unless (ges_track_add_element (track, effect));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect)));
+
+ effect1 = GES_TRACK_ELEMENT (ges_effect_new ("identity"));
+ fail_unless (ges_track_add_element (track1, effect1));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect1)));
+
+ effect2 = GES_TRACK_ELEMENT (ges_effect_new ("identity"));
+ fail_unless (ges_track_add_element (track2, effect2));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect2)));
+
+ foundelem = ges_clip_find_track_element (clip, track, G_TYPE_NONE);
+ fail_unless (foundelem == effect);
+ gst_object_unref (foundelem);
+
+ foundelem = ges_clip_find_track_element (clip, NULL, GES_TYPE_SOURCE);
+ fail_unless (foundelem == NULL);
+
+ foundelements = ges_clip_find_track_elements (clip, NULL,
+ GES_TRACK_TYPE_AUDIO, G_TYPE_NONE);
+ fail_unless_equals_int (g_list_length (foundelements), 2);
+ g_list_free_full (foundelements, gst_object_unref);
+
+ foundelements = ges_clip_find_track_elements (clip, NULL,
+ GES_TRACK_TYPE_VIDEO, G_TYPE_NONE);
+ fail_unless_equals_int (g_list_length (foundelements), 1);
+ g_list_free_full (foundelements, gst_object_unref);
+
+ foundelements = ges_clip_find_track_elements (clip, track,
+ GES_TRACK_TYPE_VIDEO, G_TYPE_NONE);
+ fail_unless_equals_int (g_list_length (foundelements), 2);
+ fail_unless (g_list_find (foundelements, effect2) != NULL,
+ "In the video track");
+ fail_unless (g_list_find (foundelements, effect2) != NULL, "In 'track'");
+ g_list_free_full (foundelements, gst_object_unref);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_effects_priorities)
+{
+ GESClip *clip;
+ GESTimeline *timeline;
+ GESTrack *audio_track, *video_track;
+ GESLayer *layer, *layer1;
+
+ GESTrackElement *effect, *effect1, *effect2;
+
+ ges_init ();
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ audio_track = GES_TRACK (ges_audio_track_new ());
+ video_track = GES_TRACK (ges_video_track_new ());
+
+ timeline = ges_timeline_new ();
+ fail_unless (ges_timeline_add_track (timeline, audio_track));
+ fail_unless (ges_timeline_add_track (timeline, video_track));
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+
+ ges_layer_add_clip (layer, clip);
+
+ effect = GES_TRACK_ELEMENT (ges_effect_new ("agingtv"));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect)));
+
+ effect1 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv"));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect1)));
+
+ effect2 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv"));
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect2)));
+
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0,
+ _PRIORITY (effect));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1,
+ _PRIORITY (effect1));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2,
+ _PRIORITY (effect2));
+
+ fail_unless (ges_clip_set_top_effect_index (clip, GES_BASE_EFFECT (effect),
+ 2));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0,
+ _PRIORITY (effect1));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1,
+ _PRIORITY (effect2));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2,
+ _PRIORITY (effect));
+
+ fail_unless (ges_clip_set_top_effect_index (clip, GES_BASE_EFFECT (effect),
+ 0));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0,
+ _PRIORITY (effect));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1,
+ _PRIORITY (effect1));
+ fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2,
+ _PRIORITY (effect2));
+
+ fail_unless (ges_clip_move_to_layer (clip, layer1));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0,
+ _PRIORITY (effect));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1,
+ _PRIORITY (effect1));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2,
+ _PRIORITY (effect2));
+
+ fail_unless (ges_clip_set_top_effect_index (clip, GES_BASE_EFFECT (effect),
+ 2));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0,
+ _PRIORITY (effect1));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1,
+ _PRIORITY (effect2));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2,
+ _PRIORITY (effect));
+
+ fail_unless (ges_clip_set_top_effect_index (clip, GES_BASE_EFFECT (effect),
+ 0));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0,
+ _PRIORITY (effect));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1,
+ _PRIORITY (effect1));
+ fail_unless_equals_int (LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2,
+ _PRIORITY (effect2));
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-clip");
+ TCase *tc_chain = tcase_create ("clip");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_object_properties);
+ tcase_add_test (tc_chain, test_split_object);
+ tcase_add_test (tc_chain, test_split_direct_bindings);
+ tcase_add_test (tc_chain, test_split_direct_absolute_bindings);
+ tcase_add_test (tc_chain, test_clip_group_ungroup);
+ tcase_add_test (tc_chain, test_clip_refcount_remove_child);
+ tcase_add_test (tc_chain, test_clip_find_track_element);
+ tcase_add_test (tc_chain, test_effects_priorities);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+void
+deep_prop_changed_cb (GESTrackElement * track_element, GstElement * element,
+ GParamSpec * spec);
+
+GST_START_TEST (test_effect_basic)
+{
+ GESEffect *effect;
+
+ ges_init ();
+
+ effect = ges_effect_new ("agingtv");
+ fail_unless (effect != NULL);
+ gst_object_unref (effect);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_add_effect_to_clip)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_audio, *track_video;
+ GESEffect *effect;
+ GESTestClip *source;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_audio = GES_TRACK (ges_audio_track_new ());
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, track_audio);
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ source = ges_test_clip_new ();
+
+ g_object_set (source, "duration", 10 * GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) source);
+
+
+ GST_DEBUG ("Create effect");
+ effect = ges_effect_new ("agingtv");
+
+ fail_unless (GES_IS_EFFECT (effect));
+ fail_unless (ges_container_add (GES_CONTAINER (source),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) !=
+ NULL);
+
+ assert_equals_int (GES_TRACK_ELEMENT (effect)->active, TRUE);
+
+ ges_layer_remove_clip (layer, (GESClip *) source);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_get_effects_from_tl)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_video;
+ GESTrackElement *video_source;
+ GESEffect *effect, *effect1, *effect2;
+ GESTestClip *source;
+ GList *effects, *tmp = NULL;
+ gint effect_prio = -1;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ source = ges_test_clip_new ();
+
+ g_object_set (source, "duration", 10 * GST_SECOND, NULL);
+
+ GST_DEBUG ("Adding source to layer");
+ ges_layer_add_clip (layer, (GESClip *) source);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (source)), 1);
+ video_source = GES_CONTAINER_CHILDREN (source)->data;
+ fail_unless (GES_IS_VIDEO_TEST_SOURCE (video_source));
+ assert_equals_int (_PRIORITY (video_source),
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+
+ GST_DEBUG ("Create effect");
+ effect = ges_effect_new ("agingtv");
+ effect1 = ges_effect_new ("agingtv");
+ effect2 = ges_effect_new ("agingtv");
+
+ fail_unless (GES_IS_EFFECT (effect));
+ fail_unless (GES_IS_EFFECT (effect1));
+ fail_unless (GES_IS_EFFECT (effect2));
+
+ GST_DEBUG ("Adding effect (0)");
+ fail_unless (ges_container_add (GES_CONTAINER (source),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) ==
+ track_video);
+ assert_equals_int (_PRIORITY (effect), MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0);
+ assert_equals_int (_PRIORITY (video_source),
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1);
+
+ GST_DEBUG ("Adding effect 1");
+ fail_unless (ges_container_add (GES_CONTAINER (source),
+ GES_TIMELINE_ELEMENT (effect1)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect1)) ==
+ track_video);
+ assert_equals_int (_PRIORITY (effect), MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (_PRIORITY (effect1),
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1);
+ assert_equals_int (_PRIORITY (video_source),
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 2);
+
+ GST_DEBUG ("Adding effect 2");
+ fail_unless (ges_container_add (GES_CONTAINER (source),
+ GES_TIMELINE_ELEMENT (effect2)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect2)) ==
+ track_video);
+ assert_equals_int (GES_CONTAINER_HEIGHT (source), 4);
+
+ effects = ges_clip_get_top_effects (GES_CLIP (source));
+ fail_unless (g_list_length (effects) == 3);
+ for (tmp = effects; tmp; tmp = tmp->next) {
+ gint priority = ges_clip_get_top_effect_position (GES_CLIP (source),
+ GES_BASE_EFFECT (tmp->data));
+ fail_unless (priority > effect_prio);
+ fail_unless (GES_IS_EFFECT (tmp->data));
+ effect_prio = priority;
+
+ gst_object_unref (tmp->data);
+ }
+ g_list_free (effects);
+
+ ges_layer_remove_clip (layer, (GESClip *) source);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_effect_clip)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_audio, *track_video;
+ GESEffectClip *effect_clip;
+ GESEffect *effect, *effect1;
+ GList *effects, *tmp;
+ gint i, clip_height;
+ gint effect_prio = -1;
+ /* FIXME the order of track type is not well defined */
+ guint track_type[4] = { GES_TRACK_TYPE_AUDIO,
+ GES_TRACK_TYPE_VIDEO, GES_TRACK_TYPE_VIDEO,
+ GES_TRACK_TYPE_AUDIO
+ };
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_audio = GES_TRACK (ges_audio_track_new ());
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, track_audio);
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ GST_DEBUG ("Create effect");
+ effect_clip = ges_effect_clip_new ("agingtv", "audiopanorama");
+
+ g_object_set (effect_clip, "duration", 25 * GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) effect_clip);
+
+ effect = ges_effect_new ("agingtv");
+ fail_unless (ges_container_add (GES_CONTAINER (effect_clip),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) ==
+ track_video);
+
+ g_object_get (effect_clip, "height", &clip_height, NULL);
+ assert_equals_int (clip_height, 3);
+
+ effect1 = ges_effect_new ("audiopanorama");
+ fail_unless (ges_container_add (GES_CONTAINER (effect_clip),
+ GES_TIMELINE_ELEMENT (effect1)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect1)) ==
+ track_audio);
+
+ g_object_get (effect_clip, "height", &clip_height, NULL);
+ assert_equals_int (clip_height, 4);
+
+ effects = ges_clip_get_top_effects (GES_CLIP (effect_clip));
+ for (tmp = effects, i = 0; tmp; tmp = tmp->next, i++) {
+ gint priority = ges_clip_get_top_effect_position (GES_CLIP (effect_clip),
+ GES_BASE_EFFECT (tmp->data));
+ fail_unless (priority > effect_prio);
+ fail_unless (GES_IS_EFFECT (tmp->data));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (tmp->data))->
+ type == track_type[i]);
+ effect_prio = priority;
+
+ gst_object_unref (tmp->data);
+ }
+ g_list_free (effects);
+
+ ges_layer_remove_clip (layer, (GESClip *) effect_clip);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_priorities_clip)
+{
+ GList *effects, *tmp;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESEffectClip *effect_clip;
+ GESTrack *track_audio, *track_video;
+ GESEffect *effect, *effect1, *audio_effect = NULL, *video_effect = NULL;
+
+ gint effect_prio = -1;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_audio = GES_TRACK (ges_audio_track_new ());
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, track_audio);
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ GST_DEBUG ("Create effect");
+ effect_clip = ges_effect_clip_new ("agingtv", "audiopanorama");
+
+ g_object_set (effect_clip, "duration", 25 * GST_SECOND, NULL);
+
+ ges_layer_add_clip ((layer), (GESClip *) effect_clip);
+ for (tmp = GES_CONTAINER_CHILDREN (effect_clip); tmp; tmp = tmp->next) {
+ if (ges_track_element_get_track_type (GES_TRACK_ELEMENT (tmp->data)) ==
+ GES_TRACK_TYPE_AUDIO)
+ audio_effect = tmp->data;
+ else if (ges_track_element_get_track_type (GES_TRACK_ELEMENT (tmp->data)) ==
+ GES_TRACK_TYPE_VIDEO)
+ video_effect = tmp->data;
+ else
+ g_assert (0);
+ }
+ fail_unless (GES_IS_EFFECT (audio_effect));
+ fail_unless (GES_IS_EFFECT (video_effect));
+
+ /* FIXME This is ridiculus, both effects should have the same priority */
+ assert_equals_int (_PRIORITY (audio_effect),
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (_PRIORITY (video_effect),
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1);
+ assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 2);
+
+ effect = ges_effect_new ("agingtv");
+ GST_DEBUG ("Adding effect to the effect clip %" GST_PTR_FORMAT, effect);
+ fail_unless (ges_container_add (GES_CONTAINER (effect_clip),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) ==
+ track_video);
+ assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 3);
+
+ effect1 = ges_effect_new ("audiopanorama");
+ GST_DEBUG ("Adding effect1 to the effect clip %" GST_PTR_FORMAT, effect1);
+ fail_unless (ges_container_add (GES_CONTAINER (effect_clip),
+ GES_TIMELINE_ELEMENT (effect1)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect1)) ==
+ track_audio);
+
+ fail_unless (ges_clip_set_top_effect_priority (GES_CLIP (effect_clip),
+ GES_BASE_EFFECT (effect1), 0));
+ assert_equals_int (_PRIORITY (effect_clip), 1);
+
+ assert_equals_int (_PRIORITY (effect), 3 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (_PRIORITY (effect1),
+ 0 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+
+ assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 4);
+
+ fail_unless (ges_clip_set_top_effect_priority (GES_CLIP (effect_clip),
+ GES_BASE_EFFECT (effect1), 3));
+ assert_equals_int (_PRIORITY (effect), 2 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (_PRIORITY (effect1),
+ 3 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 4);
+
+ effects = ges_clip_get_top_effects (GES_CLIP (effect_clip));
+ for (tmp = effects; tmp; tmp = tmp->next) {
+ gint priority = ges_clip_get_top_effect_position (GES_CLIP (effect_clip),
+ GES_BASE_EFFECT (tmp->data));
+ fail_unless (priority > effect_prio);
+ fail_unless (GES_IS_EFFECT (tmp->data));
+ effect_prio = priority;
+
+ gst_object_unref (tmp->data);
+ }
+ g_list_free (effects);
+
+ assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 4);
+ effects = ges_clip_get_top_effects (GES_CLIP (effect_clip));
+ effect_prio = 0;
+ for (tmp = effects; tmp; tmp = tmp->next) {
+ gint priority = ges_clip_get_top_effect_position (GES_CLIP (effect_clip),
+ GES_BASE_EFFECT (tmp->data));
+ fail_unless (priority >= effect_prio, "%d >= %d", priority, effect_prio);
+ fail_unless (GES_IS_EFFECT (tmp->data));
+ effect_prio = priority;
+
+ gst_object_unref (tmp->data);
+ }
+ g_list_free (effects);
+
+ ges_layer_remove_clip (layer, (GESClip *) effect_clip);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_effect_set_properties)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_video;
+ GESEffectClip *effect_clip;
+ GESTimelineElement *effect;
+ guint scratch_line, n_props, i;
+ gboolean color_aging;
+ GParamSpec **pspecs, *spec;
+ GValue val = { 0 };
+ GValue nval = { 0 };
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ GST_DEBUG ("Create effect");
+ effect_clip = ges_effect_clip_new ("agingtv", NULL);
+
+ g_object_set (effect_clip, "duration", 25 * GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) effect_clip);
+
+ effect = GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv"));
+ fail_unless (ges_container_add (GES_CONTAINER (effect_clip),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) ==
+ track_video);
+
+ ges_timeline_element_set_child_properties (effect,
+ "GstAgingTV::scratch-lines", 17, "color-aging", FALSE, NULL);
+ ges_timeline_element_get_child_properties (effect,
+ "GstAgingTV::scratch-lines", &scratch_line,
+ "color-aging", &color_aging, NULL);
+ fail_unless (scratch_line == 17);
+ fail_unless (color_aging == FALSE);
+
+ pspecs = ges_timeline_element_list_children_properties (effect, &n_props);
+ fail_unless (n_props == 7);
+
+ spec = pspecs[0];
+ i = 1;
+ while (g_strcmp0 (spec->name, "scratch-lines")) {
+ spec = pspecs[i++];
+ }
+
+ g_value_init (&val, G_TYPE_UINT);
+ g_value_init (&nval, G_TYPE_UINT);
+ g_value_set_uint (&val, 10);
+
+ ges_timeline_element_set_child_property_by_pspec (effect, spec, &val);
+ ges_timeline_element_get_child_property_by_pspec (effect, spec, &nval);
+ fail_unless (g_value_get_uint (&nval) == 10);
+
+ for (i = 0; i < n_props; i++) {
+ g_param_spec_unref (pspecs[i]);
+ }
+ g_free (pspecs);
+
+ ges_layer_remove_clip (layer, (GESClip *) effect_clip);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+effect_added_cb (GESClip * clip, GESBaseEffect * trop, gboolean * effect_added)
+{
+ GST_DEBUG ("Effect added");
+ fail_unless (GES_IS_CLIP (clip));
+ fail_unless (GES_IS_EFFECT (trop));
+ *effect_added = TRUE;
+}
+
+void
+deep_prop_changed_cb (GESTrackElement * track_element, GstElement * element,
+ GParamSpec * spec)
+{
+ GST_DEBUG ("%s property changed", g_param_spec_get_name (spec));
+ fail_unless (GES_IS_TRACK_ELEMENT (track_element));
+ fail_unless (GST_IS_ELEMENT (element));
+}
+
+GST_START_TEST (test_clip_signals)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_video;
+ GESEffectClip *effect_clip;
+ GESTimelineElement *effect;
+ GValue val = { 0, };
+ gboolean effect_added = FALSE;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ GST_DEBUG ("Create effect");
+ effect_clip = ges_effect_clip_new ("agingtv", NULL);
+ g_signal_connect (effect_clip, "child-added", (GCallback) effect_added_cb,
+ &effect_added);
+
+ g_object_set (effect_clip, "duration", 25 * GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) effect_clip);
+
+ effect = GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv"));
+ fail_unless (ges_container_add (GES_CONTAINER (effect_clip), effect));
+ fail_unless (effect_added);
+ g_signal_handlers_disconnect_by_func (effect_clip, effect_added_cb,
+ &effect_added);
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) ==
+ track_video);
+ g_signal_connect (effect, "deep-notify", (GCallback) deep_prop_changed_cb,
+ effect);
+
+ ges_timeline_element_set_child_properties (effect,
+ "GstAgingTV::scratch-lines", 17, NULL);
+
+ g_value_init (&val, G_TYPE_UINT);
+ ges_timeline_element_get_child_property (effect,
+ "GstAgingTV::scratch-lines", &val);
+ fail_unless (G_VALUE_HOLDS_UINT (&val));
+ g_value_unset (&val);
+
+ ges_layer_remove_clip (layer, (GESClip *) effect_clip);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_split_clip_effect_priorities)
+{
+ GESLayer *layer;
+ GESTimeline *timeline;
+ GESTrack *track_video;
+ GESClip *clip, *nclip;
+ GESEffect *effect;
+ GESTrackElement *source, *nsource, *neffect;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_video = GES_TRACK (ges_video_track_new ());
+
+ g_object_set (timeline, "auto-transition", TRUE, NULL);
+ ges_timeline_add_track (timeline, track_video);
+ ges_timeline_add_layer (timeline, layer);
+
+ GST_DEBUG ("Create effect");
+ effect = ges_effect_new ("agingtv");
+ clip = GES_CLIP (ges_test_clip_new ());
+ g_object_set (clip, "duration", GST_SECOND * 2, NULL);
+
+ fail_unless (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (effect)));
+ ges_layer_add_clip (layer, clip);
+
+ source = ges_clip_find_track_element (clip, NULL, GES_TYPE_VIDEO_SOURCE);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (effect), 3);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (source), 4);
+
+ nclip = ges_clip_split (clip, GST_SECOND);
+ neffect = ges_clip_find_track_element (nclip, NULL, GES_TYPE_EFFECT);
+ nsource = ges_clip_find_track_element (nclip, NULL, GES_TYPE_VIDEO_SOURCE);
+
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (effect), 3);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (source), 4);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (neffect), 5);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (nsource), 6);
+
+ /* Create a transition ... */
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), GST_SECOND / 2);
+
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (effect), 3);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (source), 4);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (neffect), 5);
+ assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (nsource), 6);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges");
+ TCase *tc_chain = tcase_create ("effect");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_effect_basic);
+ tcase_add_test (tc_chain, test_add_effect_to_clip);
+ tcase_add_test (tc_chain, test_get_effects_from_tl);
+ tcase_add_test (tc_chain, test_effect_clip);
+ tcase_add_test (tc_chain, test_priorities_clip);
+ tcase_add_test (tc_chain, test_effect_set_properties);
+ tcase_add_test (tc_chain, test_clip_signals);
+ tcase_add_test (tc_chain, test_split_clip_effect_priorities);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+GST_START_TEST (test_move_group)
+{
+ GESAsset *asset;
+ GESGroup *group;
+ GESTimeline *timeline;
+ GESLayer *layer, *layer1;
+ GESClip *clip, *clip1, *clip2;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ /**
+ * Our timeline:
+ * -------------
+ *
+ * 0------------Group1---------------110
+ * |-------- |
+ * layer: | clip | |
+ * |-------10 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 10--------20 50----------|
+ * |----------------------------------|
+ */
+ clip = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clip1 =
+ ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clip2 =
+ ges_layer_add_asset (layer1, asset, 50, 0, 60, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, clip);
+ clips = g_list_prepend (clips, clip1);
+ clips = g_list_prepend (clips, clip2);
+ group = GES_GROUP (ges_container_group (clips));
+ g_list_free (clips);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+
+ fail_unless (GES_IS_GROUP (group));
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+ fail_unless (g_list_length (GES_CONTAINER_CHILDREN (group)) == 3);
+ assert_equals_int (GES_CONTAINER_HEIGHT (group), 2);
+
+ /* Nothing should move */
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip1), 5);
+
+ CHECK_OBJECT_PROPS (clip, 0, 0, 10);
+ CHECK_OBJECT_PROPS (clip1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 50, 0, 60);
+ CHECK_OBJECT_PROPS (group, 0, 0, 110);
+
+ /*
+ * 0 10------------Group1---------------120
+ * |-------- |
+ * layer: | clip | |
+ * |-------20 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 10);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 10);
+ CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 60);
+ CHECK_OBJECT_PROPS (group, 10, 0, 110);
+
+ /*
+ * 0 10------------Group1---------------120
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 5);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 60);
+ CHECK_OBJECT_PROPS (group, 10, 0, 110);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip2), 50);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 5--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip1), 5);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 5--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip1), 5);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+ fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 20));
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+
+ fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25));
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+
+ /* Same thing in the end... */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+
+ /*
+ * 0 12------------Group1---------------110
+ * 2------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 7--------- 2----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 22--------30 62----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 12);
+ CHECK_OBJECT_PROPS (clip, 12, 2, 3);
+ CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
+ CHECK_OBJECT_PROPS (clip2, 62, 2, 48);
+ CHECK_OBJECT_PROPS (group, 12, 0, 98);
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+
+ /* Setting the duration would lead to overlaps */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 10);
+ CHECK_OBJECT_PROPS (clip, 12, 2, 3);
+ CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
+ CHECK_OBJECT_PROPS (clip2, 62, 2, 48);
+ CHECK_OBJECT_PROPS (group, 12, 0, 98);
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100);
+ CHECK_OBJECT_PROPS (clip, 12, 2, 3);
+ CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
+ CHECK_OBJECT_PROPS (clip2, 62, 2, 50);
+ CHECK_OBJECT_PROPS (group, 12, 0, 100);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20);
+ CHECK_OBJECT_PROPS (clip, 20, 2, 3);
+ CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
+ CHECK_OBJECT_PROPS (clip2, 70, 2, 50);
+ CHECK_OBJECT_PROPS (group, 20, 0, 100);
+
+ fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10));
+ CHECK_OBJECT_PROPS (clip, 20, 2, 3);
+ CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
+ CHECK_OBJECT_PROPS (clip2, 70, 2, 50);
+ CHECK_OBJECT_PROPS (group, 20, 0, 100);
+
+ ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
+ check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL);
+ gst_object_unref (asset);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+
+static void
+_changed_layer_cb (GESTimelineElement * clip,
+ GParamSpec * arg G_GNUC_UNUSED, guint * nb_calls)
+{
+ *nb_calls = *nb_calls + 1;
+}
+
+GST_START_TEST (test_group_in_group)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group, *group1;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1, *c2, *c3, *c4, *c5;
+
+ guint nb_layer_notifies = 0;
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20---------------30-------Group1----------70
+ * | +-----------+ |+-----------50 |
+ * L | | C | || C3 | |
+ * | +-----------+ |+-----------+ |
+ * --|-------------------------------------------|-----40----------------|
+ * | +------------+ +-------------+ | +--------60 |
+ * L1 | | C1 | | C2 | | | C4 | |
+ * | +------------+ +-------------+ | +--------+ |
+ * --|-------------------------------------------|-----------------------|
+ * | | +--------+|
+ * L2 | | | c5 ||
+ * | | +--------+|
+ * --+-------------------------------------------+-----------------------+
+ *
+ * L3
+ *
+ * -----------------------------------------------------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c2 = ges_layer_add_asset (layer1, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ clips = g_list_prepend (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 10);
+ CHECK_OBJECT_PROPS (group, 0, 0, 30);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (NULL, c3);
+ clips = g_list_prepend (clips, c4);
+ clips = g_list_prepend (clips, c5);
+ group1 = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group1));
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group1, 30, 0, 40);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (ges_container_add (GES_CONTAINER (group),
+ GES_TIMELINE_ELEMENT (group1)));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group, 0, 0, 70);
+ CHECK_OBJECT_PROPS (group1, 30, 0, 40);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (c4), 50);
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 30, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 50, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 60, 0, 20);
+ CHECK_OBJECT_PROPS (group, 10, 0, 70);
+ CHECK_OBJECT_PROPS (group1, 40, 0, 40);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ /* Our timeline
+ *
+ * L
+ * -----------------------------------------------------------------------
+ * 0------------10-Group-----20---------------30-------Group1----------70
+ * | +-----------+ |+-----------50 |
+ * L1 | | C | || C3 | |
+ * | +-----------+ |+-----------+ |
+ * | | |
+ * --|-------------------------------------------|-----40----------------|
+ * | +------------+ +-------------+ | +--------60 |
+ * L2 | | C1 | | C2 | | | C4 | |
+ * | +------------+ +-------------+ | +--------+ |
+ * --|-------------------------------------------|-----------------------|
+ * | | +--------+|
+ * L3 | | | c5 ||
+ * | | +--------+|
+ * --+-------------------------------------------+-----------------------+
+ */
+ fail_unless (ges_clip_move_to_layer (c, layer1));
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+ assert_equals_int (_PRIORITY (group), 1);
+ assert_equals_int (_PRIORITY (group1), 1);
+
+ /* We can not move so far! */
+ g_signal_connect_after (c4, "notify::layer",
+ (GCallback) _changed_layer_cb, &nb_layer_notifies);
+ fail_if (ges_clip_move_to_layer (c4, layer));
+ assert_equals_int (nb_layer_notifies, 0);
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+ assert_equals_int (_PRIORITY (group), 1);
+ assert_equals_int (_PRIORITY (group1), 1);
+
+ clips = ges_container_ungroup (GES_CONTAINER (group), FALSE);
+ assert_equals_int (g_list_length (clips), 4);
+ g_list_free_full (clips, gst_object_unref);
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_group_in_group_layer_moving)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20
+ * | +-----------+ |
+ * L | | C | |
+ * | +-----------+ |
+ * --|--------------------------
+ * | +------------+
+ * L1 | | C1 |
+ * | +------------+
+ * -----------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ fail_unless (layer2 && layer3);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (group, 0, 0, 20);
+
+ /* Our timeline
+ *
+ * --0--------10-----------20-Group----30
+ * | +-----------+ |
+ * L | | C | |
+ * | +-----------+ |
+ * --|-----------------------------------
+ * | +------------+
+ * L1 | | C1 |
+ * | +------------+
+ * -------------------------------------
+ */
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL,
+ -1, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 10));
+
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (group, 10, 0, 20);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 0);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 1);
+
+ ges_layer_set_priority (layer2, 0);
+ ges_layer_set_priority (layer, 1);
+ ges_layer_set_priority (layer1, 2);
+
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 1);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 2);
+
+ /* Our timeline
+ *
+ * --0--------10-----------20-Group----30
+ * | +-----------+ |
+ * L2 | | C | |
+ * | +-----------+ |
+ * --|-----------------------------------
+ * | +------------+
+ * L | | C1 |
+ * | +------------+
+ * -------------------------------------
+ *
+ * L1
+ * -------------------------------------
+ */
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL,
+ 0, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 10));
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (group, 10, 0, 20);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 0);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 1);
+
+ /* Our timeline
+ *
+ * --0--------10-----------20-Group----30
+ * L2 | |
+ * --------------------------------------
+ * | +-----------+ |
+ * L | | C | |
+ * | +-----------+ |
+ * --|-----------------------------------
+ * | +------------+
+ * L1 | | C1 |
+ * | +------------+
+ * -------------------------------------
+ */
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL,
+ 1, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 10));
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (group, 10, 0, 20);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 1);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 2);
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_group_in_self)
+{
+ GESLayer *layer;
+ GESClip *c, *c1;
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ layer = ges_timeline_append_layer (timeline);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+
+
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_if (ges_container_add (GES_CONTAINER (group),
+ GES_TIMELINE_ELEMENT (group)));
+ clips = ges_container_get_children (GES_CONTAINER (group), TRUE);
+ assert_equals_int (g_list_length (clips), 6);
+ g_list_free_full (clips, g_object_unref);
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+project_loaded_cb (GESProject * project, GESTimeline * timeline,
+ GMainLoop * mainloop)
+{
+ GST_ERROR ("LOADED!");
+ g_main_loop_quit (mainloop);
+}
+
+GST_START_TEST (test_group_serialization)
+{
+ gchar *tmpuri;
+ GESLayer *layer;
+ GESClip *c, *c1, *c2, *c3;
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+ GESProject *project;
+ GMainLoop *mainloop;
+
+ GError *err = NULL;
+ GList *tmp, *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ layer = ges_timeline_append_layer (timeline);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+
+ c1 = ges_layer_add_asset (layer, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+
+ c2 = ges_layer_add_asset (layer, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+
+ clips = g_list_append (NULL, group);
+ clips = g_list_append (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ clips = g_list_append (NULL, group);
+ clips = g_list_append (clips, c3);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ project =
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
+
+ tmpuri = ges_test_get_tmp_uri ("test-auto-transition-save.xges");
+ fail_unless (ges_project_save (project, timeline, tmpuri, NULL, TRUE, NULL));
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+
+ project = ges_project_new (tmpuri);
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), &err));
+ g_main_loop_run (mainloop);
+
+ fail_unless (err == NULL, "%s", err ? err->message : "Nothing");
+ fail_unless (timeline != NULL);
+
+ layer = timeline->layers->data;
+ clips = ges_layer_get_clips (layer);
+ for (tmp = clips; tmp; tmp = tmp->next) {
+ fail_unless (GES_IS_GROUP (GES_TIMELINE_ELEMENT_PARENT (tmp->data)),
+ "%s parent is %p, NOT a group", GES_TIMELINE_ELEMENT_NAME (tmp->data),
+ GES_TIMELINE_ELEMENT_PARENT (tmp->data));
+ }
+ g_list_free_full (clips, g_object_unref);
+
+ g_free (tmpuri);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-group");
+ TCase *tc_chain = tcase_create ("group");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_move_group);
+ tcase_add_test (tc_chain, test_group_in_group);
+ tcase_add_test (tc_chain, test_group_in_self);
+ tcase_add_test (tc_chain, test_group_serialization);
+ tcase_add_test (tc_chain, test_group_in_group_layer_moving);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+#define LAYER_HEIGHT 1000
+
+GST_START_TEST (test_layer_properties)
+{
+ GESTimeline *timeline;
+ GESLayer *layer, *layer1;
+ GESTrack *track;
+ GESTrackElement *trackelement;
+ GESClip *clip;
+
+ ges_init ();
+
+ /* Timeline and 1 Layer */
+ timeline = ges_timeline_new ();
+
+ /* The default priority is 0 */
+ fail_unless ((layer = ges_timeline_append_layer (timeline)));
+ fail_unless_equals_int (ges_layer_get_priority (layer), 0);
+
+ fail_if (g_object_is_floating (layer));
+
+ fail_unless ((layer1 = ges_timeline_append_layer (timeline)));
+ fail_unless_equals_int (ges_layer_get_priority (layer1), 1);
+
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip = (GESClip *) ges_test_clip_new ();
+ fail_unless (clip != NULL);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 51,
+ "in-point", (guint64) 12, NULL);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+ assert_equals_uint64 (_PRIORITY (clip), 0);
+
+ /* Add the clip to the timeline */
+ fail_unless (g_object_is_floating (clip));
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
+ fail_if (g_object_is_floating (clip));
+ trackelement = ges_clip_find_track_element (clip, track, G_TYPE_NONE);
+ fail_unless (trackelement != NULL);
+
+ /* This is not a SimpleLayer, therefore the properties shouldn't have changed */
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+ assert_equals_uint64 (_PRIORITY (clip), 1);
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change the priority of the layer */
+ g_object_set (layer, "priority", 1, NULL);
+ assert_equals_int (ges_layer_get_priority (layer), 1);
+ assert_equals_uint64 (_PRIORITY (clip), 1);
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change it to an insanely high value */
+ g_object_set (layer, "priority", 31, NULL);
+ assert_equals_int (ges_layer_get_priority (layer), 31);
+ assert_equals_uint64 (_PRIORITY (clip), 1);
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + LAYER_HEIGHT * 31, TRUE);
+
+ /* and back to 0 */
+ fail_unless (ges_timeline_move_layer (timeline, layer, 0));
+ assert_equals_int (ges_layer_get_priority (layer), 0);
+ assert_equals_uint64 (_PRIORITY (clip), 1);
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
+
+ gst_object_unref (trackelement);
+ fail_unless (ges_layer_remove_clip (layer, clip));
+ fail_unless (ges_timeline_remove_track (timeline, track));
+ fail_unless (ges_timeline_remove_layer (timeline, layer));
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_priorities)
+{
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer1, *layer2, *layer3;
+ GESTrackElement *trackelement1, *trackelement2, *trackelement3;
+ GESClip *clip1, *clip2, *clip3;
+ GstElement *nleobj1, *nleobj2, *nleobj3;
+ guint prio1, prio2, prio3;
+ GList *objs;
+
+ ges_init ();
+
+ /* Timeline and 3 Layer */
+ timeline = ges_timeline_new ();
+ fail_unless ((layer1 = ges_timeline_append_layer (timeline)));
+ fail_unless ((layer2 = ges_timeline_append_layer (timeline)));
+ fail_unless ((layer3 = ges_timeline_append_layer (timeline)));
+ fail_unless_equals_int (ges_layer_get_priority (layer1), 0);
+ fail_unless_equals_int (ges_layer_get_priority (layer2), 1);
+ fail_unless_equals_int (ges_layer_get_priority (layer3), 2);
+
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip1 = GES_CLIP (ges_test_clip_new ());
+ clip2 = GES_CLIP (ges_test_clip_new ());
+ clip3 = GES_CLIP (ges_test_clip_new ());
+ fail_unless (clip1 != NULL);
+ fail_unless (clip2 != NULL);
+ fail_unless (clip3 != NULL);
+
+ g_object_set (clip1, "start", 0, "duration", 10, NULL);
+ g_object_set (clip2, "start", 10, "duration", 10, NULL);
+ g_object_set (clip3, "start", 20, "duration", 10, NULL);
+
+ /* Add objects to the timeline */
+ fail_unless (ges_layer_add_clip (layer1, clip1));
+ trackelement1 = ges_clip_find_track_element (clip1, track, G_TYPE_NONE);
+ fail_unless (trackelement1 != NULL);
+
+ fail_unless (ges_layer_add_clip (layer2, clip2));
+ trackelement2 = ges_clip_find_track_element (clip2, track, G_TYPE_NONE);
+ fail_unless (trackelement2 != NULL);
+
+ fail_unless (ges_layer_add_clip (layer3, clip3));
+ trackelement3 = ges_clip_find_track_element (clip3, track, G_TYPE_NONE);
+ fail_unless (trackelement3 != NULL);
+
+ ges_timeline_commit (timeline);
+ assert_equals_int (_PRIORITY (clip1), 1);
+ nleobj1 = ges_track_element_get_nleobject (trackelement1);
+ fail_unless (nleobj1 != NULL);
+ g_object_get (nleobj1, "priority", &prio1, NULL);
+ assert_equals_int (prio1, MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+
+ assert_equals_int (_PRIORITY (clip2), 1);
+ nleobj2 = ges_track_element_get_nleobject (trackelement2);
+ fail_unless (nleobj2 != NULL);
+ g_object_get (nleobj2, "priority", &prio2, NULL);
+ /* clip2 is on the second layer and has a priority of 1 */
+ assert_equals_int (prio2, MIN_NLE_PRIO + LAYER_HEIGHT + 1);
+
+ /* We do not take into account users set priorities */
+ assert_equals_int (_PRIORITY (clip3), 1);
+
+ nleobj3 = ges_track_element_get_nleobject (trackelement3);
+ fail_unless (nleobj3 != NULL);
+
+ /* clip3 is on the third layer and has a priority of LAYER_HEIGHT + 1
+ * it priority must have the maximum priority of this layer*/
+ g_object_get (nleobj3, "priority", &prio3, NULL);
+ assert_equals_int (prio3, 1 + MIN_NLE_PRIO + LAYER_HEIGHT * 2);
+
+ /* Move layers around */
+ fail_unless (ges_timeline_move_layer (timeline, layer1, 2));
+ ges_timeline_commit (timeline);
+
+ /* And check the new priorities */
+ assert_equals_int (ges_layer_get_priority (layer1), 2);
+ assert_equals_int (ges_layer_get_priority (layer2), 0);
+ assert_equals_int (ges_layer_get_priority (layer3), 1);
+ assert_equals_int (_PRIORITY (clip1), 1);
+ assert_equals_int (_PRIORITY (clip2), 1);
+ assert_equals_int (_PRIORITY (clip3), 1);
+ g_object_get (nleobj1, "priority", &prio1, NULL);
+ g_object_get (nleobj2, "priority", &prio2, NULL);
+ g_object_get (nleobj3, "priority", &prio3, NULL);
+ assert_equals_int (prio1,
+ 2 * LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (prio2, MIN_NLE_PRIO + 1);
+ assert_equals_int (prio3, LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+
+ /* And move objects around */
+ fail_unless (ges_clip_move_to_layer (clip2, layer1));
+ fail_unless (ges_clip_move_to_layer (clip3, layer1));
+ ges_timeline_commit (timeline);
+
+ objs = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objs), 3);
+ fail_unless (ges_layer_get_clips (layer2) == NULL);
+ fail_unless (ges_layer_get_clips (layer3) == NULL);
+ g_list_free_full (objs, gst_object_unref);
+
+ /* Check their priorities (layer1 priority is now 2) */
+ assert_equals_int (_PRIORITY (clip1), 1);
+ assert_equals_int (_PRIORITY (clip2), 2);
+ assert_equals_int (_PRIORITY (clip3), 3);
+ g_object_get (nleobj1, "priority", &prio1, NULL);
+ g_object_get (nleobj2, "priority", &prio2, NULL);
+ g_object_get (nleobj3, "priority", &prio3, NULL);
+ assert_equals_int (prio1,
+ 2 * LAYER_HEIGHT + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (prio2,
+ 2 * LAYER_HEIGHT + 1 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+ assert_equals_int (prio3,
+ 2 * LAYER_HEIGHT + 2 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT);
+
+ gst_object_unref (trackelement1);
+ gst_object_unref (trackelement2);
+ gst_object_unref (trackelement3);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_timeline_auto_transition)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESLayer *layer, *layer1, *layer2;
+
+ ges_init ();
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ fail_unless (GES_IS_ASSET (asset));
+ gst_object_unref (asset);
+
+ GST_DEBUG ("Create timeline");
+ timeline = ges_timeline_new_audio_video ();
+ assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+ GST_DEBUG ("Create layers");
+ layer = ges_layer_new ();
+ assert_is_type (layer, GES_TYPE_LAYER);
+ layer1 = ges_layer_new ();
+ assert_is_type (layer, GES_TYPE_LAYER);
+ layer2 = ges_layer_new ();
+ assert_is_type (layer, GES_TYPE_LAYER);
+
+ GST_DEBUG ("Set auto-transition to the layers");
+ ges_layer_set_auto_transition (layer, TRUE);
+ ges_layer_set_auto_transition (layer1, TRUE);
+ ges_layer_set_auto_transition (layer2, TRUE);
+
+ GST_DEBUG ("Add layers to the timeline");
+ ges_timeline_add_layer (timeline, layer);
+ ges_timeline_add_layer (timeline, layer1);
+ ges_timeline_add_layer (timeline, layer2);
+
+ GST_DEBUG ("Check that auto-transition was properly set to the layers");
+ fail_unless (ges_layer_get_auto_transition (layer));
+ fail_unless (ges_layer_get_auto_transition (layer1));
+ fail_unless (ges_layer_get_auto_transition (layer2));
+
+ GST_DEBUG ("Set timeline auto-transition property to FALSE");
+ ges_timeline_set_auto_transition (timeline, FALSE);
+
+ GST_DEBUG
+ ("Check that layers auto-transition has the same value as timeline");
+ fail_if (ges_layer_get_auto_transition (layer));
+ fail_if (ges_layer_get_auto_transition (layer1));
+ fail_if (ges_layer_get_auto_transition (layer2));
+
+ GST_DEBUG ("Set timeline auto-transition property to TRUE");
+ ges_timeline_set_auto_transition (timeline, TRUE);
+
+ GST_DEBUG
+ ("Check that layers auto-transition has the same value as timeline");
+ fail_unless (ges_layer_get_auto_transition (layer));
+ fail_unless (ges_layer_get_auto_transition (layer1));
+ fail_unless (ges_layer_get_auto_transition (layer2));
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_single_layer_automatic_transition)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GList *objects;
+ GESClip *transition;
+ GESLayer *layer;
+ GESTimelineElement *src, *src1, *src2;
+
+ ges_init ();
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ fail_unless (GES_IS_ASSET (asset));
+
+ GST_DEBUG ("Create timeline");
+ timeline = ges_timeline_new_audio_video ();
+ assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+ GST_DEBUG ("Create first layer");
+ layer = ges_layer_new ();
+ assert_is_type (layer, GES_TYPE_LAYER);
+
+ GST_DEBUG ("Add first layer to timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ GST_DEBUG ("Set auto transition to first layer");
+ ges_layer_set_auto_transition (layer, TRUE);
+
+ GST_DEBUG ("Check that auto-transition was properly set");
+ fail_unless (ges_layer_get_auto_transition (layer));
+
+ GST_DEBUG ("Adding assets to first layer");
+ GST_DEBUG ("Adding clip from 0 -- 1000 to first layer");
+ src = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 0, 0,
+ 1000, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src));
+
+ GST_DEBUG ("Adding clip from 500 -- 1000 to first layer");
+ src1 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 500,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src1));
+
+ /*
+ * 500__transition__1000
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ ges_timeline_commit (timeline);
+
+ GST_DEBUG ("Checking that a transition has been added");
+ objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (objects->data, GES_TYPE_TEST_CLIP);
+
+ transition = objects->next->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ transition = objects->next->next->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Moving first source to 250");
+ ges_timeline_element_set_start (src, 250);
+
+ /*
+ * 600_____transition______1500
+ * 600___________src_________1600
+ * 500___________src1_________1500
+ */
+ GST_DEBUG ("Checking src timing values");
+ CHECK_OBJECT_PROPS (src, 250, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+
+ objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ transition = objects->next->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ CHECK_OBJECT_PROPS (transition, 500, 0, 750);
+
+ transition = objects->next->next->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_int (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 750);
+ g_list_free_full (objects, gst_object_unref);
+
+ fail_if (ges_timeline_element_set_start (src1, 250));
+
+ fail_if (ges_container_edit (GES_CONTAINER (src), NULL, -1,
+ GES_EDIT_MODE_TRIM, GES_EDGE_START, 500));
+ CHECK_OBJECT_PROPS (src, 250, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+ fail_if (ges_timeline_element_trim (src, 500));
+ CHECK_OBJECT_PROPS (src, 250, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+ fail_if (ges_timeline_element_trim (src, 750));
+ CHECK_OBJECT_PROPS (src, 250, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+ fail_if (ges_timeline_element_set_start (src, 500));
+ CHECK_OBJECT_PROPS (src, 250, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+
+ /*
+ * 600_____transition______1500
+ * 600___________src_________1600
+ * 500___________src1_________1500
+ */
+ ges_timeline_element_set_start (src, 600);
+ CHECK_OBJECT_PROPS (src, 600, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+ objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ transition = objects->next->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ CHECK_OBJECT_PROPS (transition, 600, 0, 900);
+ g_list_free_full (objects, gst_object_unref);
+
+ GST_DEBUG ("Adding asset to first layer");
+ GST_DEBUG ("Adding clip from 1250 -- 1000 to first layer");
+ fail_if (ges_layer_add_asset (layer, asset, 1250, 0,
+ 1000, GES_TRACK_TYPE_UNKNOWN));
+
+ /*
+ * 1500___________src2________2000
+ * 1500_trans_1600
+ * 600______________src________________1600
+ * 600_____transition______1500
+ * 500___________src1_________1500
+ */
+ src2 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 1500, 0,
+ 500, GES_TRACK_TYPE_UNKNOWN));
+ assert_is_type (src2, GES_TYPE_TEST_CLIP);
+
+ CHECK_OBJECT_PROPS (src, 600, 0, 1000);
+ CHECK_OBJECT_PROPS (src1, 500, 0, 1000);
+ CHECK_OBJECT_PROPS (src2, 1500, 0, 500);
+ objects = ges_layer_get_clips (layer);
+ transition = objects->next->next->data;
+ assert_equals_int (g_list_length (objects), 7);
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ CHECK_OBJECT_PROPS (transition, 600, 0, 900);
+ transition = objects->next->next->next->next->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ CHECK_OBJECT_PROPS (transition, 1500, 0, 100);
+
+ g_list_free_full (objects, gst_object_unref);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_multi_layer_automatic_transition)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GList *objects, *current;
+ GESClip *transition;
+ GESLayer *layer, *layer1;
+ GESTimelineElement *src, *src1, *src2, *src3;
+
+ ges_init ();
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ fail_unless (GES_IS_ASSET (asset));
+
+ GST_DEBUG ("Create timeline");
+ timeline = ges_timeline_new_audio_video ();
+ assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+ GST_DEBUG ("Create first layer");
+ layer = ges_layer_new ();
+ assert_is_type (layer, GES_TYPE_LAYER);
+
+ GST_DEBUG ("Add first layer to timeline");
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ GST_DEBUG ("Append a new layer to the timeline");
+ layer1 = ges_timeline_append_layer (timeline);
+ assert_is_type (layer1, GES_TYPE_LAYER);
+
+ GST_DEBUG ("Set auto transition to first layer");
+ ges_layer_set_auto_transition (layer, TRUE);
+
+ GST_DEBUG ("Check that auto-transition was properly set");
+ fail_unless (ges_layer_get_auto_transition (layer));
+ fail_if (ges_layer_get_auto_transition (layer1));
+
+ GST_DEBUG ("Adding assets to first layer");
+ GST_DEBUG ("Adding clip from 0 -- 1000 to first layer");
+ src = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 0, 0,
+ 1000, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src));
+
+ GST_DEBUG ("Adding clip from 500 -- 1000 to first layer");
+ src1 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 500,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+ ges_timeline_commit (timeline);
+ fail_unless (GES_IS_CLIP (src1));
+
+ /*
+ * 500__transition__1000
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+
+ GST_DEBUG ("Checking that a transition has been added");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Adding clip 2 from 500 -- 1000 to second layer");
+ src2 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer1, asset, 0,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+ GST_DEBUG ("Adding clip 3 from 500 -- 1000 to second layer");
+ src3 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer1, asset, 500,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+ assert_is_type (src3, GES_TYPE_TEST_CLIP);
+
+ /* 500__transition__1000
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ *----------------------------------------------------
+ * 0___________src2_________1000
+ * 500___________src3_________1500 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 500);
+ assert_equals_uint64 (_DURATION (src3), 1500 - 500);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Checking transitions on second layer");
+ current = objects = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objects), 2);
+ fail_unless (current->data == src2);
+ fail_unless (current->next->data == src3);
+ g_list_free_full (objects, gst_object_unref);
+
+ GST_DEBUG
+ ("Set auto transition to second layer, a new transition should be added");
+ ges_layer_set_auto_transition (layer1, TRUE);
+
+ /* 500__transition__1000
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ *----------------------------------------------------
+ * 500__transition__1000
+ * 0__________src2_________1000
+ * 500___________src3_________1500 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 500);
+ assert_equals_uint64 (_DURATION (src3), 1500 - 500);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Checking transitions has been added on second layer");
+ current = objects = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Moving src3 to 1000. should remove transition");
+ ges_timeline_element_set_start (src3, 1000);
+
+ /* 500__transition__1000
+ * 0___________src_________1000
+ * 500___________src1_________1500 Layer
+ *----------------------------------------------------
+ * 0__________src2_________1000
+ * 1000___________src3_________2000 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 1000);
+ assert_equals_uint64 (_DURATION (src3), 2000 - 1000);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Checking transitions has been removed on second layer");
+ current = objects = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objects), 2);
+ fail_unless (current->data == src2);
+ fail_unless (current->next->data == src3);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Moving src3 to first layer, should add a transition");
+ ges_clip_move_to_layer (GES_CLIP (src3), layer);
+
+ /* 500__transition__1000
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ * 1000___________src3_________2000 Layer
+ * 1000__tr__1500
+ *----------------------------------------------------
+ * 0__________src2_________1000 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 1000);
+ assert_equals_uint64 (_DURATION (src3), 2000 - 1000);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 7);
+ fail_unless (current->data == src);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ fail_unless (current->data == src1);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1000);
+ assert_equals_uint64 (_DURATION (transition), 1500 - 1000);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1000);
+ assert_equals_uint64 (_DURATION (transition), 1500 - 1000);
+
+ current = current->next;
+ fail_unless (current->data == src3);
+
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Checking second layer");
+ current = objects = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == src2);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG
+ ("Moving src to second layer, should remove first transition on first layer");
+ fail_if (ges_clip_move_to_layer (GES_CLIP (src), layer1));
+
+ /* 500___________src1_________1500
+ * 1000___________src3_________2000 Layer
+ * 1000__tr__1500
+ *----------------------------------------------------
+ * 0___________src_________1000
+ * 0__________src2_________1000 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 1000);
+ assert_equals_uint64 (_DURATION (src3), 2000 - 1000);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 7);
+
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Edit src to first layer start=1500");
+ ges_container_edit (GES_CONTAINER (src), NULL, 0,
+ GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 1500);
+ /* 1500___________src_________2500
+ * 1500______tr______2000
+ * 500___________src1_________1500 ^
+ * 1000_________^_src3_________2000 Layer
+ * 1000__tr__1500
+ *---------------------------------------------------------------------------
+ * 0__________src2_________1000 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 1500);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 1000);
+ assert_equals_uint64 (_DURATION (src3), 2000 - 1000);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 7);
+ fail_unless (current->data == src1);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1000);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1000);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ fail_unless (current->data == src3);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ fail_unless (current->data == src);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Checking second layer");
+ current = objects = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objects), 1);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Ripple src1 to 700");
+ ges_container_edit (GES_CONTAINER (src1), NULL, 0,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 700);
+
+ /* 1700___________src_________2700
+ * 1700__tr__2000
+ * 700___________src1_________1700
+ * 1200___________src3_________2200 Layer
+ * 1200___tr__1700
+ *---------------------------------------------------------------------------
+ * 0__________src2_________1000 Layer1
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 1700);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 700);
+ assert_equals_uint64 (_DURATION (src1), 1700 - 700);
+ assert_equals_uint64 (_START (src2), 0);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 1200);
+ assert_equals_uint64 (_DURATION (src3), 2200 - 1200);
+
+ GST_DEBUG ("Checking transitions on first layer");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 7);
+ fail_unless (current->data == src1);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1200);
+ assert_equals_uint64 (_DURATION (transition), 1700 - 1200);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1200);
+ assert_equals_uint64 (_DURATION (transition), 1700 - 1200);
+
+ current = current->next;
+ fail_unless (current->data == src3);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1700);
+ assert_equals_uint64 (_DURATION (transition), 2200 - 1700);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1700);
+ assert_equals_uint64 (_DURATION (transition), 2200 - 1700);
+
+ current = current->next;
+ fail_unless (current->data == src);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Checking second layer");
+ current = objects = ges_layer_get_clips (layer1);
+ assert_equals_int (g_list_length (objects), 1);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_activate_automatic_transition)
+{
+ GESAsset *asset, *transition_asset;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GList *objects, *current;
+ GESClip *transition;
+ GESTimelineElement *src, *src1, *src2, *src3;
+
+ ges_init ();
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ transition_asset =
+ ges_asset_request (GES_TYPE_TRANSITION_CLIP, "crossfade", NULL);
+ fail_unless (GES_IS_ASSET (asset));
+
+ GST_DEBUG ("Create timeline");
+ timeline = ges_timeline_new_audio_video ();
+ assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+ GST_DEBUG ("Append a layer to the timeline");
+ layer = ges_timeline_append_layer (timeline);
+ assert_is_type (layer, GES_TYPE_LAYER);
+
+ GST_DEBUG ("Adding clip from 0 -- 1000 to layer");
+ src = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 0, 0,
+ 1000, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src));
+
+ GST_DEBUG ("Adding clip from 500 -- 1000 to first layer");
+ src1 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 500,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src1));
+
+ GST_DEBUG ("Adding clip from 1000 -- 2000 to layer");
+ src2 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 1000,
+ 0, 1000, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src2));
+
+ GST_DEBUG ("Adding clip from 2000 -- 2500 to layer");
+ src3 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 2000,
+ 0, 500, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_CLIP (src3));
+
+ /*
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ * 1000____src2_______2000
+ * 2000_______src2_____2500
+ */
+ GST_DEBUG ("Checking src timing values");
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 1000);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 2000);
+ assert_equals_uint64 (_DURATION (src3), 500);
+
+ GST_DEBUG ("Checking that no transition has been added");
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 4);
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+ g_list_free_full (objects, gst_object_unref);
+
+ GST_DEBUG ("Adding transition from 1000 -- 1500 to layer");
+ transition =
+ GES_CLIP (ges_layer_add_asset (layer,
+ transition_asset, 1000, 0, 500, GES_TRACK_TYPE_VIDEO));
+ g_object_unref (transition_asset);
+ fail_unless (GES_IS_TRANSITION_CLIP (transition));
+ objects = GES_CONTAINER_CHILDREN (transition);
+ assert_equals_int (g_list_length (objects), 1);
+
+ GST_DEBUG ("Checking the transitions");
+ /*
+ * 0___________src_________1000
+ * 500___________src1_________1500
+ * 1000__tr__1500 (1 of the 2 tracks only)
+ * 1000____src2_______2000
+ * 2000_______src3_____2500
+ */
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 5);
+ current = current->next;
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+ current = current->next;
+ assert_is_type (current->data, GES_TYPE_TRANSITION_CLIP);
+ current = current->next;
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+ current = current->next;
+ assert_is_type (current->data, GES_TYPE_TEST_CLIP);
+ g_list_free_full (objects, gst_object_unref);
+
+ ges_layer_set_auto_transition (layer, TRUE);
+ /*
+ * 0___________src_________1000
+ * 500______tr______1000
+ * 500___________src1_________1500
+ * 1000__tr__1500
+ * 1000____src2_______2000
+ * 2000_______src3_____2500
+ */
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 8);
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 1000);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 2000);
+ assert_equals_uint64 (_DURATION (src3), 500);
+
+ GST_DEBUG ("Checking transitions");
+ fail_unless (current->data == src);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ fail_unless (current->data == src1);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1000);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1000);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ fail_unless (current->data == src2);
+
+ current = current->next;
+ fail_unless (current->data == src3);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+ GST_DEBUG ("Moving src2 to 1200, check everything updates properly");
+ ges_timeline_element_set_start (src2, 1200);
+ ges_timeline_commit (timeline);
+ /*
+ * 0___________src_________1000
+ * 500______tr______1000
+ * 500___________src1_________1500
+ * 1200_tr_1500
+ * 1200____src2_______2200
+ * !__tr__^
+ * 2000_______src3_____2500
+ */
+ current = objects = ges_layer_get_clips (layer);
+ assert_equals_int (g_list_length (objects), 10);
+ assert_equals_uint64 (_START (src), 0);
+ assert_equals_uint64 (_DURATION (src), 1000);
+ assert_equals_uint64 (_START (src1), 500);
+ assert_equals_uint64 (_DURATION (src1), 1500 - 500);
+ assert_equals_uint64 (_START (src2), 1200);
+ assert_equals_uint64 (_DURATION (src2), 1000);
+ assert_equals_uint64 (_START (src3), 2000);
+ assert_equals_uint64 (_DURATION (src3), 500);
+
+ GST_DEBUG ("Checking transitions");
+ fail_unless (current->data == src);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 500);
+ assert_equals_uint64 (_DURATION (transition), 500);
+
+ current = current->next;
+ fail_unless (current->data == src1);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1200);
+ assert_equals_uint64 (_DURATION (transition), 300);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 1200);
+ assert_equals_uint64 (_DURATION (transition), 300);
+
+ current = current->next;
+ fail_unless (current->data == src2);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 2000);
+ assert_equals_uint64 (_DURATION (transition), 200);
+
+ current = current->next;
+ transition = current->data;
+ assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
+ assert_equals_uint64 (_START (transition), 2000);
+ assert_equals_uint64 (_DURATION (transition), 200);
+
+ current = current->next;
+ fail_unless (current->data == src3);
+ g_list_free_full (objects, gst_object_unref);
+ ASSERT_OBJECT_REFCOUNT (transition, "layer + timeline", 2);
+
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_string)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ const gchar *result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_string (GES_META_CONTAINER (layer),
+ "ges-test", "blub");
+
+ fail_unless ((result = ges_meta_container_get_string (GES_META_CONTAINER
+ (layer), "ges-test")) != NULL);
+
+ assert_equals_string (result, "blub");
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_boolean)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ gboolean result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_boolean (GES_META_CONTAINER (layer), "ges-test", TRUE);
+
+ fail_unless (ges_meta_container_get_boolean (GES_META_CONTAINER
+ (layer), "ges-test", &result));
+
+ fail_unless (result);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_int)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ gint result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_int (GES_META_CONTAINER (layer), "ges-test", 1234);
+
+ fail_unless (ges_meta_container_get_int (GES_META_CONTAINER (layer),
+ "ges-test", &result));
+
+ assert_equals_int (result, 1234);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_uint)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ guint result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_uint (GES_META_CONTAINER (layer), "ges-test", 42);
+
+ fail_unless (ges_meta_container_get_uint (GES_META_CONTAINER
+ (layer), "ges-test", &result));
+
+ assert_equals_int (result, 42);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_int64)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ gint64 result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_int64 (GES_META_CONTAINER (layer), "ges-test", 1234);
+
+ fail_unless (ges_meta_container_get_int64 (GES_META_CONTAINER (layer),
+ "ges-test", &result));
+
+ assert_equals_int64 (result, 1234);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_uint64)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ guint64 result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_uint64 (GES_META_CONTAINER (layer), "ges-test", 42);
+
+ fail_unless (ges_meta_container_get_uint64 (GES_META_CONTAINER
+ (layer), "ges-test", &result));
+
+ assert_equals_uint64 (result, 42);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_float)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ gfloat result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ fail_unless (ges_meta_container_set_float (GES_META_CONTAINER (layer),
+ "ges-test", 23.456));
+
+ fail_unless (ges_meta_container_get_float (GES_META_CONTAINER (layer),
+ "ges-test", &result));
+
+ assert_equals_float (result, 23.456f);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_double)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ gdouble result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_double (GES_META_CONTAINER (layer),
+ "ges-test", 23.456);
+
+ fail_unless (ges_meta_container_get_double (GES_META_CONTAINER
+ (layer), "ges-test", &result));
+ fail_unless (result == 23.456);
+
+ //TODO CHECK
+ assert_equals_float (result, 23.456);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_date)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GDate *input;
+ GDate *result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ input = g_date_new_dmy (1, 1, 2012);
+
+ ges_meta_container_set_date (GES_META_CONTAINER (layer), "ges-test", input);
+
+ fail_unless (ges_meta_container_get_date (GES_META_CONTAINER
+ (layer), "ges-test", &result));
+
+ fail_unless (g_date_compare (result, input) == 0);
+
+ g_date_free (input);
+ g_date_free (result);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_date_time)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GstDateTime *input;
+ GstDateTime *result = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ input = gst_date_time_new_from_unix_epoch_local_time (123456789);
+
+ fail_unless (ges_meta_container_set_date_time (GES_META_CONTAINER
+ (layer), "ges-test", input));
+
+ fail_unless (ges_meta_container_get_date_time (GES_META_CONTAINER
+ (layer), "ges-test", &result));
+
+ fail_unless (result != NULL);
+ fail_unless (gst_date_time_get_day (input) == gst_date_time_get_day (result));
+ fail_unless (gst_date_time_get_hour (input) ==
+ gst_date_time_get_hour (result));
+
+ gst_date_time_unref (input);
+ gst_date_time_unref (result);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_layer_meta_value)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GValue data = G_VALUE_INIT;
+ const GValue *result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ g_value_init (&data, G_TYPE_STRING);
+ g_value_set_string (&data, "Hello world!");
+
+ ges_meta_container_set_meta (GES_META_CONTAINER (layer),
+ "ges-test-value", &data);
+
+ result =
+ ges_meta_container_get_meta (GES_META_CONTAINER (layer),
+ "ges-test-value");
+ assert_equals_string (g_value_get_string (result), "Hello world!");
+
+ g_value_unset (&data);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_meta_register)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ const gchar *result;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ fail_unless (ges_meta_container_register_meta_string (GES_META_CONTAINER
+ (layer), GES_META_READABLE, "ges-test-value", "Hello world!"));
+
+ result = ges_meta_container_get_string (GES_META_CONTAINER (layer),
+ "ges-test-value");
+ assert_equals_string (result, "Hello world!");
+
+ fail_if (ges_meta_container_set_int (GES_META_CONTAINER (layer),
+ "ges-test-value", 123456));
+
+ result = ges_meta_container_get_string (GES_META_CONTAINER (layer),
+ "ges-test-value");
+ assert_equals_string (result, "Hello world!");
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+test_foreach (const GESMetaContainer * container, const gchar * key,
+ GValue * value, gpointer user_data)
+{
+ fail_unless ((0 == g_strcmp0 (key, "some-string")) ||
+ (0 == g_strcmp0 (key, "some-int")) || (0 == g_strcmp0 (key, "volume")));
+}
+
+GST_START_TEST (test_layer_meta_foreach)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_meta_container_set_string (GES_META_CONTAINER (layer),
+ "some-string", "some-content");
+
+ ges_meta_container_set_int (GES_META_CONTAINER (layer), "some-int", 123456);
+
+ ges_meta_container_foreach (GES_META_CONTAINER (layer),
+ (GESMetaForeachFunc) test_foreach, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_get_clips_in_interval)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESClip *clip, *clip2, *clip3;
+ GList *objects, *current;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ clip = (GESClip *) ges_test_clip_new ();
+ fail_unless (clip != NULL);
+ g_object_set (clip, "start", 10, "duration", 30, NULL);
+ assert_equals_uint64 (_START (clip), 10);
+ assert_equals_uint64 (_DURATION (clip), 30);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+
+ /* Clip's start lies between the interval */
+ current = objects = ges_layer_get_clips_in_interval (layer, 0, 30);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ current = objects = ges_layer_get_clips_in_interval (layer, 0, 11);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ /* Clip's end lies between the interval */
+ current = objects = ges_layer_get_clips_in_interval (layer, 30, 50);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ current = objects = ges_layer_get_clips_in_interval (layer, 39, 50);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ /* Clip exactly overlaps the interval */
+ current = objects = ges_layer_get_clips_in_interval (layer, 10, 40);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ /* Clip completely inside the interval */
+ current = objects = ges_layer_get_clips_in_interval (layer, 0, 50);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ /* Interval completely inside the clip duration */
+ current = objects = ges_layer_get_clips_in_interval (layer, 20, 30);
+ assert_equals_int (g_list_length (objects), 1);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ g_list_free_full (objects, gst_object_unref);
+
+ /* No intersecting clip */
+ objects = ges_layer_get_clips_in_interval (layer, 0, 10);
+ assert_equals_int (g_list_length (objects), 0);
+
+ objects = ges_layer_get_clips_in_interval (layer, 40, 50);
+ assert_equals_int (g_list_length (objects), 0);
+
+ /* Multiple intersecting clips */
+ clip2 = (GESClip *) ges_test_clip_new ();
+ fail_unless (clip2 != NULL);
+ g_object_set (clip2, "start", 50, "duration", 10, NULL);
+ assert_equals_uint64 (_START (clip2), 50);
+ assert_equals_uint64 (_DURATION (clip2), 10);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip2));
+
+ clip3 = (GESClip *) ges_test_clip_new ();
+ fail_unless (clip3 != NULL);
+ g_object_set (clip3, "start", 0, "duration", 5, NULL);
+ assert_equals_uint64 (_START (clip3), 0);
+ assert_equals_uint64 (_DURATION (clip3), 5);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip3));
+
+ /**
+ * Our timeline:
+ * -------------
+ *
+ * |-------- 0--------------- 0--------- |
+ * layer: | clip3 | | clip | | clip2 | |
+ * |-------05 10-------------40 50--------60 |
+ * |--------------------------------------------------|
+ *
+ */
+
+ current = objects = ges_layer_get_clips_in_interval (layer, 4, 52);
+ assert_equals_int (g_list_length (objects), 3);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip3));
+ current = current->next;
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ current = current->next;
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip2));
+ g_list_free_full (objects, gst_object_unref);
+
+ current = objects = ges_layer_get_clips_in_interval (layer, 39, 65);
+ assert_equals_int (g_list_length (objects), 2);
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip));
+ current = current->next;
+ fail_unless (current->data == GES_TIMELINE_ELEMENT (clip2));
+ g_list_free_full (objects, gst_object_unref);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-layer");
+ TCase *tc_chain = tcase_create ("timeline-layer");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_layer_properties);
+ tcase_add_test (tc_chain, test_layer_priorities);
+ tcase_add_test (tc_chain, test_timeline_auto_transition);
+ tcase_add_test (tc_chain, test_single_layer_automatic_transition);
+ tcase_add_test (tc_chain, test_multi_layer_automatic_transition);
+ tcase_add_test (tc_chain, test_layer_activate_automatic_transition);
+ tcase_add_test (tc_chain, test_layer_meta_string);
+ tcase_add_test (tc_chain, test_layer_meta_boolean);
+ tcase_add_test (tc_chain, test_layer_meta_int);
+ tcase_add_test (tc_chain, test_layer_meta_uint);
+ tcase_add_test (tc_chain, test_layer_meta_int64);
+ tcase_add_test (tc_chain, test_layer_meta_uint64);
+ tcase_add_test (tc_chain, test_layer_meta_float);
+ tcase_add_test (tc_chain, test_layer_meta_double);
+ tcase_add_test (tc_chain, test_layer_meta_date);
+ tcase_add_test (tc_chain, test_layer_meta_date_time);
+ tcase_add_test (tc_chain, test_layer_meta_value);
+ tcase_add_test (tc_chain, test_layer_meta_register);
+ tcase_add_test (tc_chain, test_layer_meta_foreach);
+ tcase_add_test (tc_chain, test_layer_get_clips_in_interval);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gst-editing-services
+ *
+ * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * gst-editing-services 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 program. If not, see <http://www.gnu.org/licenses/>.";
+ *
+ */
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+#include <ges/ges-smart-adder.h>
+
+static GMainLoop *main_loop;
+
+GST_START_TEST (simple_smart_adder_test)
+{
+ GstPad *requested_pad;
+ GstPadTemplate *template = NULL;
+ GESTrack *track;
+ GstElement *smart_adder;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_audio_track_new ());
+ smart_adder = ges_smart_adder_new (track);
+
+ fail_unless (GES_IS_SMART_ADDER (smart_adder));
+ fail_unless (GST_IS_ELEMENT (smart_adder));
+ fail_unless (GST_IS_ELEMENT (GES_SMART_ADDER (smart_adder)->adder));
+ fail_unless (GST_IS_PAD (GES_SMART_ADDER (smart_adder)->srcpad));
+
+ template =
+ gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (smart_adder),
+ "sink_%u");
+ fail_unless (template != NULL);
+ requested_pad = gst_element_request_pad (GST_ELEMENT (smart_adder),
+ template, NULL, NULL);
+ fail_unless (GST_IS_PAD (requested_pad));
+
+ gst_object_unref (requested_pad);
+ gst_object_unref (smart_adder);
+ gst_object_unref (track);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+message_received_cb (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ g_main_loop_quit (main_loop);
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ g_main_loop_quit (main_loop);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ g_main_loop_quit (main_loop);
+ default:
+ break;
+ }
+}
+
+GST_START_TEST (simple_audio_mixed_with_pipeline)
+{
+ GstBus *bus;
+ GESAsset *asset;
+ GESClip *tmpclip;
+ GstMessage *message;
+ GESLayer *layer, *layer1;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESPipeline *pipeline;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_audio_track_new ());
+ timeline = ges_timeline_new ();
+ pipeline = ges_test_create_pipeline (timeline);
+
+ ges_timeline_add_track (timeline, track);
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+
+ asset = GES_ASSET (ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL));
+
+ GST_DEBUG ("Setting volume on the layer");
+ ges_meta_container_set_float (GES_META_CONTAINER (layer), GES_META_VOLUME,
+ 1.5);
+
+ tmpclip = ges_layer_add_asset (layer, asset, 0, 0, 1 * GST_SECOND,
+ GES_TRACK_TYPE_AUDIO);
+ ges_audio_test_source_set_volume (GES_CONTAINER_CHILDREN (tmpclip)->data,
+ 1.0);
+ ges_audio_test_source_set_freq (GES_CONTAINER_CHILDREN (tmpclip)->data, 550);
+
+ tmpclip = ges_layer_add_asset (layer1, asset, 0, 0, 2 * GST_SECOND,
+ GES_TRACK_TYPE_AUDIO);
+ g_object_unref (asset);
+
+ ges_audio_test_source_set_volume (GES_CONTAINER_CHILDREN (tmpclip)->data, 1);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+ g_signal_connect (bus, "message", (GCallback) message_received_cb, pipeline);
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING)
+ == GST_STATE_CHANGE_FAILURE);
+ message = gst_bus_timed_pop_filtered (bus, 5 * GST_SECOND,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+
+ if (message == NULL) {
+ fail_unless ("No message after 5 seconds" == NULL);
+ goto done;
+ } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR)
+ fail_error_message (message);
+
+ gst_message_unref (message);
+ GST_INFO ("running main loop");
+ g_main_loop_run (main_loop);
+ g_main_loop_unref (main_loop);
+
+done:
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (audio_video_mixed_with_pipeline)
+{
+ GstBus *bus;
+ GESAsset *asset;
+ GESClip *tmpclip;
+ GstMessage *message;
+ GESLayer *layer, *layer1;
+ GESTrack *track;
+ GESTrack *track_audio;
+ GESTimeline *timeline;
+ GESPipeline *pipeline;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_video_track_new ());
+ track_audio = GES_TRACK (ges_audio_track_new ());
+ timeline = ges_timeline_new ();
+ pipeline = ges_test_create_pipeline (timeline);
+
+ ges_timeline_add_track (timeline, track);
+ ges_timeline_add_track (timeline, track_audio);
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+
+ asset = GES_ASSET (ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL));
+
+ tmpclip =
+ ges_layer_add_asset (layer, asset, 0 * GST_SECOND, 0, 2 * GST_SECOND,
+ GES_TRACK_TYPE_UNKNOWN);
+
+ ges_test_clip_set_vpattern (GES_TEST_CLIP (tmpclip), 18);
+
+ tmpclip =
+ ges_layer_add_asset (layer1, asset, 1 * GST_SECOND, 0, 5 * GST_SECOND,
+ GES_TRACK_TYPE_UNKNOWN);
+ g_object_unref (asset);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+ g_signal_connect (bus, "message", (GCallback) message_received_cb, pipeline);
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING)
+ == GST_STATE_CHANGE_FAILURE);
+
+ message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+
+ if (message == NULL) {
+ fail_unless ("No message after 5 seconds" == NULL);
+ goto done;
+ } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR)
+ fail_error_message (message);
+
+ gst_message_unref (message);
+ GST_INFO ("running main loop");
+ g_main_loop_run (main_loop);
+ g_main_loop_unref (main_loop);
+
+done:
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("Smart mixers");
+ TCase *tc_chain = tcase_create ("smart-mixers");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, simple_smart_adder_test);
+ tcase_add_test (tc_chain, simple_audio_mixed_with_pipeline);
+ tcase_add_test (tc_chain, audio_video_mixed_with_pipeline);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+#include <signal.h>
+
+static void
+sigabrt_handler (int signum)
+{
+ /* expected abort */
+ exit (0);
+}
+
+static gpointer
+deinit_thread_func (gpointer user_data)
+{
+ signal (SIGABRT, sigabrt_handler);
+ ges_deinit ();
+
+ /* shouldn't be reached */
+ exit (1);
+
+ return NULL;
+}
+
+GST_START_TEST (test_inconsistent_init_deinit_thread)
+{
+ GThread *thread;
+
+ fail_unless (ges_init ());
+
+ /* test assertion, when trying to call ges_deinit() in a thread different
+ * from that of ges_init() called.
+ */
+ thread = g_thread_new ("test-ges-deinit-thread",
+ (GThreadFunc) deinit_thread_func, NULL);
+
+ g_thread_join (thread);
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-negative");
+ TCase *tc_chain = tcase_create ("negative");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_inconsistent_init_deinit_thread);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+GST_START_TEST (test_overlay_basic)
+{
+ GESTextOverlayClip *source;
+
+ ges_init ();
+
+ source = ges_text_overlay_clip_new ();
+ fail_unless (source != NULL);
+
+ gst_object_unref (source);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_overlay_properties)
+{
+ GESClip *clip;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ track = ges_track_new (GES_TRACK_TYPE_VIDEO, gst_caps_ref (GST_CAPS_ANY));
+ fail_unless (track != NULL);
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ clip = (GESClip *) ges_text_overlay_clip_new ();
+ fail_unless (clip != NULL);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 51,
+ "in-point", (guint64) 12, NULL);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
+ GES_TIMELINE_ELEMENT (clip));
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 51);
+ assert_equals_uint64 (_INPOINT (trackelement), 12);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change more properties, see if they propagate */
+ g_object_set (clip, "start", (guint64) 420, "duration", (guint64) 510,
+ "in-point", (guint64) 120, NULL);
+ ges_timeline_commit (timeline);
+ assert_equals_uint64 (_START (clip), 420);
+ assert_equals_uint64 (_DURATION (clip), 510);
+ assert_equals_uint64 (_INPOINT (clip), 120);
+ assert_equals_uint64 (_START (trackelement), 420);
+ assert_equals_uint64 (_DURATION (trackelement), 510);
+ assert_equals_uint64 (_INPOINT (trackelement), 120);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
+
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (trackelement));
+ gst_object_unref (clip);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_overlay_in_layer)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *a, *v;
+ GESTrackElement *track_element;
+ GESTextOverlayClip *source;
+ gchar *text;
+ gint halign, valign;
+ guint32 color;
+ gdouble xpos;
+ gdouble ypos;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = (GESLayer *) ges_layer_new ();
+ a = GES_TRACK (ges_audio_track_new ());
+ v = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, a);
+ ges_timeline_add_track (timeline, v);
+ ges_timeline_add_layer (timeline, layer);
+
+ source = ges_text_overlay_clip_new ();
+
+ g_object_set (source, "duration", (guint64) GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) source);
+
+ /* specifically test the text property */
+ g_object_set (source, "text", (gchar *) "some text", NULL);
+ g_object_get (source, "text", &text, NULL);
+ assert_equals_string ("some text", text);
+ g_free (text);
+
+ track_element =
+ ges_clip_find_track_element (GES_CLIP (source), v, G_TYPE_NONE);
+
+ /* test the font-desc property */
+ g_object_set (source, "font-desc", (gchar *) "sans 72", NULL);
+ g_object_get (source, "font-desc", &text, NULL);
+ assert_equals_string ("sans 72", text);
+ g_free (text);
+
+ assert_equals_string ("sans 72",
+ ges_text_overlay_get_font_desc (GES_TEXT_OVERLAY (track_element)));
+
+ /* test halign and valign */
+ g_object_set (source, "halignment", (gint)
+ GES_TEXT_HALIGN_LEFT, "valignment", (gint) GES_TEXT_VALIGN_TOP, NULL);
+ g_object_get (source, "halignment", &halign, "valignment", &valign, NULL);
+ assert_equals_int (halign, GES_TEXT_HALIGN_LEFT);
+ assert_equals_int (valign, GES_TEXT_VALIGN_TOP);
+
+ halign = ges_text_overlay_get_halignment (GES_TEXT_OVERLAY (track_element));
+ valign = ges_text_overlay_get_valignment (GES_TEXT_OVERLAY (track_element));
+ assert_equals_int (halign, GES_TEXT_HALIGN_LEFT);
+ assert_equals_int (valign, GES_TEXT_VALIGN_TOP);
+
+ /* test color */
+ g_object_set (source, "color", (gint) 2147483647, NULL);
+ g_object_get (source, "color", &color, NULL);
+ assert_equals_int (color, 2147483647);
+
+ color = ges_text_overlay_get_color (GES_TEXT_OVERLAY (track_element));
+ assert_equals_int (color, 2147483647);
+
+ /* test xpos */
+ g_object_set (source, "xpos", (gdouble) 0.5, NULL);
+ g_object_get (source, "xpos", &xpos, NULL);
+ assert_equals_float (xpos, 0.5);
+
+ xpos = ges_text_overlay_get_xpos (GES_TEXT_OVERLAY (track_element));
+ assert_equals_float (xpos, 0.5);
+
+ /* test ypos */
+ g_object_set (source, "ypos", (gdouble) 0.33, NULL);
+ g_object_get (source, "ypos", &ypos, NULL);
+ assert_equals_float (ypos, 0.33);
+
+ ypos = ges_text_overlay_get_ypos (GES_TEXT_OVERLAY (track_element));
+ assert_equals_float (ypos, 0.33);
+
+ GST_DEBUG ("removing the source");
+
+ ges_layer_remove_clip (layer, (GESClip *) source);
+
+ GST_DEBUG ("removing the layer");
+
+ gst_object_unref (track_element);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-overlays");
+ TCase *tc_chain = tcase_create ("overlays");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_overlay_basic);
+ tcase_add_test (tc_chain, test_overlay_properties);
+ tcase_add_test (tc_chain, test_overlay_in_layer);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2012> 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.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+#include <gst/controller/gstdirectcontrolbinding.h>
+#include <gst/controller/gstinterpolationcontrolsource.h>
+
+GMainLoop *mainloop;
+
+static void
+project_loaded_cb (GESProject * project, GESTimeline * timeline,
+ GMainLoop * mainloop)
+{
+ g_main_loop_quit (mainloop);
+}
+
+GST_START_TEST (test_project_simple)
+{
+ gchar *id;
+ GESProject *project;
+ GESTimeline *timeline;
+
+ ges_init ();
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ project = GES_PROJECT (ges_asset_request (GES_TYPE_TIMELINE, NULL, NULL));
+ fail_unless (GES_IS_PROJECT (project));
+ assert_equals_string (ges_asset_get_id (GES_ASSET (project)), "project-0");
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+ g_main_loop_run (mainloop);
+
+ fail_unless (GES_IS_TIMELINE (timeline));
+ id = ges_extractable_get_id (GES_EXTRACTABLE (timeline));
+ assert_equals_string (id, "project-0");
+ ASSERT_OBJECT_REFCOUNT (timeline, "We own the only ref", 1);
+
+ g_free (id);
+ gst_object_unref (project);
+ gst_object_unref (timeline);
+ g_main_loop_unref (mainloop);
+ g_signal_handlers_disconnect_by_func (project, (GCallback) project_loaded_cb,
+ mainloop);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+asset_removed_add_cb (GESProject * project, GESAsset * asset, gboolean * called)
+{
+ *called = TRUE;
+}
+
+static void
+asset_created_cb (GObject * source, GAsyncResult * res, GESAsset ** asset)
+{
+ GError *error = NULL;
+ *asset = ges_asset_request_finish (res, &error);
+
+ fail_unless (error == NULL);
+ g_main_loop_quit (mainloop);
+}
+
+GST_START_TEST (test_project_add_assets)
+{
+ GESProject *project;
+ GESAsset *asset;
+ gboolean added_cb_called = FALSE;
+ gboolean removed_cb_called = FALSE;
+
+ ges_init ();
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ project = GES_PROJECT (ges_asset_request (GES_TYPE_TIMELINE, NULL, NULL));
+ fail_unless (GES_IS_PROJECT (project));
+
+ g_signal_connect (project, "asset-added",
+ (GCallback) asset_removed_add_cb, &added_cb_called);
+ g_signal_connect (project, "asset-removed",
+ (GCallback) asset_removed_add_cb, &removed_cb_called);
+
+ ges_asset_request_async (GES_TYPE_TEST_CLIP, NULL, NULL,
+ (GAsyncReadyCallback) asset_created_cb, &asset);
+ g_main_loop_run (mainloop);
+ g_main_loop_unref (mainloop);
+
+ fail_unless (GES_IS_ASSET (asset));
+
+ fail_unless (ges_project_add_asset (project, asset));
+ fail_unless (added_cb_called);
+ ASSERT_OBJECT_REFCOUNT (project, "The project", 2);
+ ASSERT_OBJECT_REFCOUNT (asset, "The asset (1 for project and one for "
+ "us + 1 cache)", 3);
+
+ fail_unless (ges_project_remove_asset (project, asset));
+ fail_unless (removed_cb_called);
+
+ g_signal_handlers_disconnect_by_func (project,
+ (GCallback) asset_removed_add_cb, &added_cb_called);
+ g_signal_handlers_disconnect_by_func (project,
+ (GCallback) asset_removed_add_cb, &removed_cb_called);
+
+ gst_object_unref (asset);
+ gst_object_unref (project);
+ ASSERT_OBJECT_REFCOUNT (asset, "The asset (1 ref in cache)", 1);
+ ASSERT_OBJECT_REFCOUNT (project, "The project (1 ref in cache)", 1);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+error_loading_asset_cb (GESProject * project, GError * error, gchar * id,
+ GType extractable_type, GMainLoop * mainloop)
+{
+ fail_unless (g_error_matches (error, GST_PARSE_ERROR,
+ GST_PARSE_ERROR_NO_SUCH_ELEMENT));
+ g_main_loop_quit (mainloop);
+}
+
+GST_START_TEST (test_project_unexistant_effect)
+{
+ GESProject *project;
+ gboolean added_cb_called = FALSE;
+ gboolean removed_cb_called = FALSE;
+
+ ges_init ();
+
+ project = GES_PROJECT (ges_asset_request (GES_TYPE_TIMELINE, NULL, NULL));
+ fail_unless (GES_IS_PROJECT (project));
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (project, "asset-added",
+ (GCallback) asset_removed_add_cb, &added_cb_called);
+ g_signal_connect (project, "asset-removed",
+ (GCallback) asset_removed_add_cb, &removed_cb_called);
+ g_signal_connect (project, "error-loading-asset",
+ (GCallback) error_loading_asset_cb, mainloop);
+
+ fail_unless (ges_project_create_asset (project, "nowaythiselementexists",
+ GES_TYPE_EFFECT));
+ g_main_loop_run (mainloop);
+
+ /* And.... try again! */
+ fail_if (ges_project_create_asset (project, "nowaythiselementexists",
+ GES_TYPE_EFFECT));
+
+ fail_if (added_cb_called);
+ fail_if (removed_cb_called);
+
+ ASSERT_OBJECT_REFCOUNT (project, "The project", 2);
+ gst_object_unref (project);
+ g_main_loop_unref (mainloop);
+
+ ASSERT_OBJECT_REFCOUNT (project, "The project (1 ref in cache)", 1);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+asset_added_cb (GESProject * project, GESAsset * asset)
+{
+ gchar *uri = ges_test_file_uri ("audio_video.ogg");
+ GstDiscovererInfo *info;
+
+ if (ges_asset_get_extractable_type (asset) == GES_TYPE_EFFECT) {
+ assert_equals_string (ges_asset_get_id (asset), "video agingtv");
+ } else {
+ info = ges_uri_clip_asset_get_info (GES_URI_CLIP_ASSET (asset));
+ fail_unless (GST_IS_DISCOVERER_INFO (info));
+ assert_equals_string (ges_asset_get_id (asset), uri);
+ }
+
+ g_free (uri);
+}
+
+static gchar *
+_set_new_uri (GESProject * project, GError * error, GESAsset * wrong_asset)
+{
+ fail_unless (!g_strcmp0 (ges_asset_get_id (wrong_asset),
+ "file:///test/not/exisiting"));
+
+ return ges_test_file_uri ("audio_video.ogg");
+}
+
+static void
+_test_project (GESProject * project, GESTimeline * timeline)
+{
+ guint a_meta;
+ gchar *media_uri;
+ GESTrack *track;
+ const GList *profiles;
+ GstEncodingContainerProfile *profile;
+ GList *tracks, *tmp, *tmptrackelement, *clips;
+
+ fail_unless (GES_IS_TIMELINE (timeline));
+ assert_equals_int (g_list_length (timeline->layers), 2);
+
+ assert_equals_string (ges_meta_container_get_string (GES_META_CONTAINER
+ (project), "name"), "Example project");
+ clips = ges_layer_get_clips (GES_LAYER (timeline->layers->data));
+ fail_unless (ges_meta_container_get_uint (GES_META_CONTAINER
+ (timeline->layers->data), "a", &a_meta));
+ assert_equals_int (a_meta, 3);
+ assert_equals_int (g_list_length (clips), 1);
+ media_uri = ges_test_file_uri ("audio_video.ogg");
+ assert_equals_string (ges_asset_get_id (ges_extractable_get_asset
+ (GES_EXTRACTABLE (clips->data))), media_uri);
+ g_free (media_uri);
+ g_list_free_full (clips, gst_object_unref);
+
+ /* Check tracks and the objects they contain */
+ tracks = ges_timeline_get_tracks (timeline);
+ assert_equals_int (g_list_length (tracks), 2);
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ GList *trackelements;
+ track = GES_TRACK (tmp->data);
+
+ trackelements = ges_track_get_elements (track);
+ GST_DEBUG_OBJECT (track, "Testing track");
+ switch (track->type) {
+ case GES_TRACK_TYPE_VIDEO:
+ assert_equals_int (g_list_length (trackelements), 2);
+ for (tmptrackelement = trackelements; tmptrackelement;
+ tmptrackelement = tmptrackelement->next) {
+ GESTrackElement *trackelement =
+ GES_TRACK_ELEMENT (tmptrackelement->data);
+
+ if (GES_IS_BASE_EFFECT (trackelement)) {
+ guint nb_scratch_lines;
+
+ ges_timeline_element_get_child_properties (tmptrackelement->data,
+ "scratch-lines", &nb_scratch_lines, NULL);
+ assert_equals_int (nb_scratch_lines, 12);
+
+ nle_object_check (ges_track_element_get_nleobject (trackelement),
+ 0, 1000000000, 0, 1000000000, MIN_NLE_PRIO + TRANSITIONS_HEIGHT,
+ TRUE);
+ } else {
+ nle_object_check (ges_track_element_get_nleobject (trackelement),
+ 0, 1000000000, 0, 1000000000,
+ MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1, TRUE);
+ }
+ }
+ break;
+ case GES_TRACK_TYPE_AUDIO:
+ assert_equals_int (g_list_length (trackelements), 2);
+ break;
+ default:
+ g_assert (1);
+ }
+
+ g_list_free_full (trackelements, gst_object_unref);
+
+ }
+ g_list_free_full (tracks, gst_object_unref);
+
+ /* Now test the encoding profile */
+ profiles = ges_project_list_encoding_profiles (project);
+ assert_equals_int (g_list_length ((GList *) profiles), 1);
+ profile = profiles->data;
+ fail_unless (GST_IS_ENCODING_CONTAINER_PROFILE (profile));
+ profiles = gst_encoding_container_profile_get_profiles (profile);
+ assert_equals_int (g_list_length ((GList *) profiles), 2);
+}
+
+static void
+_add_properties (GESTimeline * timeline)
+{
+ GList *tracks;
+ GList *tmp;
+
+ tracks = ges_timeline_get_tracks (timeline);
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ GESTrack *track;
+ GList *track_elements;
+ GList *tmp_tck;
+
+ track = GES_TRACK (tmp->data);
+ switch (track->type) {
+ case GES_TRACK_TYPE_VIDEO:
+ track_elements = ges_track_get_elements (track);
+
+ for (tmp_tck = track_elements; tmp_tck; tmp_tck = tmp_tck->next) {
+ GESTrackElement *element = GES_TRACK_ELEMENT (tmp_tck->data);
+
+ /* Adding keyframes */
+ if (GES_IS_EFFECT (element)) {
+ GstControlSource *source;
+ GstControlBinding *tmp_binding, *binding;
+
+ source = gst_interpolation_control_source_new ();
+
+ /* Check binding creation and replacement */
+ binding =
+ ges_track_element_get_control_binding (element,
+ "scratch-lines");
+ fail_unless (binding == NULL);
+ ges_track_element_set_control_source (element,
+ source, "scratch-lines", "direct");
+ tmp_binding =
+ ges_track_element_get_control_binding (element,
+ "scratch-lines");
+ fail_unless (tmp_binding != NULL);
+ ges_track_element_set_control_source (element,
+ source, "scratch-lines", "direct");
+ binding =
+ ges_track_element_get_control_binding (element,
+ "scratch-lines");
+ fail_unless (binding != tmp_binding);
+
+
+ g_object_set (source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source), 0 * GST_SECOND, 0.);
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source), 5 * GST_SECOND, 0.);
+ gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source), 10 * GST_SECOND, 1.);
+ } else if (GES_IS_VIDEO_SOURCE (element)) {
+ /* Adding children properties */
+ gint64 posx = 42;
+ ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT
+ (element), "posx", posx, NULL);
+ ges_timeline_element_get_child_properties (GES_TIMELINE_ELEMENT
+ (element), "posx", &posx, NULL);
+ fail_unless_equals_int64 (posx, 42);
+ }
+
+ }
+ g_list_free_full (track_elements, g_object_unref);
+ break;
+ default:
+ break;
+ }
+ }
+
+ g_list_free_full (tracks, g_object_unref);
+}
+
+static void
+_check_properties (GESTimeline * timeline)
+{
+ GList *tracks;
+ GList *tmp;
+
+ tracks = ges_timeline_get_tracks (timeline);
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ GESTrack *track;
+ GList *track_elements;
+ GList *tmp_tck;
+
+ track = GES_TRACK (tmp->data);
+ switch (track->type) {
+ case GES_TRACK_TYPE_VIDEO:
+ track_elements = ges_track_get_elements (track);
+
+ for (tmp_tck = track_elements; tmp_tck; tmp_tck = tmp_tck->next) {
+ GESTrackElement *element = GES_TRACK_ELEMENT (tmp_tck->data);
+ /* Checking keyframes */
+ if (GES_IS_EFFECT (element)) {
+ GstControlBinding *binding;
+ GstControlSource *source;
+ GList *timed_values;
+ GstTimedValue *value;
+
+ binding =
+ ges_track_element_get_control_binding (element,
+ "scratch-lines");
+ fail_unless (binding != NULL);
+ g_object_get (binding, "control-source", &source, NULL);
+ fail_unless (source != NULL);
+
+ /* Now check keyframe position */
+ timed_values =
+ gst_timed_value_control_source_get_all
+ (GST_TIMED_VALUE_CONTROL_SOURCE (source));
+ value = timed_values->data;
+ fail_unless (value->value == 0.);
+ fail_unless (value->timestamp == 0 * GST_SECOND);
+ timed_values = timed_values->next;
+ value = timed_values->data;
+ fail_unless (value->value == 0.);
+ fail_unless (value->timestamp == 5 * GST_SECOND);
+ timed_values = timed_values->next;
+ value = timed_values->data;
+ fail_unless (value->value == 1.);
+ fail_unless (value->timestamp == 10 * GST_SECOND);
+ }
+ /* Checking children properties */
+ else if (GES_IS_VIDEO_SOURCE (element)) {
+ /* Init 'posx' with a wrong value */
+ gint64 posx = 27;
+ ges_timeline_element_get_child_properties (GES_TIMELINE_ELEMENT
+ (element), "posx", &posx, NULL);
+ fail_unless_equals_int64 (posx, 42);
+ }
+ }
+ g_list_free_full (track_elements, g_object_unref);
+ break;
+ default:
+ break;
+ }
+ }
+
+ g_list_free_full (tracks, g_object_unref);
+}
+
+GST_START_TEST (test_project_add_properties)
+{
+ GESProject *project;
+ GESTimeline *timeline;
+ gchar *uri;
+
+ ges_init ();
+
+ uri = ges_test_file_uri ("test-properties.xges");
+ project = ges_project_new (uri);
+ g_free (uri);
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ /* Connect the signals */
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+ g_signal_connect (project, "missing-uri", (GCallback) _set_new_uri, NULL);
+
+ /* Now extract a timeline from it */
+ GST_LOG ("Loading project");
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+
+ g_main_loop_run (mainloop);
+
+ GST_LOG ("Test first loading");
+
+
+ _add_properties (timeline);
+
+ uri = ges_test_get_tmp_uri ("test-properties-save.xges");
+ fail_unless (ges_project_save (project, timeline, uri, NULL, TRUE, NULL));
+ gst_object_unref (timeline);
+ gst_object_unref (project);
+
+ project = ges_project_new (uri);
+ g_free (uri);
+
+ ASSERT_OBJECT_REFCOUNT (project, "Our + cache", 2);
+
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+
+ GST_LOG ("Loading saved project");
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+ fail_unless (GES_IS_TIMELINE (timeline));
+
+ g_main_loop_run (mainloop);
+
+ _check_properties (timeline);
+
+ gst_object_unref (timeline);
+ gst_object_unref (project);
+
+ g_main_loop_unref (mainloop);
+ g_signal_handlers_disconnect_by_func (project, (GCallback) project_loaded_cb,
+ mainloop);
+ g_signal_handlers_disconnect_by_func (project, (GCallback) asset_added_cb,
+ NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_project_load_xges)
+{
+ gboolean saved;
+ GESProject *loaded_project, *saved_project;
+ GESTimeline *timeline;
+ GESAsset *formatter_asset;
+ gchar *uri;
+ GList *tmp;
+
+ ges_init ();
+
+ uri = ges_test_file_uri ("test-project.xges");
+ loaded_project = ges_project_new (uri);
+ mainloop = g_main_loop_new (NULL, FALSE);
+ fail_unless (GES_IS_PROJECT (loaded_project));
+
+ /* Connect the signals */
+ g_signal_connect (loaded_project, "asset-added", (GCallback) asset_added_cb,
+ NULL);
+ g_signal_connect (loaded_project, "loaded", (GCallback) project_loaded_cb,
+ mainloop);
+
+ /* Make sure we update the project's dummy URL to some actual URL */
+ g_signal_connect (loaded_project, "missing-uri", (GCallback) _set_new_uri,
+ NULL);
+
+ /* Now extract a timeline from it */
+ GST_LOG ("Loading project");
+ timeline =
+ GES_TIMELINE (ges_asset_extract (GES_ASSET (loaded_project), NULL));
+ fail_unless (GES_IS_TIMELINE (timeline));
+ tmp = ges_project_get_loading_assets (loaded_project);
+ assert_equals_int (g_list_length (tmp), 1);
+ g_list_free_full (tmp, g_object_unref);
+
+ g_main_loop_run (mainloop);
+ GST_LOG ("Test first loading");
+ _test_project (loaded_project, timeline);
+ g_free (uri);
+
+ uri = ges_test_get_tmp_uri ("test-project_TMP.xges");
+ formatter_asset = ges_asset_request (GES_TYPE_FORMATTER, "ges", NULL);
+ saved =
+ ges_project_save (loaded_project, timeline, uri, formatter_asset, TRUE,
+ NULL);
+ fail_unless (saved);
+ gst_object_unref (timeline);
+
+ saved_project = ges_project_new (uri);
+ ASSERT_OBJECT_REFCOUNT (saved_project, "Our + cache", 2);
+ g_signal_connect (saved_project, "asset-added", (GCallback) asset_added_cb,
+ NULL);
+ g_signal_connect (saved_project, "loaded", (GCallback) project_loaded_cb,
+ mainloop);
+
+ GST_LOG ("Loading saved project");
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (saved_project), NULL));
+ fail_unless (GES_IS_TIMELINE (timeline));
+ g_main_loop_run (mainloop);
+ _test_project (saved_project, timeline);
+
+ fail_unless (ges_meta_container_get_string (GES_META_CONTAINER
+ (loaded_project), GES_META_FORMAT_VERSION));
+ fail_unless_equals_string (ges_meta_container_get_string (GES_META_CONTAINER
+ (loaded_project), GES_META_FORMAT_VERSION),
+ ges_meta_container_get_string (GES_META_CONTAINER (loaded_project),
+ GES_META_FORMAT_VERSION));
+ gst_object_unref (timeline);
+ gst_object_unref (saved_project);
+ gst_object_unref (loaded_project);
+ g_free (uri);
+
+ ASSERT_OBJECT_REFCOUNT (saved_project, "Still 1 ref for asset cache", 1);
+
+ g_main_loop_unref (mainloop);
+ g_signal_handlers_disconnect_by_func (saved_project,
+ (GCallback) project_loaded_cb, mainloop);
+ g_signal_handlers_disconnect_by_func (saved_project,
+ (GCallback) asset_added_cb, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_project_auto_transition)
+{
+ GList *layers, *tmp;
+ GESProject *project;
+ GESTimeline *timeline;
+ GESLayer *layer = NULL;
+ GESAsset *formatter_asset;
+ gboolean saved;
+ gchar *tmpuri, *uri;
+
+ ges_init ();
+
+ uri = ges_test_file_uri ("test-auto-transition.xges");
+ project = ges_project_new (uri);
+ mainloop = g_main_loop_new (NULL, FALSE);
+ fail_unless (GES_IS_PROJECT (project));
+
+ /* Connect the signals */
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+ g_signal_connect (project, "missing-uri", (GCallback) _set_new_uri, NULL);
+
+ /* Now extract a timeline from it */
+ GST_LOG ("Loading project");
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+
+ g_main_loop_run (mainloop);
+
+ /* Check timeline and layers auto-transition, must be FALSE */
+ fail_if (ges_timeline_get_auto_transition (timeline));
+ layers = ges_timeline_get_layers (timeline);
+ for (tmp = layers; tmp; tmp = tmp->next) {
+ layer = tmp->data;
+ fail_if (ges_layer_get_auto_transition (layer));
+ }
+
+ g_list_free_full (layers, gst_object_unref);
+ g_free (uri);
+
+ /* Set timeline and layers auto-transition to TRUE */
+ ges_timeline_set_auto_transition (timeline, TRUE);
+
+ tmpuri = ges_test_get_tmp_uri ("test-auto-transition-save.xges");
+ formatter_asset = ges_asset_request (GES_TYPE_FORMATTER, "ges", NULL);
+ saved =
+ ges_project_save (project, timeline, tmpuri, formatter_asset, TRUE, NULL);
+ fail_unless (saved);
+
+ gst_object_unref (timeline);
+ gst_object_unref (project);
+
+ project = ges_project_new (tmpuri);
+
+ ASSERT_OBJECT_REFCOUNT (project, "Our + cache", 2);
+
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+
+ GST_LOG ("Loading saved project");
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+ fail_unless (GES_IS_TIMELINE (timeline));
+
+ g_main_loop_run (mainloop);
+
+ /* Check timeline and layers auto-transition, must be TRUE */
+ fail_unless (ges_timeline_get_auto_transition (timeline));
+ layers = ges_timeline_get_layers (timeline);
+ for (tmp = layers; tmp; tmp = tmp->next) {
+ layer = tmp->data;
+ fail_unless (ges_layer_get_auto_transition (layer));
+ }
+
+ g_list_free_full (layers, gst_object_unref);
+ gst_object_unref (timeline);
+ gst_object_unref (project);
+ g_free (tmpuri);
+
+ g_main_loop_unref (mainloop);
+ g_signal_handlers_disconnect_by_func (project, (GCallback) project_loaded_cb,
+ mainloop);
+ g_signal_handlers_disconnect_by_func (project, (GCallback) asset_added_cb,
+ NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+/* FIXME This test does not pass for some bad reason */
+#if 0
+static void
+project_loaded_now_play_cb (GESProject * project, GESTimeline * timeline)
+{
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+
+ GESPipeline *pipeline = ges_pipeline_new ();
+
+ fail_unless (ges_pipeline_set_timeline (pipeline, timeline));
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ GST_ERROR ("GOT MESSAGE: %" GST_PTR_FORMAT, message);
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS, we did not even start!");
+ carry_on = FALSE;
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ break;
+ case GST_MESSAGE_ASYNC_DONE:
+ GST_DEBUG ("prerolling done");
+ carry_on = FALSE;
+ break;
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+ gst_object_unref (pipeline);
+ g_main_loop_quit (mainloop);
+}
+
+
+GST_START_TEST (test_load_xges_and_play)
+{
+ GESProject *project;
+ GESTimeline *timeline;
+ gchar *uri = ges_test_file_uri ("test-project_TMP.xges");
+
+ project = ges_project_new (uri);
+ fail_unless (GES_IS_PROJECT (project));
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ /* Connect the signals */
+ g_signal_connect (project, "loaded", (GCallback) project_loaded_now_play_cb,
+ NULL);
+
+ /* Now extract a timeline from it */
+ timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+ fail_unless (GES_IS_TIMELINE (timeline));
+
+ g_main_loop_run (mainloop);
+
+ g_free (uri);
+ gst_object_unref (project);
+ gst_object_unref (timeline);
+ g_main_loop_unref (mainloop);
+}
+
+GST_END_TEST;
+#endif
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-project");
+ TCase *tc_chain = tcase_create ("project");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_project_simple);
+ tcase_add_test (tc_chain, test_project_add_assets);
+ tcase_add_test (tc_chain, test_project_load_xges);
+ tcase_add_test (tc_chain, test_project_add_properties);
+ tcase_add_test (tc_chain, test_project_auto_transition);
+ /*tcase_add_test (tc_chain, test_load_xges_and_play); */
+ tcase_add_test (tc_chain, test_project_unexistant_effect);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2016 Sjors Gielen <mixml-ges@sjorsgielen.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+#include <plugins/nle/nleobject.h>
+
+GST_START_TEST (test_tempochange)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_audio;
+ GESEffect *effect;
+ GESTestClip *clip;
+ GESClip *clip2, *clip3;
+ GList *tmp;
+ int found;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_audio = GES_TRACK (ges_audio_track_new ());
+
+ ges_timeline_add_track (timeline, track_audio);
+ ges_timeline_add_layer (timeline, layer);
+
+ /* Add a 9-second clip */
+ clip = ges_test_clip_new ();
+ g_object_set (clip, "duration", 9 * GST_SECOND, NULL);
+ ges_layer_add_clip (layer, (GESClip *) clip);
+
+ /* Split it after 3 seconds */
+ clip2 = ges_clip_split ((GESClip *) clip, 3 * GST_SECOND);
+
+ /* Add pitch effect to play 1.5 times faster */
+ effect = ges_effect_new ("pitch tempo=1.5");
+
+ fail_unless (GES_IS_EFFECT (effect));
+ fail_unless (ges_container_add (GES_CONTAINER (clip2),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) !=
+ NULL);
+
+ assert_equals_int (GES_TRACK_ELEMENT (effect)->active, TRUE);
+
+ /* Split clip again after 6 seconds (note: this is timeline time) */
+ clip3 = ges_clip_split (clip2, 6 * GST_SECOND);
+
+ // note: start and duration are counted in timeline time, while
+ // inpoint is counted in media time.
+ fail_unless_equals_int64 (_START (clip), 0 * GST_SECOND);
+ fail_unless_equals_int64 (_INPOINT (clip), 0 * GST_SECOND);
+ fail_unless_equals_int64 (_DURATION (clip), 3 * GST_SECOND);
+
+ fail_unless_equals_int64 (_START (clip2), 3 * GST_SECOND);
+ fail_unless_equals_int64 (_INPOINT (clip2), 3 * GST_SECOND);
+ fail_unless_equals_int64 (_DURATION (clip2), 3 * GST_SECOND);
+
+ fail_unless_equals_int64 (_START (clip3), 6 * GST_SECOND);
+ fail_unless_equals_int64 (_INPOINT (clip3), 7.5 * GST_SECOND);
+ fail_unless_equals_int64 (_DURATION (clip3), 3 * GST_SECOND);
+
+#define MDF_OF_CLIP(x) \
+ ((NleObject *) ges_track_element_get_nleobject \
+ (ges_clip_find_track_element (GES_CLIP (x), track_audio, \
+ G_TYPE_NONE)))->media_duration_factor
+
+ fail_unless_equals_float (MDF_OF_CLIP (clip), 1.0);
+ fail_unless_equals_float (MDF_OF_CLIP (clip2), 1.5);
+ fail_unless_equals_float (MDF_OF_CLIP (clip3), 1.5);
+
+ found = 0;
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip2); tmp; tmp = tmp->next) {
+ // A clip may have children other than the effect we added. As such, we
+ // need to find the child which is the pitch effect in order to check its
+ // value.
+ GstElement *nle = ges_track_element_get_nleobject (tmp->data);
+ if (strncmp (GST_OBJECT_NAME (nle), "GESEffect:", 10) == 0) {
+ fail_if (found);
+ found = 1;
+ fail_unless_equals_float (((NleObject *) nle)->media_duration_factor,
+ 1.5);
+ }
+ }
+
+ fail_unless (found);
+
+ found = 0;
+ for (tmp = GES_CONTAINER_CHILDREN (clip3); tmp; tmp = tmp->next) {
+ GstElement *nle = ges_track_element_get_nleobject (tmp->data);
+ if (strncmp (GST_OBJECT_NAME (nle), "GESEffect:", 10) == 0) {
+ fail_if (found);
+ found = 1;
+ fail_unless_equals_float (((NleObject *) nle)->media_duration_factor,
+ 1.5);
+ }
+ }
+
+ fail_unless (found);
+
+ ges_layer_remove_clip (layer, (GESClip *) clip);
+ ges_layer_remove_clip (layer, clip2);
+ ges_layer_remove_clip (layer, clip3);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges");
+ TCase *tc_chain = tcase_create ("tempochange");
+ GstPluginFeature *pitch = gst_registry_find_feature (gst_registry_get (),
+ "pitch", GST_TYPE_ELEMENT_FACTORY);
+
+ if (pitch) {
+ gst_object_unref (pitch);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_tempochange);
+ }
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+<ges version="0.1">
+ <project metadatas='metadatas, name=(string)"Example\ project";'>
+ <encoding-profiles>
+ <encoding-profile name='first_profile' description='(null)' type='container' format='application/ogg'>
+ <stream-profile parent='first_profile' id='0' type='video' presence='0' format='video/x-h264' pass='0' variableframerate='0' />
+ <stream-profile parent='first_profile' id='1' type='audio' presence='0' format='audio/x-aac' />
+ </encoding-profile>
+ </encoding-profiles>
+ <resources>
+ <asset id="file:///test/not/exisiting"
+ extractable-type-name="GESUriClip"/>
+ </resources>
+ <timeline properties='properties, auto-transition=(boolean)false'>
+ <track track-type="2" caps="audio/x-raw" track-id="0"/>
+ <track track-type="4" caps="video/x-raw" track-id="1"/>
+ <layer priority="0" properties='properties, auto-transition=(boolean)false;' metadatas='metadatas, a=(guint)3'>
+ <clip id="0" layer-priority='0' asset-id="file:///test/not/exisiting" type-name="GESUriClip" track-types="6" start="0" duration="1000000000">
+ <effect asset-id='agingtv' clip-id='0' type-name='GESEffect' track-type='4' track-id='1' metadatas='metadatas;' children-properties='properties, scratch-lines=(uint)12;'/>
+ </clip>
+ </layer>
+ <layer priority="1" properties='properties, auto-transition=(boolean)false;'>
+ <clip id="1" asset-id="file:///test/not/exisiting" layer-priority="1"
+ type-name="GESUriClip" track-types="2" start="1000000000" duration="1000000000">
+ </clip>
+ </layer>
+ </timeline>
+ </project>
+</ges>
--- /dev/null
+<ges version="0.1">
+ <project metadatas='metadatas, name=(string)"Example\ project";'>
+ <encoding-profiles>
+ <encoding-profile name='first_profile' description='(null)' type='container' format='application/ogg'>
+ <stream-profile parent='first_profile' enabled='0' id='0' type='video' presence='0' format='video/x-h264' pass='0' variableframerate='0' />
+ <stream-profile parent='first_profile' id='1' type='audio' presence='0' format='audio/x-aac' />
+ </encoding-profile>
+ </encoding-profiles>
+ <resources>
+ <asset id="file:///test/not/exisiting"
+ extractable-type-name="GESUriClip"/>
+ </resources>
+ <timeline>
+ <track track-type="2" caps="audio/x-raw" track-id="0"/>
+ <track track-type="4" caps="video/x-raw" track-id="1"/>
+ <layer priority="0" properties='properties, auto-transition=(boolean)true;' metadatas='metadatas, a=(guint)3'>
+ <clip id="0" layer-priority='0' asset-id="file:///test/not/exisiting" type-name="GESUriClip" track-types="6" start="0" duration="1000000000">
+ <effect asset-id='agingtv' clip-id='0' type-name='GESEffect' track-type='4' track-id='1' metadatas='metadatas;' children-properties='properties, scratch-lines=(uint)12;'/>
+ </clip>
+ </layer>
+ <layer priority="1" properties='properties, auto-transition=(boolean)true;'>
+ <clip id="1" asset-id="file:///test/not/exisiting" layer-priority="1"
+ type-name="GESUriClip" track-types="2" start="1000000000" duration="1000000000">
+ </clip>
+ </layer>
+ </timeline>
+ </project>
+</ges>
--- /dev/null
+<ges version="0.1">
+ <project metadatas='metadatas, name=(string)"Example\ project";'>
+ <encoding-profiles>
+ <encoding-profile name='first_profile' description='(null)' type='container' format='application/ogg'>
+ <stream-profile parent='first_profile' id='0' type='video' presence='0' format='video/x-h264' pass='0' variableframerate='0' />
+ <stream-profile parent='first_profile' id='1' type='audio' presence='0' format='audio/x-aac' />
+ </encoding-profile>
+ </encoding-profiles>
+ <resources>
+ <asset id="file:///test/not/exisiting"
+ extractable-type-name="GESUriClip"/>
+ </resources>
+ <timeline>
+ <track track-type="2" caps="audio/x-raw" track-id="0"/>
+ <track track-type="4" caps="video/x-raw" track-id="1"/>
+ <layer priority="0" properties='properties, auto-transition=(boolean)true;' metadatas='metadatas, a=(guint)3'>
+ <clip id="0" layer-priority='0' asset-id="file:///test/not/exisiting" type-name="GESUriClip" track-types="6" start="0" duration="10000000000">
+ <effect asset-id='agingtv' clip-id='0' type-name='GESEffect' track-type='4' track-id='1' metadatas='metadatas;' children-properties='properties, scratch-lines=(uint)12;'/>
+ </clip>
+ </layer>
+ </timeline>
+ </project>
+</ges>
--- /dev/null
+/**
+ * Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <gio/gio.h>
+
+typedef struct _DestroyedObjectStruct
+{
+ GObject *object;
+ gboolean destroyed;
+} DestroyedObjectStruct;
+
+gchar *
+ges_test_get_audio_only_uri (void)
+{
+ gchar *uri;
+ GFile *cfile, *fdir, *f_audio_only;
+
+ cfile = g_file_new_for_path (__FILE__);
+ fdir = g_file_get_parent (cfile);
+
+ f_audio_only = g_file_get_child (fdir, "audio_only.ogg");
+ uri = g_file_get_uri (f_audio_only);
+
+ gst_object_unref (cfile);
+ gst_object_unref (fdir);
+ gst_object_unref (f_audio_only);
+
+ return uri;
+}
+
+gchar *
+ges_test_get_audio_video_uri (void)
+{
+ return ges_test_file_uri ("audio_video.ogg");
+}
+
+gchar *
+ges_test_get_image_uri (void)
+{
+ return ges_test_file_uri ("image.png");
+}
+
+gchar *
+ges_test_file_uri (const gchar * filename)
+{
+ gchar *path, *uri;
+
+ path = g_build_filename (GES_TEST_FILES_PATH, filename, NULL);
+ uri = gst_filename_to_uri (path, NULL);
+
+ g_free (path);
+
+ return uri;
+}
+
+GESPipeline *
+ges_test_create_pipeline (GESTimeline * timeline)
+{
+ GESPipeline *pipeline;
+
+ pipeline = ges_pipeline_new ();
+ fail_unless (ges_pipeline_set_timeline (pipeline, timeline));
+
+ g_object_set (pipeline, "audio-sink", gst_element_factory_make ("fakesink",
+ "test-audiofakesink"), "video-sink",
+ gst_element_factory_make ("fakesink", "test-videofakesink"), NULL);
+
+ return pipeline;
+}
+
+gchar *
+ges_test_file_name (const gchar * filename)
+{
+ return g_strjoin ("/", "file:/", g_get_current_dir (), filename, NULL);
+}
+
+gboolean
+ges_generate_test_file_audio_video (const gchar * filedest,
+ const gchar * audio_enc,
+ const gchar * video_enc,
+ const gchar * mux, const gchar * video_pattern, const gchar * audio_wave)
+{
+ GError *error = NULL;
+ GstElement *pipeline;
+ GstBus *bus;
+ GstMessage *message;
+ gchar *pipeline_str;
+ gboolean done = FALSE;
+ gboolean ret = FALSE;
+
+ if (g_file_test (filedest, G_FILE_TEST_EXISTS)) {
+ GST_INFO ("The file %s already existed.", filedest);
+ return TRUE;
+ }
+
+ pipeline_str = g_strdup_printf ("audiotestsrc num-buffers=430 wave=%s "
+ "%c %s ! %s name=m ! filesink location= %s/%s "
+ "videotestsrc pattern=%s num-buffers=300 ! %s ! m.",
+ audio_wave,
+ audio_enc ? '!' : ' ',
+ audio_enc ? audio_enc : "",
+ mux, g_get_current_dir (), filedest, video_pattern, video_enc);
+
+ pipeline = gst_parse_launch (pipeline_str, &error);
+
+ if (pipeline == NULL)
+ return FALSE;
+
+ g_free (pipeline_str);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+ gst_bus_add_signal_watch (bus);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ while (!done) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_CLOCK_TIME_NONE);
+ if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS) {
+ done = TRUE;
+ ret = TRUE;
+ } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
+ gchar *debug = NULL;
+ GError *err = NULL;
+
+ gst_message_parse_error (message, &err, &debug);
+ done = TRUE;
+ ret = FALSE;
+ GST_ERROR ("Got error %s from %s fron the bus while generation: %s"
+ "debug infos: %s", GST_OBJECT_NAME (message->src), err->message,
+ debug ? debug : "none", filedest);
+ g_clear_error (&err);
+ g_free (debug);
+ }
+ }
+
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ return ret;
+}
+
+static void
+weak_notify (DestroyedObjectStruct * destroyed, GObject ** object)
+{
+ destroyed->destroyed = TRUE;
+}
+
+void
+check_destroyed (GObject * object_to_unref, GObject * first_object, ...)
+{
+ GObject *object;
+ GList *objs = NULL, *tmp;
+ DestroyedObjectStruct *destroyed = g_slice_new0 (DestroyedObjectStruct);
+
+ destroyed->object = object_to_unref;
+ g_object_weak_ref (object_to_unref, (GWeakNotify) weak_notify, destroyed);
+ objs = g_list_prepend (objs, destroyed);
+
+ if (first_object) {
+ va_list varargs;
+
+ object = first_object;
+
+ va_start (varargs, first_object);
+ while (object) {
+ destroyed = g_slice_new0 (DestroyedObjectStruct);
+ destroyed->object = object;
+ g_object_weak_ref (object, (GWeakNotify) weak_notify, destroyed);
+ objs = g_list_prepend (objs, destroyed);
+ object = va_arg (varargs, GObject *);
+ }
+ va_end (varargs);
+ }
+ gst_object_unref (object_to_unref);
+
+ for (tmp = objs; tmp; tmp = tmp->next) {
+ fail_unless (((DestroyedObjectStruct *) tmp->data)->destroyed == TRUE,
+ "%p is not destroyed", ((DestroyedObjectStruct *) tmp->data)->object);
+ g_slice_free (DestroyedObjectStruct, tmp->data);
+ }
+ g_list_free (objs);
+
+}
+
+static gboolean
+my_bus_callback (GstBus * bus, GstMessage * message, GMainLoop * loop)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:{
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error (message, &err, &debug);
+
+ g_assert_no_error (err);
+ g_error_free (err);
+ g_free (debug);
+ g_main_loop_quit (loop);
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ GST_INFO ("EOS\n");
+ g_main_loop_quit (loop);
+ break;
+ default:
+ /* unhandled message */
+ break;
+ }
+ return TRUE;
+}
+
+gboolean
+play_timeline (GESTimeline * timeline)
+{
+ GstBus *bus;
+ GESPipeline *pipeline;
+ GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+
+ ges_timeline_commit (timeline);
+ pipeline = ges_pipeline_new ();
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_watch (bus, (GstBusFunc) my_bus_callback, loop);
+ gst_object_unref (bus);
+
+ ges_pipeline_set_timeline (pipeline, timeline);
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+ gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, -1);
+
+ g_main_loop_run (loop);
+
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, -1);
+
+ gst_object_unref (pipeline);
+
+ return TRUE;
+}
+
+gchar *
+ges_test_get_tmp_uri (const gchar * filename)
+{
+ gchar *location, *uri;
+
+ location = g_build_filename (g_get_tmp_dir (), filename, NULL);
+
+ uri = g_strconcat ("file://", location, NULL);
+ g_free (location);
+
+ return uri;
+}
+
+void
+print_timeline (GESTimeline * timeline)
+{
+ GList *layer, *clip, *clips, *group;
+
+ g_printerr
+ ("\n\n=========================== GESTimeline: %p ==================\n",
+ timeline);
+ for (layer = timeline->layers; layer; layer = layer->next) {
+ clips = ges_layer_get_clips (layer->data);
+
+ g_printerr ("layer %04d: ", ges_layer_get_priority (layer->data));
+ for (clip = clips; clip; clip = clip->next) {
+ g_printerr ("{ %s [ %" G_GUINT64_FORMAT "(%" G_GUINT64_FORMAT ") %"
+ G_GUINT64_FORMAT "] } ", GES_TIMELINE_ELEMENT_NAME (clip->data),
+ GES_TIMELINE_ELEMENT_START (clip->data),
+ GES_TIMELINE_ELEMENT_INPOINT (clip->data),
+ GES_TIMELINE_ELEMENT_END (clip->data));
+ }
+ if (layer->next)
+ g_printerr ("\n--------------------------------------------------\n");
+
+ g_list_free_full (clips, gst_object_unref);
+ }
+
+ if (ges_timeline_get_groups (timeline)) {
+ g_printerr ("\n--------------------------------------------------\n");
+ g_printerr ("\nGROUPS:");
+ g_printerr ("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+ }
+
+ for (group = ges_timeline_get_groups (timeline); group; group = group->next) {
+ g_printerr ("%" GES_FORMAT ": ", GES_ARGS (group->data));
+ for (clip = GES_CONTAINER_CHILDREN (group->data); clip; clip = clip->next)
+ g_printerr ("[ %s ]", GES_TIMELINE_ELEMENT_NAME (clip->data));
+ }
+
+ g_printerr
+ ("\n=====================================================================\n");
+}
--- /dev/null
+/**
+ * Gstreamer Editing Services
+ *
+ * Copyright (C) <2012> 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GES_TEST_UTILS
+#define _GES_TEST_UTILS
+
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+GESPipeline * ges_test_create_pipeline (GESTimeline *timeline);
+/* The first 2 NLE priorities are used for:
+ * 0- The Mixing element
+ * 1- The Gaps
+ */
+#define MIN_NLE_PRIO 2
+#define TRANSITIONS_HEIGHT 1
+#define LAYER_HEIGHT 1000
+
+gchar * ges_test_get_tmp_uri (const gchar * filename);
+gchar * ges_test_get_audio_only_uri (void);
+gchar * ges_test_get_audio_video_uri (void);
+gchar * ges_test_get_image_uri (void);
+gchar * ges_test_file_uri (const gchar *filename);
+
+void check_destroyed (GObject *object_to_unref, GObject *first_object, ...) G_GNUC_NULL_TERMINATED;
+gchar * ges_test_file_name (const gchar *filename);
+gboolean
+ges_generate_test_file_audio_video (const gchar * filedest,
+ const gchar * audio_enc,
+ const gchar * video_enc,
+ const gchar * mux, const gchar * video_pattern, const gchar * audio_wave);
+gboolean
+play_timeline (GESTimeline * timeline);
+
+#define nle_object_check(nleobj, start, duration, mstart, mduration, priority, active) { \
+ guint64 pstart, pdur, inpoint, pprio, pact; \
+ g_object_get (nleobj, "start", &pstart, "duration", &pdur, \
+ "inpoint", &inpoint, "priority", &pprio, "active", &pact, \
+ NULL); \
+ assert_equals_uint64 (pstart, start); \
+ assert_equals_uint64 (pdur, duration); \
+ assert_equals_uint64 (inpoint, mstart); \
+ assert_equals_int (pprio, priority); \
+ assert_equals_int (pact, active); \
+ }
+
+/* copied from nle */
+#define fail_error_message(msg) \
+ G_STMT_START { \
+ GError *error; \
+ gst_message_parse_error(msg, &error, NULL); \
+ fail_unless(FALSE, "Error Message from %s : %s", \
+ GST_OBJECT_NAME (GST_MESSAGE_SRC(msg)), error->message); \
+ g_error_free (error); \
+ } G_STMT_END;
+
+#define assert_is_type(object, type) \
+G_STMT_START { \
+ fail_unless (g_type_is_a(G_OBJECT_TYPE(object), type), \
+ "%s is not a %s", G_OBJECT_TYPE_NAME(object), \
+ g_type_name (type)); \
+} G_STMT_END;
+
+#define _START(obj) GES_TIMELINE_ELEMENT_START (obj)
+#define _DURATION(obj) GES_TIMELINE_ELEMENT_DURATION (obj)
+#define _INPOINT(obj) GES_TIMELINE_ELEMENT_INPOINT (obj)
+#define _PRIORITY(obj) GES_TIMELINE_ELEMENT_PRIORITY (obj)
+#ifndef _END
+#define _END(obj) (_START(obj) + _DURATION(obj))
+#endif
+
+#define CHECK_OBJECT_PROPS(obj, start, inpoint, duration) {\
+ fail_unless (_START (obj) == start, "%s start is %" GST_TIME_FORMAT " != %" GST_TIME_FORMAT, GES_TIMELINE_ELEMENT_NAME(obj), GST_TIME_ARGS (_START(obj)), GST_TIME_ARGS (start));\
+ fail_unless (_INPOINT (obj) == inpoint, "%s inpoint is %" GST_TIME_FORMAT " != %" GST_TIME_FORMAT, GES_TIMELINE_ELEMENT_NAME(obj), GST_TIME_ARGS (_INPOINT(obj)), GST_TIME_ARGS (inpoint));\
+ fail_unless (_DURATION (obj) == duration, "%s duration is %" GST_TIME_FORMAT " != %" GST_TIME_FORMAT, GES_TIMELINE_ELEMENT_NAME(obj), GST_TIME_ARGS (_DURATION(obj)), GST_TIME_ARGS (duration));\
+}
+
+#define check_layer(clip, layer_prio) { \
+ fail_unless (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip) == (layer_prio), \
+ "%s in layer %d instead of %d", GES_TIMELINE_ELEMENT_NAME (clip), \
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip), layer_prio); \
+}
+
+#define GES_TIMELINE_ELEMENT_FORMAT \
+ "s<%p>" \
+ " [ %" GST_TIME_FORMAT \
+ " (%" GST_TIME_FORMAT \
+ ") - %" GST_TIME_FORMAT "(%" GST_TIME_FORMAT") layer: %" G_GINT32_FORMAT "] "
+
+#define GES_TIMELINE_ELEMENT_ARGS(element) \
+ GES_TIMELINE_ELEMENT_NAME(element), element, \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_START(element)), \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_INPOINT(element)), \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_DURATION(element)), \
+ GST_TIME_ARGS(GES_TIMELINE_ELEMENT_MAX_DURATION(element)), \
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY(element)
+#define GES_ARGS GES_TIMELINE_ELEMENT_ARGS
+#define GES_FORMAT GES_TIMELINE_ELEMENT_FORMAT
+
+void print_timeline(GESTimeline *timeline);
+
+#endif /* _GES_TEST_UTILS */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2012> 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+#define DEEP_CHECK(element, start, inpoint, duration) \
+{ \
+ GList *track_elements, *tmp; \
+ CHECK_OBJECT_PROPS (element, start, inpoint, duration) \
+ \
+ track_elements = GES_CONTAINER_CHILDREN (element); \
+ for (tmp = track_elements; tmp; tmp = tmp->next) { \
+ CHECK_OBJECT_PROPS (tmp->data, start, inpoint, duration) \
+ } \
+}
+
+#define CHECK_CLIP(element, start, inpoint, duration, layer_prio) \
+{ \
+ DEEP_CHECK(element, start, inpoint, duration);\
+ check_layer (element, layer_prio); \
+}\
+
+
+GST_START_TEST (test_basic_timeline_edition)
+{
+ GESAsset *asset;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *trackelement, *trackelement1, *trackelement2;
+ GESContainer *clip, *clip1, *clip2;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_audio_track_new ());
+ fail_unless (track != NULL);
+
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+ fail_unless (GES_IS_ASSET (asset));
+
+ /**
+ * Our timeline
+ *
+ * inpoints 0------- 0-------- 0-----------
+ * | clip | | clip1 | | clip2 |
+ * time 0------- 10 --------20 50---------60
+ */
+ clip = GES_CONTAINER (ges_layer_add_asset (layer, asset, 0, 0, 10,
+ GES_TRACK_TYPE_UNKNOWN));
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (GES_IS_TRACK_ELEMENT (trackelement));
+
+ clip1 = GES_CONTAINER (ges_layer_add_asset (layer, asset, 10, 0, 10,
+ GES_TRACK_TYPE_UNKNOWN));
+ trackelement1 = GES_CONTAINER_CHILDREN (clip1)->data;
+ fail_unless (GES_IS_TRACK_ELEMENT (trackelement1));
+
+ clip2 = GES_CONTAINER (ges_layer_add_asset (layer, asset, 50, 0, 60,
+ GES_TRACK_TYPE_UNKNOWN));
+ g_object_unref (asset);
+ trackelement2 = GES_CONTAINER_CHILDREN (clip2)->data;
+ fail_unless (GES_IS_TRACK_ELEMENT (trackelement2));
+
+ CHECK_OBJECT_PROPS (trackelement, 0, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 50, 0, 60);
+
+ /**
+ * Simple rippling clip to: 10
+ *
+ * New timeline:
+ * ------------
+ *
+ * inpoints 0------- 0-------- 0-----------
+ * | clip | | clip1 | | clip2 |
+ * time 10------- 20 --------30 60---------120
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_NONE, 10) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 60, 0, 60);
+
+
+ /* FIXME find a way to check that we are using the same MovingContext
+ * inside the GESTrack */
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_NONE, 40) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 40, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 80, 0, 60);
+
+ /**
+ * Rippling clip1 back to: 20 (getting to the exact same timeline as before
+ */
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_NONE, 20) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 60, 0, 60);
+
+ /**
+ * Simple move clip to: 27 and clip2 to 35
+ *
+ * New timeline:
+ * ------------
+ * 0------------
+ * inpoints 0-------|--- clip 0--|----------
+ * | clip1 27 -|-----|-37 clip2 |
+ * time 20-----------30 35-------------120
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 27) == TRUE);
+ fail_unless (ges_container_edit (clip2, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 35) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 27, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+
+ /**
+ * Trim start clip to: 32 and clip2 to 35
+ *
+ * New timeline:
+ * ------------
+ * 5--------
+ * inpoints 0----------- | clip 0--|----------
+ * | clip1 | 32----|-37 clip2 |
+ * time 20-----------30 35-------------120
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_START, 32) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 5);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+
+ /* Ripple end clip to 42
+ * New timeline:
+ * ------------
+ * 5--------
+ * inpoints 0----------- | clip 0--|----------
+ * | clip1 | 32----|-42 clip2 |
+ * time 20-----------30 35-------------120
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 42) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+
+ /**
+ * New timeline:
+ * ------------
+ * inpoints 0------- 5-------- 0-----------
+ * | clip1 | | clip1 || clip2 |
+ * time 20-------30 32--------52 ---------112
+ */
+ fail_unless (ges_container_edit (clip2, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 42) == TRUE);
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 52) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 20);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 52, 0, 60);
+
+ /**
+ * New timeline:
+ * ------------
+ * inpoints 0------- 5-------- 0------------
+ * | clip1 | | clip || clip2 |
+ * time 20-------40 42--------62 ---------122
+ */
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 40) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 42, 5, 20);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /**
+ * New timeline:
+ * ------------
+ * inpoints 0------- 3-------- 0------------
+ * | clip1 || clip || clip2 |
+ * time 20-------40 --------62 ---------122
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_START, 40) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 40, 3, 22);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+ /**
+ * New timeline:
+ * ------------
+ * inpoints 0------- 0-------- 0-----------
+ * | clip1 || clip || clip2 |
+ * time 20------ 25 ------ 62 ---------122
+ */
+ fail_if (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_START, 25));
+ CHECK_OBJECT_PROPS (trackelement, 40, 3, 22);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Make sure that not doing anything when not able to roll */
+ fail_if (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_END, 65) == TRUE, 0);
+ CHECK_OBJECT_PROPS (trackelement, 40, 3, 22);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_snapping)
+{
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESTrackElement *trackelement, *trackelement1, *trackelement2;
+ GESContainer *clip, *clip1, *clip2;
+ GESLayer *layer;
+ GList *trackelements;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip = GES_CONTAINER (ges_test_clip_new ());
+ clip1 = GES_CONTAINER (ges_test_clip_new ());
+ clip2 = GES_CONTAINER (ges_test_clip_new ());
+
+ fail_unless (clip && clip1 && clip2);
+
+ /**
+ * Our timeline
+ * ------------
+ * inpoints 0------- 0-------- 0-----------
+ * | clip1 || clip || clip2 |
+ * time 20------ 25 ------ 62 ---------122
+ */
+ g_object_set (clip, "start", (guint64) 25, "duration", (guint64) 37,
+ "in-point", (guint64) 0, NULL);
+ g_object_set (clip1, "start", (guint64) 20, "duration", (guint64) 15,
+ "in-point", (guint64) 0, NULL);
+ g_object_set (clip2, "start", (guint64) 62, "duration", (guint64) 60,
+ "in-point", (guint64) 0, NULL);
+
+ fail_unless ((layer = ges_timeline_append_layer (timeline)) != NULL);
+ assert_equals_int (ges_layer_get_priority (layer), 0);
+
+
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
+ fail_unless ((trackelements = GES_CONTAINER_CHILDREN (clip)) != NULL);
+ fail_unless ((trackelement =
+ GES_TRACK_ELEMENT (trackelements->data)) != NULL);
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+ assert_equals_uint64 (_DURATION (trackelement), 37);
+
+ ASSERT_OBJECT_REFCOUNT (trackelement, "track + timeline + clip", 3);
+ ASSERT_OBJECT_REFCOUNT (clip, "layer + timeline", 2);
+
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip1)));
+ fail_unless ((trackelements = GES_CONTAINER_CHILDREN (clip1)) != NULL);
+ fail_unless ((trackelement1 =
+ GES_TRACK_ELEMENT (trackelements->data)) != NULL);
+ fail_unless (ges_track_element_get_track (trackelement1) == track);
+ assert_equals_uint64 (_DURATION (trackelement1), 15);
+
+ /* Same ref logic */
+ ASSERT_OBJECT_REFCOUNT (trackelement1, "First trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (clip1, "First clip", 2);
+
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip2)));
+ fail_unless ((trackelements = GES_CONTAINER_CHILDREN (clip2)) != NULL);
+ fail_unless ((trackelement2 =
+ GES_TRACK_ELEMENT (trackelements->data)) != NULL);
+ fail_unless (ges_track_element_get_track (trackelement2) == track);
+ assert_equals_uint64 (_DURATION (trackelement2), 60);
+
+ /* Same ref logic */
+ ASSERT_OBJECT_REFCOUNT (trackelement2, "First trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (clip2, "First clip", 2);
+
+ /* Snaping to edge, so no move */
+ g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_END, 27) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 5);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Snaping to edge, so no move */
+ ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, 27);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 5);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /**
+ * New timeline:
+ * ------------
+ * 0----------- 0-------------
+ * inpoints 0-------|-- clip || clip2 |
+ * | clip1 25-|------- 62 -----------122
+ * time 20----------30
+ */
+ g_object_set (timeline, "snapping-distance", (guint64) 0, NULL);
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip1), 10);
+ DEEP_CHECK (clip, 25, 0, 37);
+ DEEP_CHECK (clip1, 20, 0, 10);
+ DEEP_CHECK (clip2, 62, 0, 60);
+
+ /* clip and clip1 would fully overlap ... forbiden */
+ fail_if (ges_timeline_element_roll_end (GES_TIMELINE_ELEMENT (clip1), 62));
+ DEEP_CHECK (clip, 25, 0, 37);
+ DEEP_CHECK (clip1, 20, 0, 10);
+ DEEP_CHECK (clip2, 62, 0, 60);
+ fail_if (ges_timeline_element_roll_end (GES_TIMELINE_ELEMENT (clip1),
+ 72) == TRUE);
+ DEEP_CHECK (clip, 25, 0, 37);
+ DEEP_CHECK (clip1, 20, 0, 10);
+ DEEP_CHECK (clip2, 62, 0, 60);
+
+ /**
+ * 30-------+0-------------+
+ * inpoints 0-----------5 clip || clip2 |
+ * | clip1 |------- 62 -----------122
+ * time 20----------30
+ */
+ g_object_set (timeline, "snapping-distance", (guint64) 4, NULL);
+ fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (clip),
+ 28) == TRUE);
+ DEEP_CHECK (clip, 30, 5, 32);
+ DEEP_CHECK (clip1, 20, 0, 10);
+ DEEP_CHECK (clip2, 62, 0, 60);
+
+ /**
+ * 30-------+0-------------+
+ * inpoints 0-----------5 clip || clip2 |
+ * | clip1 |------- 62 -----------122
+ * time 20----------30
+ */
+ fail_unless (ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip2),
+ 5));
+ fail_unless (ges_timeline_element_roll_start (GES_TIMELINE_ELEMENT (clip2),
+ 60));
+ DEEP_CHECK (clip, 30, 5, 32);
+ DEEP_CHECK (clip1, 20, 0, 10);
+ DEEP_CHECK (clip2, 62, 5, 60);
+
+ /**
+ * 30-------+0-------------+
+ * inpoints 0-----------5 clip || clip2 |
+ * | clip1 |------- 62 -----------122
+ * time 20----------30
+ */
+ /* Moving clip1 to 26 would lead to snapping to 30, and clip1 and clip
+ * would fully overlap */
+ fail_if (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 26) == TRUE);
+ DEEP_CHECK (clip, 30, 5, 32);
+ DEEP_CHECK (clip1, 20, 0, 10);
+ DEEP_CHECK (clip2, 62, 5, 60);
+
+ /**
+ * 30-------+0-------------+
+ * inpoints 5 clip || clip2 |-------------+
+ * +------- 62 -----------122 clip1 |
+ * time +------------132
+ * Check that clip1 snaps with the end of clip2 */
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 125) == TRUE);
+ DEEP_CHECK (clip, 30, 5, 32);
+ DEEP_CHECK (clip1, 122, 0, 10);
+ DEEP_CHECK (clip2, 62, 5, 60);
+
+ /* Check we didn't lose/screwed any references */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "First trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (trackelement1, "Second trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (trackelement2, "Third trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (clip, "First clip", 2);
+ ASSERT_OBJECT_REFCOUNT (clip1, "Second clip", 2);
+ ASSERT_OBJECT_REFCOUNT (clip2, "Third clip", 2);
+
+ check_destroyed (G_OBJECT (timeline), G_OBJECT (trackelement),
+ trackelement1, trackelement2, clip, clip1, clip2, layer, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+asset_added_cb (GESProject * project, GESAsset * asset, void *mainloop)
+{
+ GstDiscovererInfo *info;
+
+ info = ges_uri_clip_asset_get_info (GES_URI_CLIP_ASSET (asset));
+ fail_unless (GST_IS_DISCOVERER_INFO (info));
+
+ g_main_loop_quit ((GMainLoop *) mainloop);
+}
+
+GST_START_TEST (test_simple_triming)
+{
+ GList *assets, *tmp;
+ GMainLoop *mainloop;
+ GESClipAsset *asset;
+ GESProject *project;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTimelineElement *element;
+ gchar *uri;
+
+ ges_init ();
+
+ uri = ges_test_file_uri ("audio_video.ogg");
+
+ project = ges_project_new (NULL);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ g_signal_connect (project, "asset-added", (GCallback) asset_added_cb,
+ mainloop);
+ ges_project_create_asset (project, uri, GES_TYPE_URI_CLIP);
+ g_free (uri);
+
+ g_main_loop_run (mainloop);
+
+ /* the asset is now loaded */
+ timeline = ges_timeline_new_audio_video ();
+ assets = ges_project_list_assets (project, GES_TYPE_CLIP);
+
+ assert_equals_int (g_list_length (assets), 1);
+ asset = assets->data;
+
+ layer = ges_layer_new ();
+ ges_timeline_add_layer (timeline, layer);
+
+ ges_layer_add_asset (layer, GES_ASSET (asset), 0, 0, 10,
+ ges_clip_asset_get_supported_formats (asset));
+ g_list_free_full (assets, g_object_unref);
+
+ tmp = ges_layer_get_clips (layer);
+ element = tmp->data;
+
+ DEEP_CHECK (element, 0, 0, 10);
+ ges_container_edit (GES_CONTAINER (element), NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_START, 5);
+ DEEP_CHECK (element, 5, 5, 5);
+ g_list_free_full (tmp, g_object_unref);
+
+ g_main_loop_unref (mainloop);
+ gst_object_unref (timeline);
+ gst_object_unref (project);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_timeline_edition_mode)
+{
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESTrackElement *trackelement, *trackelement1, *trackelement2;
+ GESContainer *clip, *clip1, *clip2;
+ GESLayer *layer, *layer1, *layer2;
+ GList *trackelements, *layers, *tmp;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ clip = GES_CONTAINER (ges_test_clip_new ());
+ clip1 = GES_CONTAINER (ges_test_clip_new ());
+ clip2 = GES_CONTAINER (ges_test_clip_new ());
+
+ fail_unless (clip && clip1 && clip2);
+
+ /**
+ * Our timeline
+ *
+ * 0-------
+ * layer: | clip |
+ * 0-------10
+ *
+ * 0-------- 0-----------
+ * layer1: | clip1 | | clip2 |
+ * 10--------20 50---------60
+ */
+ g_object_set (clip, "start", (guint64) 0, "duration", (guint64) 10,
+ "in-point", (guint64) 0, NULL);
+ g_object_set (clip1, "start", (guint64) 10, "duration", (guint64) 10,
+ "in-point", (guint64) 0, NULL);
+ g_object_set (clip2, "start", (guint64) 50, "duration", (guint64) 60,
+ "in-point", (guint64) 0, NULL);
+
+ fail_unless ((layer = ges_timeline_append_layer (timeline)) != NULL);
+ assert_equals_int (ges_layer_get_priority (layer), 0);
+
+
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
+ fail_unless ((trackelements = GES_CONTAINER_CHILDREN (clip)) != NULL);
+ fail_unless ((trackelement =
+ GES_TRACK_ELEMENT (trackelements->data)) != NULL);
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+ assert_equals_uint64 (_DURATION (trackelement), 10);
+
+ /* Add a new layer and add clipects to it */
+ fail_unless ((layer1 = ges_timeline_append_layer (timeline)) != NULL);
+ fail_unless (layer != layer1);
+ assert_equals_int (ges_layer_get_priority (layer1), 1);
+
+ fail_unless (ges_layer_add_clip (layer1, GES_CLIP (clip1)));
+ fail_unless ((trackelements = GES_CONTAINER_CHILDREN (clip1)) != NULL);
+ fail_unless ((trackelement1 =
+ GES_TRACK_ELEMENT (trackelements->data)) != NULL);
+ fail_unless (ges_track_element_get_track (trackelement1) == track);
+ assert_equals_uint64 (_DURATION (trackelement1), 10);
+
+ fail_unless (ges_layer_add_clip (layer1, GES_CLIP (clip2)));
+ fail_unless ((trackelements = GES_CONTAINER_CHILDREN (clip2)) != NULL);
+ fail_unless ((trackelement2 =
+ GES_TRACK_ELEMENT (trackelements->data)) != NULL);
+ fail_unless (ges_track_element_get_track (trackelement2) == track);
+ assert_equals_uint64 (_DURATION (trackelement2), 60);
+
+ /**
+ * Simple rippling clip to: 10
+ *
+ * New timeline:
+ * ------------
+ *
+ * inpoints 0-------
+ * | clip |
+ * time 10-------20
+ *
+ * 0-------- 0-----------
+ * | clip1 | | clip2 |
+ * 20--------30 60--------120
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_NONE, 10) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 60, 0, 60);
+
+
+ /* FIXME find a way to check that we are using the same MovingContext
+ * inside the GESTimeline */
+ fail_unless (ges_container_edit (clip1, NULL, 3, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_NONE, 40) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 40, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 80, 0, 60);
+ layer2 = ges_clip_get_layer (GES_CLIP (clip1));
+ assert_equals_int (ges_layer_get_priority (layer2), 3);
+ /* clip2 should have moved layer too */
+ fail_unless (ges_clip_get_layer (GES_CLIP (clip2)) == layer2);
+ /* We got 2 reference to the same clipect, unref them */
+ gst_object_unref (layer2);
+ gst_object_unref (layer2);
+
+ /**
+ * Rippling clip1 back to: 20 (getting to the exact same timeline as before
+ */
+ fail_unless (ges_container_edit (clip1, NULL, 1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_NONE, 20) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 10, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 60, 0, 60);
+ layer2 = ges_clip_get_layer (GES_CLIP (clip1));
+ assert_equals_int (ges_layer_get_priority (layer2), 1);
+ /* clip2 should have moved layer too */
+ fail_unless (ges_clip_get_layer (GES_CLIP (clip2)) == layer2);
+ /* We got 2 reference to the same clipect, unref them */
+ gst_object_unref (layer2);
+ gst_object_unref (layer2);
+
+ /**
+ * Simple move clip to 27 and clip2 to 35
+ *
+ * New timeline:
+ * ------------
+ *
+ * inpoints 0-------
+ * | clip |
+ * time 27-------37
+ *
+ * 0-------- 0-----------
+ * | clip1 | | clip2 |
+ * 20--------30 35---------95
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 27) == TRUE);
+ fail_unless (ges_container_edit (clip2, NULL, -1, GES_EDIT_MODE_NORMAL,
+ GES_EDGE_NONE, 35) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 27, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+
+ /**
+ * Simple trimming start clip to: 32
+ *
+ * New timeline:
+ * ------------
+ *
+ * 5-------
+ * layer 0: | clip |
+ * 32-------37
+ *
+ * 0-------- 0-----------
+ * layer 1 | clip1 | | clip2 |
+ * 20--------30 35---------95
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_START, 32) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 5);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+
+ /* Ripple end clip to 35 and move to layer 2
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0-----------
+ * layer 1: | clip1 | | clip2 |
+ * 20--------30 35---------95
+ *
+ * 5------
+ * layer 2: | clip |
+ * 32------35
+ */
+ fail_unless (ges_container_edit (clip, NULL, 2, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 35) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 3);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip), 2);
+
+ /* Roll end clip to 50
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0-----------
+ * layer 1: | clip1 | | clip2 |
+ * 20--------30 50---------95
+ *
+ * 5------
+ * layer 2: | clip |
+ * 32------50
+ */
+ fail_unless (ges_container_edit (clip, NULL, 2, GES_EDIT_MODE_ROLL,
+ GES_EDGE_END, 50) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 18);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 50, 15, 45);
+ layer = ges_clip_get_layer (GES_CLIP (clip));
+ assert_equals_int (ges_layer_get_priority (layer), 2);
+ gst_object_unref (layer);
+
+ /* Roll end clip back to 35
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0-----------
+ * layer 1: | clip1 | | clip2 |
+ * 20--------30 35---------95
+ *
+ * 5------
+ * layer 2: | clip |
+ * 32------35
+ */
+ fail_unless (ges_container_edit (clip, NULL, 2, GES_EDIT_MODE_ROLL,
+ GES_EDGE_END, 35) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 3);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+ layer = ges_clip_get_layer (GES_CLIP (clip));
+ assert_equals_int (ges_layer_get_priority (layer), 2);
+ gst_object_unref (layer);
+
+ /* Roll end clip back to 35 */
+ /* Can not move to the first layer as clip2 should move to a layer with priority < 0 */
+ fail_if (ges_container_edit (clip, NULL, 0, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 52));
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 3);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 35, 0, 60);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip), 2);
+
+ /* Ripple clip end to 52
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0----------
+ * layer 1: | clip1 | | clip2 |
+ * 20-------30 52---------112
+ *
+ * 5------
+ * layer 2: | clip |
+ * 32------52
+ *
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 52) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 32, 5, 20);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 52, 0, 60);
+ assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip), 2);
+
+
+ /* Little check that we have 4 layers in the timeline */
+ layers = ges_timeline_get_layers (timeline);
+ assert_equals_int (g_list_length (layers), 4);
+
+ /* Some refcount checkings */
+ /* We have a reference to each layer in layers */
+ for (tmp = layers; tmp; tmp = tmp->next)
+ ASSERT_OBJECT_REFCOUNT (layer, "Layer", 2);
+ g_list_free_full (layers, gst_object_unref);
+
+ /* We have 3 references:
+ * track + timeline + clip
+ */
+ ASSERT_OBJECT_REFCOUNT (trackelement, "First trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (trackelement1, "Second trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (trackelement2, "Third trackelement", 3);
+ ASSERT_OBJECT_REFCOUNT (clip, "First clip", 2);
+ ASSERT_OBJECT_REFCOUNT (clip1, "Second clip", 2);
+ ASSERT_OBJECT_REFCOUNT (clip2, "Third clip", 2);
+
+ /* Ripple clip end to 52
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0-----------
+ * layer 0: | clip1 | | clip2 |
+ * 20-------40 62----------112
+ *
+ * 5------
+ * layer 1: | clip |
+ * 42------60
+ *
+ */
+ fail_unless (ges_container_edit (clip1, NULL, 0, GES_EDIT_MODE_RIPPLE,
+ GES_EDGE_END, 40) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 42, 5, 20);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Check that movement between layer has been done properly */
+ layer1 = ges_clip_get_layer (GES_CLIP (clip));
+ layer = ges_clip_get_layer (GES_CLIP (clip1));
+ assert_equals_int (ges_layer_get_priority (layer1), 1);
+ assert_equals_int (ges_layer_get_priority (layer), 0);
+ fail_unless (ges_clip_get_layer (GES_CLIP (clip2)) == layer);
+ gst_object_unref (layer1);
+ /* We have 2 references to @layer that we do not need anymore */ ;
+ gst_object_unref (layer);
+ gst_object_unref (layer);
+
+ /* Trim clip start to 40
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0-----------
+ * layer 0: | clip1 | | clip2 |
+ * 20-------40 62---------112
+ *
+ * 0------
+ * layer 1: | clip |
+ * 40------62
+ *
+ */
+ fail_unless (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_START, 40) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 40, 3, 22);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Roll clip end to 25
+ * New timeline:
+ * ------------
+ *
+ * 0-------- 0-----------
+ * layer 0: | clip1 | | clip2 |
+ * 20-------25 62---------112
+ *
+ * 0------
+ * layer 1: | clip |
+ * 25------62
+ *
+ */
+ ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip), 15);
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_END, 25) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 5);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Make sure that not doing anything when not able to roll */
+ fail_if (ges_container_edit (clip, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_START, 65));
+ fail_if (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_END, 65));
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 5);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Snaping to edge, so no move */
+ g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
+ ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, 27);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 5);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /* Snaping to edge, so no move */
+ ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, 27);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 5);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /**
+ * New timeline:
+ * ------------
+ * 0----------- 0-------------
+ * inpoints 0-------|-- clip || clip2 |
+ * | clip1 25-|------- 62 -----------122
+ * time 20----------30
+ */
+ g_object_set (timeline, "snapping-distance", (guint64) 0, NULL);
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_END, 30) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (trackelement2, 62, 0, 60);
+
+ /**
+ * New timeline
+ * ------------
+ * 0----------
+ * | clip |
+ * 25---------62
+ * -------------------------------------------------
+ * inpoints 0----------------------- 10--------
+ * | clip1 || clip2 |
+ * time 20---------------------- 72 --------122
+ */
+ /* Rolling involves only neighbours that are currently snapping */
+ ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_ROLL, GES_EDGE_END, 62);
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_END, 72) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 20, 0, 52);
+ CHECK_OBJECT_PROPS (trackelement2, 72, 10, 50);
+
+ /* Test Snapping */
+ /**
+ * 0----------
+ * | clip |
+ * 25---------62
+ * inpoints 5--------------- 10--------
+ * | clip1 || clip2 |
+ * time 25------------- 72 --------122
+ */
+ g_object_set (timeline, "snapping-distance", (guint64) 4, NULL);
+ fail_unless (ges_container_edit (clip1, NULL, -1, GES_EDIT_MODE_TRIM,
+ GES_EDGE_START, 28) == TRUE);
+ CHECK_OBJECT_PROPS (trackelement, 25, 0, 37);
+ CHECK_OBJECT_PROPS (trackelement1, 25, 5, 47);
+ CHECK_OBJECT_PROPS (trackelement2, 72, 10, 50);
+
+ fail_if (ges_container_edit (clip2, NULL, -1, GES_EDIT_MODE_ROLL,
+ GES_EDGE_START, 59));
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_groups)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1, *c2, *c3, *c4, *c5;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20---------------30-----------------------70
+ * | +-----------+ |+-----------50 |
+ * L | | C | || C3 | |
+ * | +-----------+ |+-----------+ |
+ * --|-------------------------------------------|-----40----------------|
+ * | +------------+ +-------------+ | +--------60 |
+ * L1 | | C1 | | C2 | | | C4 | |
+ * | +------------+ +-------------+ | +--------+ |
+ * --|-------------------------------------------|-----------------------|
+ * | | +--------+|
+ * L2 | | | c5 ||
+ * | | +--------+|
+ * --+-------------------------------------------+-----------------------+
+ *
+ * L3
+ *
+ * -----------------------------------------------------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c2 = ges_layer_add_asset (layer1, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ clips = g_list_prepend (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_CLIP (c, 0, 0, 10, 0);
+ CHECK_CLIP (c1, 10, 0, 10, 1);
+ CHECK_CLIP (c2, 20, 0, 10, 1);
+ CHECK_OBJECT_PROPS (group, 0, 0, 30);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+
+ CHECK_CLIP (c3, 30, 0, 20, 0);
+ CHECK_CLIP (c4, 40, 0, 20, 1);
+ CHECK_CLIP (c5, 50, 0, 20, 2);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, -1,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 10) == TRUE);
+
+ CHECK_CLIP (c, 10, 0, 10, 0);
+ CHECK_CLIP (c1, 20, 0, 10, 1);
+ CHECK_CLIP (c2, 30, 0, 10, 1);
+ CHECK_CLIP (c3, 40, 0, 20, 0);
+ CHECK_CLIP (c4, 50, 0, 20, 1);
+ CHECK_CLIP (c5, 60, 0, 20, 2);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, 1,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 10) == TRUE);
+ CHECK_CLIP (c, 10, 0, 10, 1);
+ CHECK_CLIP (c1, 20, 0, 10, 2);
+ CHECK_CLIP (c2, 30, 0, 10, 2);
+ CHECK_CLIP (c3, 40, 0, 20, 1);
+ CHECK_CLIP (c4, 50, 0, 20, 2);
+ CHECK_CLIP (c5, 60, 0, 20, 3);
+
+ fail_if (ges_container_edit (GES_CONTAINER (c1), NULL, 2,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 40) == TRUE);
+ fail_if (ges_container_edit (GES_CONTAINER (c1), NULL, 2,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 30) == TRUE);
+ CHECK_CLIP (c, 10, 0, 10, 1);
+ CHECK_CLIP (c1, 20, 0, 10, 2);
+ CHECK_CLIP (c2, 30, 0, 10, 2);
+ CHECK_CLIP (c3, 40, 0, 20, 1);
+ CHECK_CLIP (c4, 50, 0, 20, 2);
+ CHECK_CLIP (c5, 60, 0, 20, 3);
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, 0,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 0) == TRUE);
+ CHECK_CLIP (c, 0, 0, 10, 0);
+ CHECK_CLIP (c1, 10, 0, 10, 1);
+ CHECK_CLIP (c2, 20, 0, 10, 1);
+ CHECK_CLIP (c3, 30, 0, 20, 0);
+ CHECK_CLIP (c4, 40, 0, 20, 1);
+ CHECK_CLIP (c5, 50, 0, 20, 2);
+ CHECK_OBJECT_PROPS (group, 0, 0, 30);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, 0,
+ GES_EDIT_MODE_TRIM, GES_EDGE_START, 5) == TRUE);
+ CHECK_CLIP (c, 5, 5, 5, 0);
+ CHECK_CLIP (c1, 10, 0, 10, 1);
+ CHECK_CLIP (c2, 20, 0, 10, 1);
+ CHECK_CLIP (c3, 30, 0, 20, 0);
+ CHECK_CLIP (c4, 40, 0, 20, 1);
+ CHECK_CLIP (c5, 50, 0, 20, 2);
+ CHECK_OBJECT_PROPS (group, 5, 0, 25);
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_snapping_groups)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1, *c2, *c3, *c4, *c5;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20---------25-----30----------------------70
+ * | +-----------+ | +-----------50 |
+ * L | | C | | | C3 | |
+ * | +-----------+ | +-----------+ |
+ * --|------------------------------------|------------40----------------|
+ * | +------------+ +--------+ +--------60 |
+ * L1 | | C1 | | C2 | | C4 | |
+ * | +------------+ +--------+ +--------+ |
+ * --|------------------------------------+------------------------------|
+ * | +--------+|
+ * L2 | | c5 ||
+ * | +--------+|
+ * --+-------------------------------------------------------------------+
+ *
+ * L3
+ *
+ * -----------------------------------------------------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c2 = ges_layer_add_asset (layer1, asset, 20, 0, 5, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ clips = g_list_prepend (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 5);
+ CHECK_OBJECT_PROPS (group, 0, 0, 25);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ /* c2 should snap with C3 and make the group moving to 5 */
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, -1,
+ GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 3) == TRUE);
+
+ DEEP_CHECK (c, 5, 0, 10);
+ DEEP_CHECK (c1, 15, 0, 10);
+ DEEP_CHECK (c2, 25, 0, 5);
+ DEEP_CHECK (c2, 25, 0, 5);
+ DEEP_CHECK (c4, 40, 0, 20);
+ DEEP_CHECK (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group, 5, 0, 25);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static void
+_set_track_element_width_height (GESTrackElement * trksrc, gint wvalue,
+ gint hvalue)
+{
+ GValue width = { 0 };
+ GValue height = { 0 };
+
+ g_value_init (&width, G_TYPE_INT);
+ g_value_init (&height, G_TYPE_INT);
+ g_value_set_int (&width, wvalue);
+ g_value_set_int (&height, hvalue);
+ if (wvalue >= 0)
+ ges_timeline_element_set_child_property (GES_TIMELINE_ELEMENT (trksrc),
+ "width", &width);
+ if (hvalue >= 0)
+ ges_timeline_element_set_child_property (GES_TIMELINE_ELEMENT (trksrc),
+ "height", &height);
+}
+
+static gboolean
+check_frame_positioner_size (GESClip * clip, gint width, gint height)
+{
+ GESTrackElement *trksrc;
+ GValue val_width = { 0 };
+ GValue val_height = { 0 };
+ gint real_width, real_height;
+
+ trksrc = GES_CONTAINER_CHILDREN (clip)->data;
+ if (!GES_IS_VIDEO_SOURCE (trksrc))
+ return FALSE;
+
+ g_value_init (&val_width, G_TYPE_INT);
+ g_value_init (&val_height, G_TYPE_INT);
+
+ ges_timeline_element_get_child_property (GES_TIMELINE_ELEMENT (trksrc),
+ "width", &val_width);
+ ges_timeline_element_get_child_property (GES_TIMELINE_ELEMENT (trksrc),
+ "height", &val_height);
+
+ real_width = g_value_get_int (&val_width);
+ real_height = g_value_get_int (&val_height);
+
+ assert_equals_int (real_width, width);
+ assert_equals_int (real_height, height);
+
+ return (width == real_width && height == real_height);
+}
+
+GST_START_TEST (test_scaling)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESAsset *asset1, *asset2;
+ GESClip *clip;
+ GESTrack *trackv;
+ GstCaps *caps;
+
+ ges_init ();
+
+ trackv = GES_TRACK (ges_video_track_new ());
+ caps =
+ gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, 1200, "height",
+ G_TYPE_INT, 1000, NULL);
+
+ timeline = ges_timeline_new ();
+ ges_timeline_add_track (timeline, trackv);
+ layer = ges_layer_new ();
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ g_object_set (layer, "auto-transition", TRUE, NULL);
+
+ asset1 = GES_ASSET (ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL));
+ asset2 = GES_ASSET (ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL));
+
+ fail_unless (asset1 != NULL && asset2 != NULL);
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (timeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, "ges-integration-timeline");
+
+ ges_track_set_restriction_caps (trackv, caps);
+ gst_caps_unref (caps);
+
+ GST_DEBUG ("adding clip, should be 1200 x 1000");
+ clip =
+ ges_layer_add_asset (layer, GES_ASSET (asset1), 0 * GST_SECOND,
+ 0 * GST_SECOND, 4 * GST_SECOND, GES_TRACK_TYPE_UNKNOWN);
+ gst_object_unref (asset1);
+ gst_object_unref (asset2);
+ g_object_set (clip, "vpattern", (gint) GES_VIDEO_TEST_PATTERN_SMPTE75, NULL);
+
+ /**
+ * Our track: 1200 x 1000
+ *
+ * 0--------------0
+ * | width : 1200 |
+ * | height: 1000 |
+ * 0--------------2
+ */
+
+ /* clip takes the size set on the track as a default */
+ fail_unless (check_frame_positioner_size (clip, 1200, 1000));
+
+ if (GES_IS_VIDEO_SOURCE (GES_CONTAINER_CHILDREN (clip)->data))
+ _set_track_element_width_height (GES_CONTAINER_CHILDREN (clip)->data, 1024,
+ 768);
+
+ GST_DEBUG ("Setting clip size, should be 1024 x 768");
+
+ /**
+ * Our timeline : 1200 x 1000
+ *
+ * 0--------------0
+ * | width : 1024 |
+ * | height: 768 |
+ * 0--------------2
+ */
+
+ /* Clip has to comply to direct orders */
+ fail_unless (check_frame_positioner_size (clip, 1024, 768));
+
+ GST_DEBUG ("Changing caps, should still be 1024 x 768");
+
+ caps =
+ gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, 1400, "height",
+ G_TYPE_INT, 1200, NULL);
+ ges_track_set_restriction_caps (trackv, caps);
+ gst_caps_unref (caps);
+
+ /**
+ * Our timeline : 1400 x 1200
+ *
+ * 0--------------0
+ * | width : 1024 |
+ * | height: 768 |
+ * 0--------------2
+ */
+
+ /* Clip still has to be the same size */
+ fail_unless (check_frame_positioner_size (clip, 1024, 768));
+
+ GST_DEBUG ("Setting width to 0, should be 1400 x 768");
+
+ /* -1 means don't set it, only valid here */
+ if (GES_IS_VIDEO_SOURCE (GES_CONTAINER_CHILDREN (clip)->data))
+ _set_track_element_width_height (GES_CONTAINER_CHILDREN (clip)->data, 0,
+ -1);
+
+ /**
+ * Our timeline : 1400 x 1200
+ *
+ * 0--------------0
+ * | width : 1400 |
+ * | height: 768 |
+ * 0--------------2
+ */
+
+ /* Clip width was set to 0 so it has to use track width */
+ /* Clip height is still directly set by the user */
+ fail_unless (check_frame_positioner_size (clip, 1400, 768));
+
+ GST_DEBUG ("Setting height to 0, should be 1400 x 1200");
+
+ if (GES_IS_VIDEO_SOURCE (GES_CONTAINER_CHILDREN (clip)->data))
+ _set_track_element_width_height (GES_CONTAINER_CHILDREN (clip)->data, -1,
+ 0);
+
+ /**
+ * Our timeline : 1400 x 1200
+ *
+ * 0--------------0
+ * | width : 1400 |
+ * | height: 1200 |
+ * 0--------------2
+ */
+
+ /* Clip width still has to use track width */
+ /* Clip height was set to 0 so it has to use track height */
+ fail_unless (check_frame_positioner_size (clip, 1400, 1200));
+
+ GST_DEBUG ("Removing restriction on track height, should be 1400 x 240");
+
+ caps =
+ gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, 1400, "height",
+ G_TYPE_INT, 0, NULL);
+ ges_track_set_restriction_caps (trackv, caps);
+ gst_caps_unref (caps);
+
+ /**
+ * Our timeline : 1400 x no restriction
+ *
+ * 0--------------0
+ * | width : 1400 |
+ * | height: 240 |
+ * 0--------------2
+ */
+
+ /* Clip width still has to use track width */
+ /* Clip height was set to 0 so it has to use natural clip height */
+ fail_unless (check_frame_positioner_size (clip, 1400, 0));
+
+ GST_DEBUG ("Removing restriction on track width, should be 320 x 240");
+
+ caps = gst_caps_new_empty_simple ("video/x-raw");
+ ges_track_set_restriction_caps (trackv, caps);
+ gst_caps_unref (caps);
+
+ /**
+ * Our timeline : no restriction x no restriction
+ *
+ * 0--------------0
+ * | width : 320 |
+ * | height: 240 |
+ * 0--------------2
+ */
+
+ /* Clip width was set to 0 so it has to use natural clip width */
+ /* Clip height was set to 0 so it has to use natural clip height */
+ fail_unless (check_frame_positioner_size (clip, 0, 0));
+
+
+ /**
+ * Our timeline : 320 * 240
+ *
+ * 0--------------0
+ * | width : 320 |
+ * | height: 240 |
+ * 0--------------2
+ */
+
+ /* We set the restriction caps video size to the same as the video source
+ * size. */
+ caps = gst_caps_from_string ("video/x-raw,height=240,width=320");
+ ges_track_set_restriction_caps (trackv, caps);
+ gst_caps_unref (caps);
+ _set_track_element_width_height (GES_CONTAINER_CHILDREN (clip)->data, 320,
+ 240);
+
+ /* The video source has the same size as the track restriction caps but we
+ * are changing the aspect ratio, the video should thus not be rescaled. */
+ caps = gst_caps_from_string ("video/x-raw,height=1080,width=1920");
+ ges_track_set_restriction_caps (trackv, caps);
+ gst_caps_unref (caps);
+ fail_unless (check_frame_positioner_size (clip, 320, 240));
+
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-timeline-edition");
+ TCase *tc_chain = tcase_create ("timeline-edition");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_basic_timeline_edition);
+ tcase_add_test (tc_chain, test_snapping);
+ tcase_add_test (tc_chain, test_timeline_edition_mode);
+ tcase_add_test (tc_chain, test_simple_triming);
+ tcase_add_test (tc_chain, test_groups);
+ tcase_add_test (tc_chain, test_snapping_groups);
+ tcase_add_test (tc_chain, test_scaling);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+GST_START_TEST (test_title_source_basic)
+{
+ GESTitleClip *source;
+
+ ges_init ();
+
+ source = ges_title_clip_new ();
+ fail_unless (source != NULL);
+
+ gst_object_unref (source);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_title_source_properties)
+{
+ GESClip *clip;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (track != NULL);
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ clip = (GESClip *) ges_title_clip_new ();
+ fail_unless (clip != NULL);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 51,
+ "in-point", (guint64) 12, NULL);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 0);
+
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
+ GES_TIMELINE_ELEMENT (clip));
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 51);
+ assert_equals_uint64 (_INPOINT (trackelement), 0);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 0,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change more properties, see if they propagate */
+ g_object_set (clip, "start", (guint64) 420, "duration", (guint64) 510,
+ "in-point", (guint64) 120, NULL);
+ ges_timeline_commit (timeline);
+ assert_equals_uint64 (_START (clip), 420);
+ assert_equals_uint64 (_DURATION (clip), 510);
+ assert_equals_uint64 (_INPOINT (clip), 0);
+ assert_equals_uint64 (_START (trackelement), 420);
+ assert_equals_uint64 (_DURATION (trackelement), 510);
+ assert_equals_uint64 (_INPOINT (trackelement), 0);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 0, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
+
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (trackelement));
+ gst_object_unref (clip);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_title_source_in_layer)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *a, *v;
+ GESTrackElement *track_element;
+ GESTitleClip *source;
+ gchar *text;
+ gint halign, valign;
+ guint32 color;
+ gdouble xpos;
+ gdouble ypos;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ a = GES_TRACK (ges_audio_track_new ());
+ v = GES_TRACK (ges_video_track_new ());
+
+ ges_timeline_add_track (timeline, a);
+ ges_timeline_add_track (timeline, v);
+ ges_timeline_add_layer (timeline, layer);
+
+ source = ges_title_clip_new ();
+
+ g_object_set (source, "duration", (guint64) GST_SECOND, NULL);
+
+ ges_layer_add_clip (layer, (GESClip *) source);
+
+ /* specifically test the text property */
+ g_object_set (source, "text", (gchar *) "some text", NULL);
+ g_object_get (source, "text", &text, NULL);
+ assert_equals_string ("some text", text);
+ g_free (text);
+
+ track_element =
+ ges_clip_find_track_element (GES_CLIP (source), v, GES_TYPE_TITLE_SOURCE);
+
+ /* test the font-desc property */
+ g_object_set (source, "font-desc", (gchar *) "sans 72", NULL);
+ g_object_get (source, "font-desc", &text, NULL);
+ assert_equals_string ("sans 72", text);
+ g_free (text);
+
+ /* test halign and valign */
+ g_object_set (source, "halignment", (gint)
+ GES_TEXT_HALIGN_LEFT, "valignment", (gint) GES_TEXT_VALIGN_TOP, NULL);
+ g_object_get (source, "halignment", &halign, "valignment", &valign, NULL);
+ assert_equals_int (halign, GES_TEXT_HALIGN_LEFT);
+ assert_equals_int (valign, GES_TEXT_VALIGN_TOP);
+
+ assert_equals_int (ges_title_source_get_halignment
+ (GES_TITLE_SOURCE (track_element)), GES_TEXT_HALIGN_LEFT);
+ assert_equals_int (ges_title_source_get_valignment
+ (GES_TITLE_SOURCE (track_element)), GES_TEXT_VALIGN_TOP);
+
+ /* test color */
+ g_object_set (source, "color", (gint) 2147483647, NULL);
+ g_object_get (source, "color", &color, NULL);
+ assert_equals_int (color, 2147483647);
+
+ color = ges_title_source_get_text_color (GES_TITLE_SOURCE (track_element));
+ assert_equals_int (color, 2147483647);
+
+ /* test xpos */
+ g_object_set (source, "xpos", (gdouble) 0.25, NULL);
+ g_object_get (source, "xpos", &xpos, NULL);
+ assert_equals_float (xpos, 0.25);
+
+ xpos = ges_title_source_get_xpos (GES_TITLE_SOURCE (track_element));
+ assert_equals_float (xpos, 0.25);
+
+ /* test ypos */
+ g_object_set (source, "ypos", (gdouble) 0.66, NULL);
+ g_object_get (source, "ypos", &ypos, NULL);
+ assert_equals_float (ypos, 0.66);
+
+ xpos = ges_title_source_get_xpos (GES_TITLE_SOURCE (track_element));
+ assert_equals_float (ypos, 0.66);
+
+ GST_DEBUG ("removing the source");
+
+ ges_layer_remove_clip (layer, (GESClip *) source);
+
+ GST_DEBUG ("removing the layer");
+
+ gst_object_unref (track_element);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-titles");
+ TCase *tc_chain = tcase_create ("titles");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_title_source_basic);
+ tcase_add_test (tc_chain, test_title_source_properties);
+ tcase_add_test (tc_chain, test_title_source_in_layer);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+static gboolean
+compare_caps_from_string (GstCaps * caps, const gchar * desc)
+{
+ GstCaps *new_caps = gst_caps_from_string (desc);
+ gboolean ret = TRUE;
+
+ if (!gst_caps_is_strictly_equal (caps, new_caps))
+ ret = FALSE;
+
+ gst_caps_unref (new_caps);
+ return ret;
+}
+
+GST_START_TEST (test_update_restriction_caps)
+{
+ GESTrack *track;
+ GstCaps *original;
+ GstCaps *new;
+ GstCaps *current;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_audio_track_new ());
+
+ original = gst_caps_from_string ("audio/x-raw, format=S32LE");
+ ges_track_set_restriction_caps (track, original);
+
+ new = gst_caps_from_string ("audio/x-raw, format=S16LE, width=720");
+ ges_track_update_restriction_caps (track, new);
+ g_object_get (track, "restriction-caps", ¤t, NULL);
+
+ /* Assuming the format for to_string doesn't change */
+ fail_unless (compare_caps_from_string (current,
+ "audio/x-raw, format=(string)S16LE, width=(int)720"));
+
+ gst_caps_unref (new);
+ new = gst_caps_from_string ("audio/x-raw, width=360");
+ ges_track_update_restriction_caps (track, new);
+ gst_caps_unref (current);
+ g_object_get (track, "restriction-caps", ¤t, NULL);
+ fail_unless (compare_caps_from_string (current,
+ "audio/x-raw, format=(string)S16LE, width=(int)360"));
+
+ gst_caps_append_structure (new,
+ gst_structure_new_from_string ("audio/x-raw, format=S16LE"));
+ ges_track_update_restriction_caps (track, new);
+ gst_caps_unref (current);
+ g_object_get (track, "restriction-caps", ¤t, NULL);
+ fail_unless (compare_caps_from_string (current,
+ "audio/x-raw, format=(string)S16LE, width=(int)360; audio/x-raw, format=S16LE"));
+
+ gst_caps_unref (new);
+ new =
+ gst_caps_from_string
+ ("audio/x-raw, width=240; audio/x-raw, format=S32LE");
+ ges_track_update_restriction_caps (track, new);
+ gst_caps_unref (current);
+ g_object_get (track, "restriction-caps", ¤t, NULL);
+ fail_unless (compare_caps_from_string (current,
+ "audio/x-raw, format=(string)S16LE, width=(int)240; audio/x-raw, format=S32LE"));
+
+ gst_caps_unref (new);
+ gst_caps_unref (original);
+ gst_caps_unref (current);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-track");
+ TCase *tc_chain = tcase_create ("track");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_update_restriction_caps);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <brandon@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+/* This test uri will eventually have to be fixed */
+#define TEST_URI "blahblahblah"
+
+GST_START_TEST (test_transition_basic)
+{
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTransitionClip *tr1, *tr2;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ track = GES_TRACK (ges_video_track_new ());
+ layer = ges_layer_new ();
+ timeline = ges_timeline_new ();
+ fail_unless (track != NULL);
+ fail_unless (layer != NULL);
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ tr1 = ges_transition_clip_new (GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE);
+ fail_unless (tr1 != 0);
+ fail_unless (tr1->vtype == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE);
+
+ tr2 = ges_transition_clip_new_for_nick ((gchar *) "bar-wipe-lr");
+ fail_unless (tr2 != 0);
+ fail_unless (tr2->vtype == 1);
+
+ /* Make sure track element is created and vtype is set */
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (tr2)));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (tr2)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (tr2)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (ges_video_transition_get_transition_type
+ (GES_VIDEO_TRANSITION (trackelement)) == 1);
+
+ gst_object_unref (tr1);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_transition_properties)
+{
+ GESClip *clip;
+ GESTrack *track;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ clip = GES_CLIP (ges_transition_clip_new
+ (GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE));
+
+ track = GES_TRACK (ges_video_track_new ());
+ layer = ges_layer_new ();
+ timeline = ges_timeline_new ();
+ fail_unless (track != NULL);
+ fail_unless (layer != NULL);
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ /* Set some properties */
+ g_object_set (clip, "start", (guint64) 42, "duration", (guint64) 51,
+ "in-point", (guint64) 12, NULL);
+
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
+ ges_timeline_commit (timeline);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 51);
+ assert_equals_uint64 (_INPOINT (trackelement), 12);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO, TRUE);
+
+ /* Change more properties, see if they propagate */
+ g_object_set (clip, "start", (guint64) 420, "duration", (guint64) 510,
+ "in-point", (guint64) 120, NULL);
+ ges_timeline_commit (timeline);
+ assert_equals_uint64 (_START (clip), 420);
+ assert_equals_uint64 (_DURATION (clip), 510);
+ assert_equals_uint64 (_INPOINT (clip), 120);
+ assert_equals_uint64 (_START (trackelement), 420);
+ assert_equals_uint64 (_DURATION (trackelement), 510);
+ assert_equals_uint64 (_INPOINT (trackelement), 120);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + 0, TRUE);
+
+ /* test changing vtype */
+ GST_DEBUG ("Setting to crossfade");
+ g_object_set (clip, "vtype", GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE,
+ NULL);
+ assert_equals_int (GES_TRANSITION_CLIP (clip)->vtype,
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE);
+ assert_equals_int (ges_video_transition_get_transition_type
+ (GES_VIDEO_TRANSITION (trackelement)),
+ GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE);
+
+ /* Check that changing from crossfade to anything else fails (it should
+ * still be using crossfade */
+ GST_DEBUG ("Setting back to 1 (should fail)");
+ g_object_set (clip, "vtype", 1, NULL);
+
+ assert_equals_int (GES_TRANSITION_CLIP (clip)->vtype, 1);
+ assert_equals_int (ges_video_transition_get_transition_type
+ (GES_VIDEO_TRANSITION (trackelement)), 1);
+
+ GST_DEBUG ("Removing clip from layer");
+ gst_object_ref (clip); /* We do not want it to be destroyed */
+ ges_layer_remove_clip (layer, clip);
+
+ g_object_set (clip, "vtype", 1, NULL);
+ GST_DEBUG ("Read it to the layer");
+ fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
+ g_object_unref (clip);
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+
+ /* The new track element should have taken the previously set transition
+ * type (in this case 1) */
+ GST_DEBUG ("Setting to vtype:1");
+ assert_equals_int (ges_video_transition_get_transition_type
+ (GES_VIDEO_TRANSITION (trackelement)), 1);
+ assert_equals_int (GES_TRANSITION_CLIP (clip)->vtype, 1);
+
+ check_destroyed (G_OBJECT (timeline), G_OBJECT (track), clip, NULL);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-transition");
+ TCase *tc_chain = tcase_create ("transition");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_transition_basic);
+ tcase_add_test (tc_chain, test_transition_properties);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+/* This test uri will eventually have to be fixed */
+#define TEST_URI "http://nowhere/blahblahblah"
+
+
+static gchar *av_uri;
+static gchar *image_uri;
+GMainLoop *mainloop;
+
+typedef struct _AssetUri
+{
+ const gchar *uri;
+ GESAsset *asset;
+} AssetUri;
+
+static void
+asset_created_cb (GObject * source, GAsyncResult * res, gpointer udata)
+{
+ GList *tracks, *tmp;
+ GESAsset *asset;
+ GESLayer *layer;
+ GESUriClip *tlfs;
+
+ GError *error = NULL;
+
+ asset = ges_asset_request_finish (res, &error);
+ ASSERT_OBJECT_REFCOUNT (asset, "1 for us + for the cache + 1 taken "
+ "by g_task", 3);
+ fail_unless (error == NULL);
+ fail_if (asset == NULL);
+ fail_if (g_strcmp0 (ges_asset_get_id (asset), av_uri));
+
+ layer = GES_LAYER (g_async_result_get_user_data (res));
+ tlfs = GES_URI_CLIP (ges_layer_add_asset (layer,
+ asset, 0, 0, GST_CLOCK_TIME_NONE, GES_TRACK_TYPE_UNKNOWN));
+ fail_unless (GES_IS_URI_CLIP (tlfs));
+ fail_if (g_strcmp0 (ges_uri_clip_get_uri (tlfs), av_uri));
+ assert_equals_uint64 (_DURATION (tlfs), GST_SECOND);
+
+ fail_unless (ges_clip_get_supported_formats
+ (GES_CLIP (tlfs)) & GES_TRACK_TYPE_VIDEO);
+ fail_unless (ges_clip_get_supported_formats
+ (GES_CLIP (tlfs)) & GES_TRACK_TYPE_AUDIO);
+
+ tracks = ges_timeline_get_tracks (ges_layer_get_timeline (layer));
+ for (tmp = tracks; tmp; tmp = tmp->next) {
+ GList *trackelements = ges_track_get_elements (GES_TRACK (tmp->data));
+
+ assert_equals_int (g_list_length (trackelements), 1);
+ fail_unless (GES_IS_VIDEO_URI_SOURCE (trackelements->data)
+ || GES_IS_AUDIO_URI_SOURCE (trackelements->data));
+ g_list_free_full (trackelements, gst_object_unref);
+ }
+ g_list_free_full (tracks, gst_object_unref);
+
+ gst_object_unref (asset);
+ g_main_loop_quit (mainloop);
+}
+
+GST_START_TEST (test_filesource_basic)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ fail_unless (timeline != NULL);
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+
+ ges_asset_request_async (GES_TYPE_URI_CLIP,
+ av_uri, NULL, asset_created_cb, layer);
+
+ g_main_loop_run (mainloop);
+ g_main_loop_unref (mainloop);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static gboolean
+create_asset (AssetUri * asset_uri)
+{
+ asset_uri->asset =
+ GES_ASSET (ges_uri_clip_asset_request_sync (asset_uri->uri, NULL));
+ g_main_loop_quit (mainloop);
+
+ return FALSE;
+}
+
+GST_START_TEST (test_filesource_properties)
+{
+ GESClip *clip;
+ GESTrack *track;
+ AssetUri asset_uri;
+ GESTimeline *timeline;
+ GESUriClipAsset *asset;
+ GESLayer *layer;
+ GESTrackElement *trackelement;
+
+ ges_init ();
+
+ track = ges_track_new (GES_TRACK_TYPE_AUDIO, gst_caps_ref (GST_CAPS_ANY));
+ fail_unless (track != NULL);
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ timeline = ges_timeline_new ();
+ fail_unless (GES_IS_TIMELINE (timeline));
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, track));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ asset_uri.uri = av_uri;
+ /* Right away request the asset synchronously */
+ g_timeout_add (1, (GSourceFunc) create_asset, &asset_uri);
+ g_main_loop_run (mainloop);
+
+ asset = GES_URI_CLIP_ASSET (asset_uri.asset);
+ fail_unless (GES_IS_ASSET (asset));
+ clip = ges_layer_add_asset (layer, GES_ASSET (asset),
+ 42, 12, 51, GES_TRACK_TYPE_AUDIO);
+ ges_timeline_commit (timeline);
+ assert_is_type (clip, GES_TYPE_URI_CLIP);
+ assert_equals_uint64 (_START (clip), 42);
+ assert_equals_uint64 (_DURATION (clip), 51);
+ assert_equals_uint64 (_INPOINT (clip), 12);
+
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ trackelement = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (trackelement != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
+ GES_TIMELINE_ELEMENT (clip));
+ fail_unless (ges_track_element_get_track (trackelement) == track);
+
+ /* Check that trackelement has the same properties */
+ assert_equals_uint64 (_START (trackelement), 42);
+ assert_equals_uint64 (_DURATION (trackelement), 51);
+ assert_equals_uint64 (_INPOINT (trackelement), 12);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
+ 51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
+
+ /* Change more properties, see if they propagate */
+ g_object_set (clip, "start", (guint64) 420, "duration", (guint64) 510,
+ "in-point", (guint64) 120, NULL);
+ ges_timeline_commit (timeline);
+ assert_equals_uint64 (_START (clip), 420);
+ assert_equals_uint64 (_DURATION (clip), 510);
+ assert_equals_uint64 (_INPOINT (clip), 120);
+ assert_equals_uint64 (_START (trackelement), 420);
+ assert_equals_uint64 (_DURATION (trackelement), 510);
+ assert_equals_uint64 (_INPOINT (trackelement), 120);
+
+ /* And let's also check that it propagated correctly to GNonLin */
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
+
+ /* Test mute support */
+ g_object_set (clip, "mute", TRUE, NULL);
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, FALSE);
+ g_object_set (clip, "mute", FALSE, NULL);
+ ges_timeline_commit (timeline);
+ nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
+ 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
+
+ ges_container_remove (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (trackelement));
+
+ gst_object_unref (asset);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_filesource_images)
+{
+ GESClip *clip;
+ GESAsset *asset;
+ GESTrack *a, *v;
+ GESUriClip *uriclip;
+ AssetUri asset_uri;
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrackElement *track_element;
+
+ ges_init ();
+
+ a = GES_TRACK (ges_audio_track_new ());
+ v = GES_TRACK (ges_video_track_new ());
+
+ layer = ges_layer_new ();
+ fail_unless (layer != NULL);
+ timeline = ges_timeline_new ();
+ fail_unless (timeline != NULL);
+ fail_unless (ges_timeline_add_layer (timeline, layer));
+ fail_unless (ges_timeline_add_track (timeline, a));
+ fail_unless (ges_timeline_add_track (timeline, v));
+ ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ /* Right away request the asset synchronously */
+ asset_uri.uri = image_uri;
+ g_timeout_add (1, (GSourceFunc) create_asset, &asset_uri);
+ g_main_loop_run (mainloop);
+
+ asset = asset_uri.asset;
+ fail_unless (GES_IS_ASSET (asset));
+ fail_unless (ges_uri_clip_asset_is_image (GES_URI_CLIP_ASSET (asset)));
+ uriclip = GES_URI_CLIP (ges_asset_extract (asset, NULL));
+ fail_unless (GES_IS_URI_CLIP (uriclip));
+ fail_unless (ges_clip_get_supported_formats (GES_CLIP (uriclip)) ==
+ GES_TRACK_TYPE_VIDEO);
+ clip = GES_CLIP (uriclip);
+ fail_unless (ges_uri_clip_is_image (uriclip));
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip),
+ 1 * GST_SECOND);
+
+ /* the returned track element should be an image source */
+ /* the clip should not create any TrackElement in the audio track */
+ ges_layer_add_clip (layer, GES_CLIP (clip));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1);
+ track_element = GES_CONTAINER_CHILDREN (clip)->data;
+ fail_unless (track_element != NULL);
+ fail_unless (GES_TIMELINE_ELEMENT_PARENT (track_element) ==
+ GES_TIMELINE_ELEMENT (clip));
+ fail_unless (ges_track_element_get_track (track_element) == v);
+ fail_unless (GES_IS_IMAGE_SOURCE (track_element));
+
+ ASSERT_OBJECT_REFCOUNT (track_element, "1 in track, 1 in clip 2 in timeline",
+ 3);
+
+ gst_object_unref (asset);
+ gst_object_unref (timeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-filesource");
+ TCase *tc_chain = tcase_create ("filesource");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_filesource_basic);
+ tcase_add_test (tc_chain, test_filesource_images);
+ tcase_add_test (tc_chain, test_filesource_properties);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s;
+
+ gst_check_init (&argc, &argv);
+
+ s = ges_suite ();
+
+ av_uri = ges_test_get_audio_video_uri ();
+ image_uri = ges_test_get_image_uri ();
+
+ nf = gst_check_run_suite (s, "ges", __FILE__);
+
+ g_free (av_uri);
+ g_free (image_uri);
+
+ return nf;
+}
--- /dev/null
+<Nothing/>
--- /dev/null
+# tests and condition when to skip the test
+ges_tests = [
+ ['ges/asset'],
+ ['ges/backgroundsource'],
+ ['ges/basic'],
+ ['ges/layer'],
+ ['ges/effects'],
+ ['ges/uriclip'],
+ ['ges/clip'],
+ ['ges/timelineedition'],
+ ['ges/titles'],
+ ['ges/transition'],
+ ['ges/overlays'],
+ ['ges/mixers'],
+ ['ges/group'],
+ ['ges/project'],
+ ['ges/track'],
+ ['ges/tempochange'],
+ ['ges/negative'],
+ ['nle/simple'],
+ ['nle/complex'],
+ ['nle/nleoperation'],
+ ['nle/nlecomposition'],
+ ['nle/tempochange']
+]
+
+test_defines = [
+ '-UG_DISABLE_ASSERT',
+ '-UG_DISABLE_CAST_CHECKS',
+ '-DGES_TEST_FILES_PATH="' + meson.current_source_dir() + '/ges/"',
+ '-DGST_CHECK_TEST_ENVIRONMENT_BEACON="GST_STATE_IGNORE_ELEMENTS"',
+ '-DTESTFILE="' + meson.current_source_dir() + '/meson.build"',
+ '-DGST_USE_UNSTABLE_API',
+]
+
+pluginsdirs = []
+if gst_dep.type_name() == 'pkgconfig'
+ pbase = dependency('gstreamer-plugins-base-' + apiversion, required : false)
+ pbad = dependency('gstreamer-plugins-bad-' + apiversion, required : false)
+
+ pluginsdirs = [gst_dep.get_pkgconfig_variable('pluginsdir'),
+ pbase.get_pkgconfig_variable('pluginsdir'),
+ pbad.get_pkgconfig_variable('pluginsdir')]
+endif
+
+foreach t : ges_tests
+ fname = '@0@.c'.format(t.get(0))
+ test_name = t.get(0).underscorify()
+ if t.length() == 2
+ skip_test = t.get(1)
+ else
+ skip_test = false
+ endif
+
+ if not skip_test
+ env = environment()
+ env.set('GST_PLUGIN_SYSTEM_PATH_1_0', '')
+ env.set('GST_STATE_IGNORE_ELEMENTS', '')
+ env.set('CK_DEFAULT_TIMEOUT', '20')
+ env.set('GST_REGISTRY', '@0@/@1@.registry'.format(meson.current_build_dir(), test_name))
+ env.set('GST_PLUGIN_PATH_1_0', [meson.build_root()] + pluginsdirs)
+
+ exe = executable(test_name, fname,
+ 'ges/test-utils.c', 'nle/common.c',
+ c_args : ges_c_args + test_defines,
+ include_directories : [configinc],
+ dependencies : libges_deps + [gstcheck_dep, ges_dep],
+ )
+ test(test_name, exe, env: env, timeout : 3 * 60)
+ endif
+endforeach
+
+if build_gir
+ # Make sure to use the subproject gst-validate-launcher if avalaible.
+ if gstvalidate_dep.found() and gstvalidate_dep.type_name() == 'internal'
+ runtests = subproject('gst-devtools').get_variable('launcher')
+ else
+ runtests = find_program('gst-validate-launcher', required : false)
+ endif
+
+ if runtests.found()
+ test('pythontests', runtests, args: ['--pyunittest-dir', meson.current_source_dir(), 'pyunittest', '--dump-on-failure'],
+ env: env)
+ endif
+endif
--- /dev/null
+#include "common.h"
+
+void
+poll_the_bus (GstBus * bus)
+{
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_DEBUG ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+}
+
+static gboolean
+nle_object_commit (GstElement * nlesource, gboolean recurse)
+{
+ gboolean ret;
+
+ g_signal_emit_by_name (nlesource, "commit", recurse, &ret);
+
+ return ret;
+}
+
+GstElement *
+gst_element_factory_make_or_warn (const gchar * factoryname, const gchar * name)
+{
+ GstElement *element;
+
+ element = gst_element_factory_make (factoryname, name);
+ fail_unless (element != NULL, "Failed to make element %s", factoryname);
+ return element;
+}
+
+void
+composition_pad_added_cb (GstElement * composition, GstPad * pad,
+ CollectStructure * collect)
+{
+ fail_if (!(gst_element_link_pads_full (composition, GST_OBJECT_NAME (pad),
+ collect->sink, "sink", GST_PAD_LINK_CHECK_NOTHING)));
+}
+
+/* return TRUE to discard the Segment */
+static gboolean
+compare_segments (CollectStructure * collect, Segment * segment,
+ GstEvent * event)
+{
+ const GstSegment *received_segment;
+ guint64 running_stop, running_start, running_duration;
+
+ gst_event_parse_segment (event, &received_segment);
+
+ GST_DEBUG ("Got Segment rate:%f, format:%s, start:%" GST_TIME_FORMAT
+ ", stop:%" GST_TIME_FORMAT ", time:%" GST_TIME_FORMAT
+ ", base:%" GST_TIME_FORMAT ", offset:%" GST_TIME_FORMAT,
+ received_segment->rate, gst_format_get_name (received_segment->format),
+ GST_TIME_ARGS (received_segment->start),
+ GST_TIME_ARGS (received_segment->stop),
+ GST_TIME_ARGS (received_segment->time),
+ GST_TIME_ARGS (received_segment->base),
+ GST_TIME_ARGS (received_segment->offset));
+ GST_DEBUG ("[RUNNING] start:%" GST_TIME_FORMAT " [STREAM] start:%"
+ GST_TIME_FORMAT,
+ GST_TIME_ARGS (gst_segment_to_running_time (received_segment,
+ GST_FORMAT_TIME, received_segment->start)),
+ GST_TIME_ARGS (gst_segment_to_stream_time (received_segment,
+ GST_FORMAT_TIME, received_segment->start)));
+
+ GST_DEBUG ("Expecting rate:%f, format:%s, start:%" GST_TIME_FORMAT
+ ", stop:%" GST_TIME_FORMAT ", position:%" GST_TIME_FORMAT ", base:%"
+ GST_TIME_FORMAT, segment->rate, gst_format_get_name (segment->format),
+ GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop),
+ GST_TIME_ARGS (segment->position),
+ GST_TIME_ARGS (collect->expected_base));
+
+ running_start =
+ gst_segment_to_running_time (received_segment, GST_FORMAT_TIME,
+ received_segment->start);
+ running_stop =
+ gst_segment_to_running_time (received_segment, GST_FORMAT_TIME,
+ received_segment->stop);
+ running_duration = running_stop - running_start;
+ fail_if (received_segment->rate != segment->rate);
+ fail_if (received_segment->format != segment->format);
+ fail_unless_equals_int64 (received_segment->time, segment->position);
+ fail_unless_equals_int64 (received_segment->base, collect->expected_base);
+ fail_unless_equals_uint64 (received_segment->stop - received_segment->start,
+ segment->stop - segment->start);
+
+ collect->expected_base += running_duration;
+
+ GST_DEBUG ("Segment was valid, discarding expected Segment");
+
+ return TRUE;
+}
+
+static GstPadProbeReturn
+sinkpad_event_probe (GstPad * sinkpad, GstEvent * event,
+ CollectStructure * collect)
+{
+ Segment *segment;
+
+ GST_DEBUG_OBJECT (sinkpad, "event:%p (%s seqnum:%d) , collect:%p", event,
+ GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event), collect);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
+ fail_if (collect->expected_segments == NULL,
+ "Received unexpected segment on pad: %s:%s",
+ GST_DEBUG_PAD_NAME (sinkpad));
+
+ if (!collect->gotsegment)
+ collect->seen_segments =
+ g_list_append (NULL, GINT_TO_POINTER (GST_EVENT_SEQNUM (event)));
+ else {
+ fail_if (g_list_find (collect->seen_segments,
+ GINT_TO_POINTER (GST_EVENT_SEQNUM (event))),
+ "Got a segment event we already saw before !");
+ collect->seen_segments =
+ g_list_append (collect->seen_segments,
+ GINT_TO_POINTER (GST_EVENT_SEQNUM (event)));
+ }
+
+ segment = (Segment *) collect->expected_segments->data;
+
+ if (compare_segments (collect, segment, event) &&
+ collect->keep_expected_segments == FALSE) {
+ collect->expected_segments =
+ g_list_remove (collect->expected_segments, segment);
+ g_free (segment);
+ }
+
+ collect->gotsegment = TRUE;
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
+static GstPadProbeReturn
+sinkpad_buffer_probe (GstPad * sinkpad, GstBuffer * buffer,
+ CollectStructure * collect)
+{
+ GST_LOG_OBJECT (sinkpad, "buffer:%p (%" GST_TIME_FORMAT ") , collect:%p",
+ buffer, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), collect);
+ fail_if (!collect->gotsegment,
+ "Received a buffer without a preceding segment");
+ return GST_PAD_PROBE_OK;
+}
+
+GstPadProbeReturn
+sinkpad_probe (GstPad * sinkpad, GstPadProbeInfo * info,
+ CollectStructure * collect)
+{
+ if (info->type & GST_PAD_PROBE_TYPE_BUFFER)
+ return sinkpad_buffer_probe (sinkpad, (GstBuffer *) info->data, collect);
+ if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM)
+ return sinkpad_event_probe (sinkpad, (GstEvent *) info->data, collect);
+ return GST_PAD_PROBE_OK;
+}
+
+static GstElement *
+new_nle_src (const gchar * name, guint64 start, gint64 duration, gint priority)
+{
+ GstElement *nlesource = NULL;
+
+ nlesource = gst_element_factory_make_or_warn ("nlesource", name);
+ fail_if (nlesource == NULL);
+
+ g_object_set (G_OBJECT (nlesource),
+ "start", start,
+ "duration", duration, "inpoint", start, "priority", priority, NULL);
+ nle_object_commit (nlesource, FALSE);
+
+ return nlesource;
+}
+
+GstElement *
+videotest_nle_src (const gchar * name, guint64 start, gint64 duration,
+ gint pattern, guint priority)
+{
+ GstElement *nlesource = NULL;
+ GstElement *videotestsrc = NULL;
+ GstCaps *caps =
+ gst_caps_from_string
+ ("video/x-raw,format=(string)I420,framerate=(fraction)3/2");
+
+ fail_if (caps == NULL);
+
+ videotestsrc = gst_element_factory_make_or_warn ("videotestsrc", NULL);
+ g_object_set (G_OBJECT (videotestsrc), "pattern", pattern, NULL);
+
+ nlesource = new_nle_src (name, start, duration, priority);
+ g_object_set (G_OBJECT (nlesource), "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ gst_bin_add (GST_BIN (nlesource), videotestsrc);
+
+ return nlesource;
+}
+
+GstElement *
+videotest_nle_src_full (const gchar * name, guint64 start, gint64 duration,
+ guint64 inpoint, gint pattern, guint priority)
+{
+ GstElement *nles;
+
+ nles = videotest_nle_src (name, start, duration, pattern, priority);
+ if (nles) {
+ g_object_set (G_OBJECT (nles), "inpoint", inpoint, NULL);
+ }
+
+
+ return nles;
+}
+
+GstElement *
+videotest_in_bin_nle_src (const gchar * name, guint64 start, gint64 duration,
+ gint pattern, guint priority)
+{
+ GstElement *nlesource = NULL;
+ GstElement *videotestsrc = NULL;
+ GstElement *bin = NULL;
+ GstElement *alpha = NULL;
+ GstPad *srcpad = NULL;
+
+ alpha = gst_element_factory_make ("alpha", NULL);
+ if (alpha == NULL)
+ return NULL;
+
+ videotestsrc = gst_element_factory_make_or_warn ("videotestsrc", NULL);
+ g_object_set (G_OBJECT (videotestsrc), "pattern", pattern, NULL);
+ bin = gst_bin_new (NULL);
+
+ nlesource = new_nle_src (name, start, duration, priority);
+
+ gst_bin_add (GST_BIN (bin), videotestsrc);
+ gst_bin_add (GST_BIN (bin), alpha);
+
+ gst_element_link_pads_full (videotestsrc, "src", alpha, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+
+ gst_bin_add (GST_BIN (nlesource), bin);
+
+ srcpad = gst_element_get_static_pad (alpha, "src");
+
+ gst_element_add_pad (bin, gst_ghost_pad_new ("src", srcpad));
+
+ gst_object_unref (srcpad);
+
+ return nlesource;
+}
+
+GstElement *
+audiotest_bin_src (const gchar * name, guint64 start,
+ gint64 duration, guint priority, gboolean intaudio)
+{
+ GstElement *source = NULL;
+ GstElement *identity = NULL;
+ GstElement *audiotestsrc = NULL;
+ GstElement *audioconvert = NULL;
+ GstElement *bin = NULL;
+ GstCaps *caps;
+ GstPad *srcpad = NULL;
+
+ audiotestsrc = gst_element_factory_make_or_warn ("audiotestsrc", NULL);
+ identity = gst_element_factory_make_or_warn ("identity", NULL);
+ bin = gst_bin_new (NULL);
+ source = new_nle_src (name, start, duration, priority);
+ audioconvert = gst_element_factory_make_or_warn ("audioconvert", NULL);
+
+ if (intaudio)
+ caps = gst_caps_from_string ("audio/x-raw,format=(string)S16LE");
+ else
+ caps = gst_caps_from_string ("audio/x-raw,format=(string)F32LE");
+
+ gst_bin_add_many (GST_BIN (bin), audiotestsrc, audioconvert, identity, NULL);
+ gst_element_link_pads_full (audiotestsrc, "src", audioconvert, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+ fail_if ((gst_element_link_filtered (audioconvert, identity, caps)) != TRUE);
+
+ gst_caps_unref (caps);
+
+ gst_bin_add (GST_BIN (source), bin);
+
+ srcpad = gst_element_get_static_pad (identity, "src");
+
+ gst_element_add_pad (bin, gst_ghost_pad_new ("src", srcpad));
+
+ gst_object_unref (srcpad);
+
+ return source;
+}
+
+GstElement *
+new_operation (const gchar * name, const gchar * factory, guint64 start,
+ gint64 duration, guint priority)
+{
+ GstElement *nleoperation = NULL;
+ GstElement *operation = NULL;
+
+ operation = gst_element_factory_make_or_warn (factory, NULL);
+ nleoperation = gst_element_factory_make_or_warn ("nleoperation", name);
+
+ g_object_set (G_OBJECT (nleoperation),
+ "start", start, "duration", duration, "priority", priority, NULL);
+
+ gst_bin_add (GST_BIN (nleoperation), operation);
+
+ return nleoperation;
+}
+
+
+Segment *
+segment_new (gdouble rate, GstFormat format, gint64 start, gint64 stop,
+ gint64 position)
+{
+ Segment *segment;
+
+ segment = g_new0 (Segment, 1);
+
+ segment->rate = rate;
+ segment->format = format;
+ segment->start = start;
+ segment->stop = stop;
+ segment->position = position;
+
+ return segment;
+}
+
+GList *
+copy_segment_list (GList * list)
+{
+ GList *res = NULL;
+
+ while (list) {
+ Segment *pdata = (Segment *) list->data;
+
+ res =
+ g_list_append (res, segment_new (pdata->rate, pdata->format,
+ pdata->start, pdata->stop, pdata->position));
+
+ list = list->next;
+ }
+
+ return res;
+}
+
+static GMutex lock;
+static GCond cond;
+static void
+commited_cb (GstElement * comp, gboolean changed)
+{
+ g_mutex_lock (&lock);
+ g_cond_signal (&cond);
+ g_mutex_unlock (&lock);
+}
+
+void
+commit_and_wait (GstElement * comp, gboolean * ret)
+{
+ gulong handler_id =
+ g_signal_connect (comp, "commited", (GCallback) commited_cb, NULL);
+ g_mutex_lock (&lock);
+ *ret = nle_object_commit (comp, TRUE);
+ g_cond_wait (&cond, &lock);
+ g_mutex_unlock (&lock);
+ g_signal_handler_disconnect (comp, handler_id);
+}
+
+gboolean
+nle_composition_remove (GstBin * comp, GstElement * object)
+{
+ gboolean ret;
+
+ ret = gst_bin_remove (comp, object);
+ if (!ret)
+ return ret;
+
+ commit_and_wait ((GstElement *) comp, &ret);
+
+ return ret;
+}
+
+gboolean
+nle_composition_add (GstBin * comp, GstElement * object)
+{
+ return gst_bin_add (comp, object);
+}
+
+void
+collect_free (CollectStructure * collect)
+{
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+ if (collect->expected_segments)
+ g_list_free_full (collect->expected_segments, (GDestroyNotify) g_free);
+
+ g_free (collect);
+}
--- /dev/null
+
+#include <gst/check/gstcheck.h>
+#include <ges/ges.h>
+
+#define fail_error_message(msg) \
+ G_STMT_START { \
+ GError *error; \
+ gst_message_parse_error(msg, &error, NULL); \
+ fail_unless(FALSE, "Error Message from %s : %s", \
+ GST_OBJECT_NAME (GST_MESSAGE_SRC(msg)), error->message); \
+ g_error_free (error); \
+ } G_STMT_END;
+
+#define check_start_stop_duration(object, startval, stopval, durval) \
+ G_STMT_START { guint64 start, stop; \
+ gint64 duration; \
+ GST_DEBUG_OBJECT (object, "Checking for valid start/stop/duration values"); \
+ g_object_get (object, "start", &start, "stop", &stop, \
+ "duration", &duration, NULL); \
+ fail_unless_equals_uint64(start, startval); \
+ fail_unless_equals_uint64(stop, stopval); \
+ fail_unless_equals_int64(duration, durval); \
+ GST_DEBUG_OBJECT (object, "start/stop/duration values valid"); \
+ } G_STMT_END;
+
+#define check_state_simple(object, expected_state) \
+ G_STMT_START { \
+ GstStateChangeReturn ret; \
+ GstState state, pending; \
+ ret = gst_element_get_state(GST_ELEMENT_CAST(object), &state, &pending, 5 * GST_SECOND); \
+ fail_if (ret == GST_STATE_CHANGE_FAILURE); \
+ fail_unless (state == expected_state, "Element state (%s) is not the expected one (%s)", \
+ gst_element_state_get_name(state), gst_element_state_get_name(expected_state)); \
+ } G_STMT_END;
+
+typedef struct _Segment {
+ gdouble rate;
+ GstFormat format;
+ guint64 start, stop, position;
+} Segment;
+
+typedef struct _CollectStructure {
+ GstElement *comp;
+ GstElement *sink;
+ guint64 last_time;
+ gboolean gotsegment;
+ GList *seen_segments;
+ GList *expected_segments;
+ guint64 expected_base;
+
+ gboolean keep_expected_segments;
+} CollectStructure;
+
+void poll_the_bus(GstBus *bus);
+void composition_pad_added_cb (GstElement *composition, GstPad *pad, CollectStructure * collect);
+GstPadProbeReturn sinkpad_probe (GstPad *sinkpad, GstPadProbeInfo * info, CollectStructure * collect);
+GstElement *videotest_nle_src (const gchar * name, guint64 start, gint64 duration,
+ gint pattern, guint priority);
+GstElement * videotest_nle_src_full (const gchar * name, guint64 start, gint64 duration,
+ guint64 inpoint,
+ gint pattern, guint priority);
+GstElement *
+videotest_in_bin_nle_src (const gchar * name, guint64 start, gint64 duration, gint pattern, guint priority);
+GstElement *
+audiotest_bin_src (const gchar * name, guint64 start,
+ gint64 duration, guint priority, gboolean intaudio);
+GstElement *
+new_operation (const gchar * name, const gchar * factory, guint64 start, gint64 duration, guint priority);
+GList *
+copy_segment_list (GList *list);
+GstElement *
+gst_element_factory_make_or_warn (const gchar * factoryname, const gchar * name);
+Segment *
+segment_new (gdouble rate, GstFormat format, gint64 start, gint64 stop, gint64 position);
+
+void commit_and_wait (GstElement *comp, gboolean *ret);
+gboolean nle_composition_remove (GstBin * comp, GstElement * object);
+gboolean nle_composition_add (GstBin * comp, GstElement * object);
+
+void collect_free (CollectStructure *collect);
--- /dev/null
+#include "common.h"
+
+static void
+fill_pipeline_and_check (GstElement * comp, GList * segments,
+ gint expected_error_domain)
+{
+ GstElement *pipeline, *sink;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+ GList *listcopy = copy_segment_list (segments);
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = segments;
+
+ gst_element_link (comp, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ {
+ GError *error = NULL;
+
+ gst_message_parse_error (message, &error, NULL);
+ if (comp == GST_ELEMENT (GST_MESSAGE_SRC (message)) &&
+ expected_error_domain == error->domain) {
+ GST_DEBUG ("Expected Error Message from %s : %s",
+ GST_OBJECT_NAME (GST_MESSAGE_SRC (message)), error->message);
+ carry_on = FALSE;
+ } else {
+ fail_unless (FALSE, "Error Message from %s : %s",
+ GST_OBJECT_NAME (GST_MESSAGE_SRC (message)), error->message);
+ }
+ g_clear_error (&error);
+ }
+ break;
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to READY");
+
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_DEBUG ("Resetted pipeline to READY");
+
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+
+ collect->seen_segments = NULL;
+ collect->expected_segments = listcopy;
+ collect->gotsegment = FALSE;
+ collect->expected_base = 0;
+
+ if (expected_error_domain)
+ goto done;
+
+ GST_DEBUG ("Setting pipeline to PLAYING again");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ carry_on = TRUE;
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ } else {
+ GST_DEBUG ("bus_poll responded, but there wasn't any message...");
+ }
+ }
+
+ fail_if (collect->expected_segments != NULL);
+
+done:
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+}
+
+GST_START_TEST (test_one_space_another)
+{
+ GstElement *comp, *source1, *source2;
+ gboolean ret = FALSE;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [-source1--] [-source2--] | 1
+ * */
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_nle_src ("source1", 0, 1 * GST_SECOND, 2, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 2s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 = videotest_nle_src ("source2", 2 * GST_SECOND, 1 * GST_SECOND, 3, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 2 * GST_SECOND, 3 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ GST_ERROR ("doing one commit");
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ commit_and_wait (comp, &ret);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Remove first source */
+
+ gst_object_ref (source1);
+ nle_composition_remove (GST_BIN (comp), source1);
+ check_start_stop_duration (comp, 2 * GST_SECOND, 3 * GST_SECOND,
+ 1 * GST_SECOND);
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Re-add first source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+ gst_object_unref (source1);
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+
+ fill_pipeline_and_check (comp, segments, GST_STREAM_ERROR);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_one_default_another)
+{
+ gboolean ret = FALSE;
+ GstElement *comp, *source1, *source2, *source3, *defaultsrc;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [-source1--] [-source2--][-source3-] | 1
+ * [--------------------------defaultsource------------------] | MAXUINT32
+ * */
+
+
+ /*
+ defaultsrc source
+ Start : 0s
+ Duration : 5s
+ Priority : 2
+ */
+
+ defaultsrc =
+ videotest_nle_src ("defaultsrc", 0, 5 * GST_SECOND, 2, G_MAXUINT32);
+ g_object_set (defaultsrc, "expandable", TRUE, NULL);
+ fail_if (defaultsrc == NULL);
+ check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
+
+ /*
+ Source 1
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_nle_src ("source1", 1 * GST_SECOND, 1 * GST_SECOND, 3, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 3s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 = videotest_nle_src ("source2", 3 * GST_SECOND, 1 * GST_SECOND, 2, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 3 * GST_SECOND, 4 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /*
+ Source 3
+ Start : 4s
+ Duration : 1s
+ Priority : 1
+ */
+ source3 = videotest_nle_src ("source3", 4 * GST_SECOND, 1 * GST_SECOND, 2, 1);
+ fail_if (source3 == NULL);
+ check_start_stop_duration (source3, 4 * GST_SECOND, 5 * GST_SECOND,
+ 1 * GST_SECOND);
+
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* defaultsrc source */
+ nle_composition_add (GST_BIN (comp), defaultsrc);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (defaultsrc, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (defaultsrc, "defaultsrc", 1);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ /* Third source */
+ nle_composition_add (GST_BIN (comp), source3);
+ commit_and_wait (comp, &ret);
+ fail_unless (ret);
+ check_start_stop_duration (comp, 0, 5 * GST_SECOND, 5 * GST_SECOND);
+ check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source3, "source3", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 3 * GST_SECOND, 4 * GST_SECOND, 3 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 4 * GST_SECOND, 5 * GST_SECOND, 4 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments, GST_STREAM_ERROR);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_one_expandable_another)
+{
+ GstElement *comp, *source1, *source2, *source3, *defaultsrc;
+ GList *segments = NULL;
+ gboolean ret = FALSE;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [ source1 ] [ source2 ][ source3 ] | 1
+ * [--------------------- defaultsrc ------------------------] | 1000 EXPANDABLE
+ * */
+
+ /*
+ defaultsrc source
+ Start : 0s
+ Duration : 5s
+ Priority : 1000
+ */
+
+ defaultsrc = videotest_nle_src ("defaultsrc", 0, 5 * GST_SECOND, 2, 1000);
+ g_object_set (defaultsrc, "expandable", TRUE, NULL);
+ fail_if (defaultsrc == NULL);
+ check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
+
+ /*
+ Source 1
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_nle_src ("source1", 1 * GST_SECOND, 1 * GST_SECOND, 3, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 3s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 = videotest_nle_src ("source2", 3 * GST_SECOND, 1 * GST_SECOND, 2, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 3 * GST_SECOND, 4 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /*
+ Source 3
+ Start : 4s
+ Duration : 1s
+ Priority : 1
+ */
+ source3 = videotest_nle_src ("source3", 4 * GST_SECOND, 1 * GST_SECOND, 2, 1);
+ fail_if (source3 == NULL);
+ check_start_stop_duration (source3, 4 * GST_SECOND, 5 * GST_SECOND,
+ 1 * GST_SECOND);
+
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* defaultsrc source */
+
+ nle_composition_add (GST_BIN (comp), defaultsrc);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (defaultsrc, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (defaultsrc, "defaultsrc", 1);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+ check_start_stop_duration (defaultsrc, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+
+ /* Third source */
+
+ nle_composition_add (GST_BIN (comp), source3);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 5 * GST_SECOND, 5 * GST_SECOND);
+ check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source3, "source3", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 3 * GST_SECOND, 4 * GST_SECOND, 3 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 4 * GST_SECOND, 5 * GST_SECOND, 4 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments, 0);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+
+GST_START_TEST (test_renegotiation)
+{
+ gboolean ret;
+ GstElement *pipeline;
+ GstElement *comp, *sink, *source1, *source2, *source3;
+ GstElement *audioconvert;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+ GstCaps *caps;
+
+ ges_init ();
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 =
+ audiotest_bin_src ("source1", 0 * GST_SECOND, 1 * GST_SECOND, 1, FALSE);
+ check_start_stop_duration (source1, 0 * GST_SECOND, 1 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 =
+ audiotest_bin_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 1, TRUE);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /*
+ Source 3
+ Start : 2s
+ Duration : 1s
+ Priority : 1
+ */
+ source3 =
+ audiotest_bin_src ("source3", 2 * GST_SECOND, 1 * GST_SECOND, 1, FALSE);
+ check_start_stop_duration (source3, 2 * GST_SECOND, 3 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+
+ /* Third source */
+
+ nle_composition_add (GST_BIN (comp), source3);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source3, "source3", 1);
+
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ audioconvert = gst_element_factory_make_or_warn ("audioconvert", "aconv");
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, audioconvert, sink, NULL);
+ caps = gst_caps_from_string ("audio/x-raw,format=(string)S16LE");
+ gst_element_link_filtered (audioconvert, sink, caps);
+ gst_caps_unref (caps);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = audioconvert;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
+
+ gst_element_link (comp, audioconvert);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_DEBUG ("Resetted pipeline to READY");
+
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+ collect->seen_segments = NULL;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
+ collect->gotsegment = FALSE;
+ collect->expected_base = 0;
+
+
+ GST_DEBUG ("Setting pipeline to PLAYING again");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ carry_on = TRUE;
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ } else {
+ GST_DEBUG ("bus_poll responded, but there wasn't any message...");
+ }
+ }
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_one_bin_space_another)
+{
+ GstElement *comp, *source1, *source2;
+ gboolean ret = FALSE;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_in_bin_nle_src ("source1", 0, 1 * GST_SECOND, 3, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 2s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 =
+ videotest_in_bin_nle_src ("source2", 2 * GST_SECOND, 1 * GST_SECOND, 2,
+ 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 2 * GST_SECOND, 3 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+
+ /* Remove second source */
+
+ gst_object_ref (source1);
+ nle_composition_remove (GST_BIN (comp), source1);
+ check_start_stop_duration (comp, 2 * GST_SECOND, 3 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Re-add second source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+ gst_object_unref (source1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+
+ fill_pipeline_and_check (comp, segments, GST_STREAM_ERROR);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_one_above_another)
+{
+ GstElement *comp, *source1, *source2;
+ gboolean ret = FALSE;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 2s
+ Priority : 2
+ */
+ source1 = videotest_nle_src ("source1", 0, 2 * GST_SECOND, 3, 2);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 2s
+ Duration : 2s
+ Priority : 1
+ */
+ source2 = videotest_nle_src ("source2", 1 * GST_SECOND, 2 * GST_SECOND, 2, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+
+ /* Remove second source */
+
+ gst_object_ref (source1);
+ nle_composition_remove (GST_BIN (comp), source1);
+ check_start_stop_duration (comp, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ /* Re-add second source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+ gst_object_unref (source1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 3 * GST_SECOND, 1 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments, 0);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("gnonlin-complex");
+ TCase *tc_chain = tcase_create ("complex");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_one_space_another);
+ tcase_add_test (tc_chain, test_one_default_another);
+ tcase_add_test (tc_chain, test_one_expandable_another);
+ tcase_add_test (tc_chain, test_renegotiation);
+ tcase_add_test (tc_chain, test_one_bin_space_another);
+ tcase_add_test (tc_chain, test_one_above_another);
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+/* Gnonlin
+ * Copyright (C) <2009> Alessandro Decina <alessandro.decina@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "common.h"
+
+typedef struct
+{
+ GstElement *composition;
+ GstElement *source3;
+} TestClosure;
+
+static int seek_events;
+
+static GstPadProbeReturn
+on_source1_pad_event_cb (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEEK)
+ ++seek_events;
+
+ return GST_PAD_PROBE_OK;
+}
+
+GST_START_TEST (test_change_object_start_stop_in_current_stack)
+{
+ GstPad *srcpad;
+ GstElement *pipeline;
+ GstElement *comp, *source1, *def, *sink;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on, ret = FALSE;
+
+ ges_init ();
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ gst_element_link (comp, sink);
+
+ /*
+ source1
+ Start : 0s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source1 = videotest_nle_src ("source1", 0, 2 * GST_SECOND, 2, 2);
+ srcpad = gst_element_get_static_pad (source1, "src");
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
+ (GstPadProbeCallback) on_source1_pad_event_cb, NULL, NULL);
+ gst_object_unref (srcpad);
+
+ /*
+ def (default source)
+ Priority = G_MAXUINT32
+ */
+ def =
+ videotest_nle_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, 2,
+ G_MAXUINT32);
+ g_object_set (def, "expandable", TRUE, NULL);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (def, "default", 1);
+
+ /* Add source 1 */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ nle_composition_add (GST_BIN (comp), def);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PAUSED");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ carry_on = TRUE;
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ASYNC_DONE:
+ {
+ carry_on = FALSE;
+ GST_DEBUG ("Pipeline reached PAUSED, stopping polling");
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ {
+ GST_WARNING ("Saw EOS");
+
+ fail_if (TRUE);
+ }
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+
+ /* pipeline is paused at this point */
+
+ /* move source1 out of the active segment */
+ g_object_set (source1, "start", (guint64) 4 * GST_SECOND, NULL);
+ commit_and_wait (comp, &ret);
+
+ /* remove source1 from the composition, which will become empty and remove the
+ * ghostpad */
+
+
+ /* keep an extra ref to source1 as we remove it from the bin */
+ gst_object_ref (source1);
+ fail_unless (nle_composition_remove (GST_BIN (comp), source1));
+ g_object_set (source1, "start", (guint64) 0 * GST_SECOND, NULL);
+ /* add the source again and check that the ghostpad is added again */
+ nle_composition_add (GST_BIN (comp), source1);
+ gst_object_unref (source1);
+ commit_and_wait (comp, &ret);
+
+
+ g_object_set (source1, "duration", (guint64) 1 * GST_SECOND, NULL);
+ commit_and_wait (comp, &ret);
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+ gst_element_set_state (source1, GST_STATE_NULL);
+
+ GST_DEBUG ("Resetted pipeline to NULL");
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_check_objects_destroyed_on_unref (pipeline, comp, def, NULL);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_remove_invalid_object)
+{
+ GstBin *composition;
+ GstElement *source1, *source2;
+
+ ges_init ();
+
+ composition = GST_BIN (gst_element_factory_make ("nlecomposition",
+ "composition"));
+ gst_element_set_state (GST_ELEMENT (composition), GST_STATE_READY);
+
+ source1 = gst_element_factory_make ("nlesource", "source1");
+ source2 = gst_element_factory_make ("nlesource", "source2");
+
+ nle_composition_add (composition, source1);
+ nle_composition_remove (composition, source2);
+ fail_unless (nle_composition_remove (composition, source1));
+
+ gst_element_set_state (GST_ELEMENT (composition), GST_STATE_NULL);
+ gst_object_unref (composition);
+ gst_object_unref (source2);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_remove_last_object)
+{
+ GstBin *composition;
+ GstElement *source1, *audiotestsrc, *source2, *audiotestsrc2, *fakesink,
+ *pipeline;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean ret;
+ gint64 position = 0;
+ GstClockTime duration;
+
+ ges_init ();
+
+ pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+ composition = GST_BIN (gst_element_factory_make ("nlecomposition",
+ "composition"));
+
+ gst_element_set_state (GST_ELEMENT (composition), GST_STATE_READY);
+
+ fakesink = gst_element_factory_make ("fakesink", NULL);
+ gst_bin_add_many (GST_BIN (pipeline), GST_ELEMENT (composition), fakesink,
+ NULL);
+ gst_element_link (GST_ELEMENT (composition), fakesink);
+
+ source1 = gst_element_factory_make ("nlesource", "source1");
+ audiotestsrc = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
+ gst_bin_add (GST_BIN (source1), audiotestsrc);
+ g_object_set (source1, "start", (guint64) 0 * GST_SECOND,
+ "duration", 10 * GST_SECOND, "inpoint", (guint64) 0, "priority", 1, NULL);
+
+ nle_composition_add (composition, source1);
+
+ source2 = gst_element_factory_make ("nlesource", "source1");
+ audiotestsrc2 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
+ gst_bin_add (GST_BIN (source2), audiotestsrc2);
+ g_object_set (source2, "start", (guint64) 10 * GST_SECOND,
+ "duration", 10 * GST_SECOND, "inpoint", (guint64) 0, "priority", 1, NULL);
+
+ nle_composition_add (composition, source2);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED)
+ == GST_STATE_CHANGE_FAILURE);
+ message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+ commit_and_wait (GST_ELEMENT (composition), &ret);
+
+ message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+ gst_element_seek_simple (pipeline,
+ GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 15 * GST_SECOND);
+
+ message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+ ret =
+ gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+ &position);
+ fail_unless_equals_uint64 (position, 15 * GST_SECOND);
+
+ gst_element_seek_simple (pipeline,
+ GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 18 * GST_SECOND);
+
+ message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+ ret =
+ gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+ &position);
+ fail_unless_equals_uint64 (position, 18 * GST_SECOND);
+
+ nle_composition_remove (composition, source2);
+
+ commit_and_wait (GST_ELEMENT (composition), &ret);
+ g_object_get (composition, "duration", &duration, NULL);
+ fail_unless_equals_uint64 (duration, 10 * GST_SECOND);
+
+ ret =
+ gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+ &position);
+ fail_unless_equals_uint64 (position, 10 * GST_SECOND - 1);
+
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_object_unref (pipeline);
+ gst_object_unref (bus);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_dispose_on_commit)
+{
+ GstElement *composition;
+ GstElement *nlesource;
+ GstElement *audiotestsrc;
+ GstElement *pipeline, *fakesink;
+ gboolean ret;
+
+ ges_init ();
+
+ composition = gst_element_factory_make ("nlecomposition", "composition");
+ pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
+ fakesink = gst_element_factory_make ("fakesink", NULL);
+
+ nlesource = gst_element_factory_make ("nlesource", "nlesource1");
+ audiotestsrc = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
+ gst_bin_add (GST_BIN (nlesource), audiotestsrc);
+ g_object_set (nlesource, "start", (guint64) 0 * GST_SECOND,
+ "duration", 10 * GST_SECOND, "inpoint", (guint64) 0, "priority", 1, NULL);
+ fail_unless (nle_composition_add (GST_BIN (composition), nlesource));
+
+ gst_bin_add_many (GST_BIN (pipeline), composition, fakesink, NULL);
+ fail_unless (gst_element_link (composition, fakesink) == TRUE);
+
+
+ ASSERT_OBJECT_REFCOUNT (composition, "composition", 1);
+ g_signal_emit_by_name (composition, "commit", TRUE, &ret);
+
+ gst_object_unref (pipeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_simple_audiomixer)
+{
+ GstBus *bus;
+ GstMessage *message;
+ GstElement *pipeline;
+ GstElement *nle_audiomixer;
+ GstElement *composition;
+ GstElement *audiomixer, *fakesink;
+ GstElement *nlesource1, *nlesource2;
+ GstElement *audiotestsrc1, *audiotestsrc2;
+
+ gboolean carry_on = TRUE, ret;
+ GstClockTime total_time = 10 * GST_SECOND;
+
+ ges_init ();
+
+ pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+ composition = gst_element_factory_make ("nlecomposition", "composition");
+ gst_element_set_state (composition, GST_STATE_READY);
+ fakesink = gst_element_factory_make ("fakesink", NULL);
+
+ /* nle_audiomixer */
+ nle_audiomixer = gst_element_factory_make ("nleoperation", "nle_audiomixer");
+ audiomixer = gst_element_factory_make ("audiomixer", "audiomixer");
+ fail_unless (audiomixer != NULL);
+ gst_bin_add (GST_BIN (nle_audiomixer), audiomixer);
+ g_object_set (nle_audiomixer, "start", (guint64) 0 * GST_SECOND,
+ "duration", total_time, "inpoint", (guint64) 0 * GST_SECOND,
+ "priority", 0, NULL);
+ nle_composition_add (GST_BIN (composition), nle_audiomixer);
+
+ /* source 1 */
+ nlesource1 = gst_element_factory_make ("nlesource", "nlesource1");
+ audiotestsrc1 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
+ gst_bin_add (GST_BIN (nlesource1), audiotestsrc1);
+ g_object_set (nlesource1, "start", (guint64) 0 * GST_SECOND,
+ "duration", total_time / 2, "inpoint", (guint64) 0, "priority", 1, NULL);
+ fail_unless (nle_composition_add (GST_BIN (composition), nlesource1));
+
+ /* nlesource2 */
+ nlesource2 = gst_element_factory_make ("nlesource", "nlesource2");
+ audiotestsrc2 = gst_element_factory_make ("audiotestsrc", "audiotestsrc2");
+ gst_bin_add (GST_BIN (nlesource2), GST_ELEMENT (audiotestsrc2));
+ g_object_set (nlesource2, "start", (guint64) 0 * GST_SECOND,
+ "duration", total_time, "inpoint", (guint64) 0 * GST_SECOND, "priority",
+ 2, NULL);
+
+ GST_DEBUG ("Adding composition to pipeline");
+ gst_bin_add_many (GST_BIN (pipeline), composition, fakesink, NULL);
+
+ fail_unless (nle_composition_add (GST_BIN (composition), nlesource2));
+ fail_unless (gst_element_link (composition, fakesink) == TRUE);
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+
+ commit_and_wait (composition, &ret);
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING)
+ == GST_STATE_CHANGE_FAILURE);
+
+ message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+
+ if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR)
+ fail_error_message (message);
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, "nle-simple-audiomixer-test-play");
+
+ /* Now play the 10 second composition */
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ GST_LOG ("poll: %" GST_PTR_FORMAT, message);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+ gst_object_unref (bus);
+ gst_object_unref (pipeline);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("nlecomposition");
+ TCase *tc_chain = tcase_create ("nlecomposition");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_change_object_start_stop_in_current_stack);
+ tcase_add_test (tc_chain, test_remove_invalid_object);
+ tcase_add_test (tc_chain, test_remove_last_object);
+
+ tcase_add_test (tc_chain, test_dispose_on_commit);
+
+ if (gst_registry_check_feature_version (gst_registry_get (), "audiomixer", 1,
+ 0, 0)) {
+ tcase_add_test (tc_chain, test_simple_audiomixer);
+ } else {
+ GST_WARNING ("audiomixer element not available, skipping 1 test");
+ }
+
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+#include "common.h"
+
+static void
+fill_pipeline_and_check (GstElement * comp, GList * segments)
+{
+ GstElement *pipeline, *sink;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+ GList *listcopy = copy_segment_list (segments);
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = segments;
+
+ gst_element_link (comp, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to READY");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_DEBUG ("Resetted pipeline to READY");
+
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+
+ collect->seen_segments = NULL;
+ collect->expected_base = 0;
+ collect->expected_segments = listcopy;
+ collect->gotsegment = FALSE;
+
+ GST_DEBUG ("Setting pipeline to PLAYING again");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ carry_on = TRUE;
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ } else {
+ GST_DEBUG ("bus_poll responded, but there wasn't any message...");
+ }
+ }
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+}
+
+GST_START_TEST (test_simple_operation)
+{
+ gboolean ret = FALSE;
+ GstElement *comp, *oper, *source;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [-- oper --] | 0
+ * [------------- source -------------] | 1
+ * */
+
+ /*
+ source
+ Start : 0s
+ Duration : 3s
+ Priority : 1
+ */
+
+ source = videotest_nle_src ("source", 0, 3 * GST_SECOND, 2, 1);
+ fail_if (source == NULL);
+
+ /*
+ operation
+ Start : 1s
+ Duration : 1s
+ Priority : 0
+ */
+
+ oper = new_operation ("oper", "identity", 1 * GST_SECOND, 1 * GST_SECOND, 0);
+ fail_if (oper == NULL);
+
+ /* Add source */
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ nle_composition_add (GST_BIN (comp), source);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+
+ /* Add operaton */
+
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* remove source */
+
+ gst_object_ref (source);
+ nle_composition_remove (GST_BIN (comp), source);
+ check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+
+ /* re-add source */
+ nle_composition_add (GST_BIN (comp), source);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+ gst_object_unref (source);
+
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_pyramid_operations)
+{
+ GstElement *comp, *oper1, *oper2, *source;
+ gboolean ret = FALSE;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /*
+ source
+ Start : 0s
+ Duration : 10s
+ Priority : 2
+ */
+
+ source = videotest_nle_src ("source", 0, 10 * GST_SECOND, 2, 2);
+
+ /*
+ operation1
+ Start : 4s
+ Duration : 2s
+ Priority : 1
+ */
+
+ oper1 =
+ new_operation ("oper1", "identity", 4 * GST_SECOND, 2 * GST_SECOND, 1);
+
+ /*
+ operation2
+ Start : 2s
+ Duration : 6s
+ Priority : 0
+ */
+
+ oper2 =
+ new_operation ("oper2", "identity", 2 * GST_SECOND, 6 * GST_SECOND, 0);
+
+ /* Add source */
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+ ASSERT_OBJECT_REFCOUNT (oper1, "oper1", 1);
+ ASSERT_OBJECT_REFCOUNT (oper2, "oper2", 1);
+
+ nle_composition_add (GST_BIN (comp), source);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source, 0, 10 * GST_SECOND, 10 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 10 * GST_SECOND, 10 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source, "source", 1);
+
+ /* Add operation 1 */
+
+ nle_composition_add (GST_BIN (comp), oper1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (oper1, 4 * GST_SECOND, 6 * GST_SECOND,
+ 2 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 10 * GST_SECOND, 10 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper1, "oper1", 1);
+
+ /* Add operation 2 */
+
+ nle_composition_add (GST_BIN (comp), oper2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (oper2, 2 * GST_SECOND, 8 * GST_SECOND,
+ 6 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 10 * GST_SECOND, 10 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper1, "oper2", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 4 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 4 * GST_SECOND, 6 * GST_SECOND, 4 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 6 * GST_SECOND, 8 * GST_SECOND, 6 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 8 * GST_SECOND, 10 * GST_SECOND, 8 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_pyramid_operations2)
+{
+ gboolean ret;
+ GstElement *comp, *oper, *source1, *source2, *def;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /*
+ source1
+ Start : 0s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source1 = videotest_nle_src ("source1", 0, 2 * GST_SECOND, 2, 2);
+
+ /*
+ operation
+ Start : 1s
+ Duration : 4s
+ Priority : 1
+ */
+
+ oper = new_operation ("oper", "identity", 1 * GST_SECOND, 4 * GST_SECOND, 1);
+
+ /*
+ source2
+ Start : 4s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source2 = videotest_nle_src ("source2", 4 * GST_SECOND, 2 * GST_SECOND, 2, 2);
+
+ /*
+ def (default source)
+ Priority = G_MAXUINT32
+ */
+ def =
+ videotest_nle_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, 2,
+ G_MAXUINT32);
+ g_object_set (def, "expandable", TRUE, NULL);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+ ASSERT_OBJECT_REFCOUNT (def, "default", 1);
+
+ /* Add source 1 */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /* Add source 2 */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ /* Add operation */
+
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ /* Add default */
+
+ nle_composition_add (GST_BIN (comp), def);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 4 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 4 * GST_SECOND, 5 * GST_SECOND, 4 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 5 * GST_SECOND, 6 * GST_SECOND, 5 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_pyramid_operations_expandable)
+{
+ GstElement *comp, *oper, *source1, *source2, *def;
+ gboolean ret = FALSE;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /*
+ source1
+ Start : 0s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source1 = videotest_nle_src ("source1", 0, 2 * GST_SECOND, 2, 2);
+
+ /*
+ operation (expandable)
+ Start : XX
+ Duration : XX
+ Priority : 1
+ */
+
+ oper = new_operation ("oper", "identity", 1 * GST_SECOND, 4 * GST_SECOND, 1);
+ g_object_set (oper, "expandable", TRUE, NULL);
+
+ /*
+ source2
+ Start : 4s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source2 = videotest_nle_src ("source2", 4 * GST_SECOND, 2 * GST_SECOND, 2, 2);
+
+ /*
+ def (default source)
+ Priority = G_MAXUINT32
+ */
+ def =
+ videotest_nle_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, 2,
+ G_MAXUINT32);
+ g_object_set (def, "expandable", TRUE, NULL);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+ ASSERT_OBJECT_REFCOUNT (def, "default", 1);
+
+ /* Add source 1 */
+ nle_composition_add (GST_BIN (comp), source1);
+ /* Add source 2 */
+ nle_composition_add (GST_BIN (comp), source2);
+ /* Add operation */
+ nle_composition_add (GST_BIN (comp), oper);
+ /* Add default */
+ nle_composition_add (GST_BIN (comp), def);
+
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (oper, 0 * GST_SECOND, 6 * GST_SECOND,
+ 6 * GST_SECOND);
+ check_start_stop_duration (source2, 4 * GST_SECOND, 6 * GST_SECOND,
+ 2 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 4 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 4 * GST_SECOND, 6 * GST_SECOND, 4 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_complex_operations)
+{
+ GstElement *comp, *oper, *source1, *source2;
+ gboolean ret = FALSE;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 6 | Priority
+ * ----------------------------------------------------------------------------
+ * [ -oper- ] | 1
+ * [ -source2- -] | 2
+ * [ -source1- -] | 3
+ * */
+
+ /*
+ source1
+ Start : 0s
+ Duration : 4s
+ Priority : 3
+ */
+
+ source1 = videotest_in_bin_nle_src ("source1", 0, 4 * GST_SECOND, 2, 3);
+ fail_if (source1 == NULL);
+
+ /*
+ source2
+ Start : 2s
+ Duration : 4s
+ Priority : 2
+ */
+
+ source2 =
+ videotest_in_bin_nle_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
+ 2);
+ fail_if (source2 == NULL);
+
+ /*
+ operation
+ Start : 2s
+ Duration : 2s
+ Priority : 1
+ */
+
+ oper =
+ new_operation ("oper", "compositor", 2 * GST_SECOND, 2 * GST_SECOND, 1);
+ fail_if (oper == NULL);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Add source1 */
+ nle_composition_add (GST_BIN (comp), source1);
+ check_start_stop_duration (comp, 0, 0, 0);
+ /* If the composition already processed the source, the refcount
+ * might be 2 */
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (source1, "source1", 1, 2);
+
+ /* Add source2 */
+ nle_composition_add (GST_BIN (comp), source2);
+ check_start_stop_duration (comp, 0, 0, 0);
+ /* If the composition already processed the source, the refcount
+ * might be 2 */
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (source2, "source2", 1, 2);
+
+ /* Add operaton */
+ nle_composition_add (GST_BIN (comp), oper);
+ check_start_stop_duration (comp, 0, 0, 0);
+
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 0 * GST_SECOND, 2 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 4 * GST_SECOND, 6 * GST_SECOND, 4 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_complex_operations_bis)
+{
+ GstElement *comp, *oper, *source1, *source2;
+ gboolean ret;
+ GList *segments = NULL;
+
+ ges_init ();
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 .. 6 | Priority
+ * ----------------------------------------------------------------------------
+ * [ ......................[------ oper ----------]..........] | 1 EXPANDABLE
+ * [--------------------- source1 ----------------] | 2
+ * [------------ source2 ------------] | 3
+ * */
+
+
+ /*
+ source1
+ Start : 0s
+ Duration : 4s
+ Priority : 2
+ */
+
+ source1 = videotest_in_bin_nle_src ("source1", 0, 4 * GST_SECOND, 3, 2);
+ fail_if (source1 == NULL);
+
+ /*
+ source2
+ Start : 2s
+ Duration : 4s
+ Priority : 3
+ */
+
+ source2 =
+ videotest_in_bin_nle_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
+ 3);
+ fail_if (source2 == NULL);
+
+ /*
+ operation
+ Start : 2s
+ Duration : 2s
+ Priority : 1
+ EXPANDABLE
+ */
+
+ oper =
+ new_operation ("oper", "compositor", 2 * GST_SECOND, 2 * GST_SECOND, 1);
+ fail_if (oper == NULL);
+ g_object_set (oper, "expandable", TRUE, NULL);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Add source1 */
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Add source2 */
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Add operaton */
+
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+ /* Since it's expandable, it should have changed to full length */
+ check_start_stop_duration (oper, 0 * GST_SECOND, 6 * GST_SECOND,
+ 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 0 * GST_SECOND, 2 * GST_SECOND, 2 * GST_SECOND));
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 0 * GST_SECOND, 2 * GST_SECOND, 4 * GST_SECOND));
+
+ fill_pipeline_and_check (comp, segments);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("nleoperation");
+ TCase *tc_chain = tcase_create ("nleoperation");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_simple_operation);
+ tcase_add_test (tc_chain, test_pyramid_operations);
+ tcase_add_test (tc_chain, test_pyramid_operations2);
+ tcase_add_test (tc_chain, test_pyramid_operations_expandable);
+ if (gst_registry_check_feature_version (gst_registry_get (), "compositor", 0,
+ 11, 0)) {
+ tcase_add_test (tc_chain, test_complex_operations);
+ tcase_add_test (tc_chain, test_complex_operations_bis);
+ } else
+ GST_WARNING ("compositor element not available, skipping 1 test");
+
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+#include "common.h"
+
+GST_START_TEST (test_simple_videotestsrc)
+{
+ GstElement *pipeline;
+ GstElement *nlesource, *sink;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+
+ /*
+ Source 1
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ nlesource =
+ videotest_nle_src ("source1", 1 * GST_SECOND, 1 * GST_SECOND, 2, 1);
+ fail_if (nlesource == NULL);
+ check_start_stop_duration (nlesource, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), nlesource, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = nlesource;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+
+ gst_element_link (nlesource, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ fail_if (sinkpad == NULL);
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (pipeline);
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+ ASSERT_OBJECT_REFCOUNT (nlesource, "nlesource", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ GST_LOG ("poll");
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (FALSE);
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+
+ GST_DEBUG ("Resetted pipeline to NULL");
+
+ gst_object_unref (pipeline);
+ gst_object_unref (bus);
+
+ g_free (collect);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_videotestsrc_in_bin)
+{
+ GstElement *pipeline;
+ GstElement *nlesource, *sink;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+
+ /*
+ Source 1
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ nlesource = videotest_in_bin_nle_src ("source1", 0, 1 * GST_SECOND, 2, 1);
+ /* Handle systems which don't have alpha available */
+ if (nlesource == NULL)
+ return;
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), nlesource, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = nlesource;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+
+ gst_element_link (nlesource, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ fail_if (sinkpad == NULL);
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (pipeline);
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+ ASSERT_OBJECT_REFCOUNT (nlesource, "nlesource", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ GST_LOG ("poll");
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (FALSE);
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_DEBUG ("Resetted pipeline to NULL");
+
+ gst_object_unref (pipeline);
+ gst_object_unref (bus);
+
+ g_free (collect);
+}
+
+GST_END_TEST;
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("nlesource");
+ TCase *tc_chain = tcase_create ("nlesource");
+
+ ges_init ();
+ suite_add_tcase (s, tc_chain);
+
+ if (0)
+ tcase_add_test (tc_chain, test_simple_videotestsrc);
+ tcase_add_test (tc_chain, test_videotestsrc_in_bin);
+
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+#include "common.h"
+static const gchar *compositor_element = NULL;
+
+typedef struct _SeekInfo
+{
+ GstClockTime position; /* Seek value and segment position */
+ GstClockTime start; /* Segment start */
+ GstClockTime stop; /* Segment stop */
+ gboolean expect_failure; /* Whether we expect the seek to fail or not */
+} SeekInfo;
+
+static SeekInfo *
+new_seek_info (GstClockTime position, GstClockTime start, GstClockTime stop,
+ gboolean expect_failure)
+{
+ SeekInfo *info = g_new0 (SeekInfo, 1);
+
+ info->position = position;
+ info->start = start;
+ info->stop = stop;
+ info->expect_failure = expect_failure;
+
+ return info;
+}
+
+static void
+fill_pipeline_and_check (GstElement * comp, GList * segments, GList * seeks)
+{
+ GstElement *pipeline, *sink;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE, expected_failure;
+ GstPad *sinkpad;
+ GList *ltofree = seeks;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = segments;
+ collect->keep_expected_segments = TRUE;
+
+ gst_element_link (comp, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ break;
+ case GST_MESSAGE_ASYNC_DONE:
+ GST_DEBUG ("prerolling done");
+
+ if (seeks == NULL) {
+ carry_on = FALSE;
+ g_list_free_full (collect->expected_segments, g_free);
+ collect->expected_segments = NULL;
+ GST_DEBUG ("Done seeking");
+ break;
+ }
+
+ g_list_free_full (collect->expected_segments, g_free);
+ collect->expected_segments = NULL;
+ expected_failure = TRUE;
+ while (expected_failure && carry_on) {
+ SeekInfo *sinfo = (SeekInfo *) seeks->data;
+
+ seeks = seeks->next;
+
+ if (!sinfo->expect_failure) {
+ collect->gotsegment = FALSE;
+ collect->expected_base = 0;
+ collect->expected_segments =
+ g_list_append (collect->expected_segments, segment_new (1.0,
+ GST_FORMAT_TIME, sinfo->start, sinfo->stop,
+ sinfo->position));
+
+ expected_failure = FALSE;
+ }
+
+ GST_DEBUG ("Seeking to %" GST_TIME_FORMAT ", Expecting (%"
+ GST_TIME_FORMAT " %" GST_TIME_FORMAT ")",
+ GST_TIME_ARGS (sinfo->position), GST_TIME_ARGS (sinfo->start),
+ GST_TIME_ARGS (sinfo->stop));
+
+ fail_unless_equals_int (gst_element_seek_simple (pipeline,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, sinfo->position),
+ !sinfo->expect_failure);
+
+ if (!sinfo->expect_failure) {
+ g_free (sinfo);
+ break;
+ }
+
+ if (seeks == NULL)
+ carry_on = FALSE;
+ g_free (sinfo);
+ }
+ break;
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to READY");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ g_list_free (ltofree);
+ g_free (collect);
+}
+
+static void
+test_simplest_full (void)
+{
+ gboolean ret;
+ GstElement *comp, *source1;
+ GList *segments = NULL;
+ GList *seeks = NULL;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Media start : 5s
+ Priority : 1
+ */
+ source1 =
+ videotest_nle_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
+ 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
+
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 5.5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0 * GST_SECOND, 5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND - 1, 6 * GST_SECOND - 1,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND, 6 * GST_SECOND,
+ 6 * GST_SECOND, TRUE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 5.5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0 * GST_SECOND, 5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND - 1, 6 * GST_SECOND - 1,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND, 6 * GST_SECOND,
+ 6 * GST_SECOND, TRUE));
+
+ fill_pipeline_and_check (comp, segments, seeks);
+}
+
+static void
+test_one_after_other_full (void)
+{
+ gboolean ret;
+ GstElement *comp, *source1, *source2;
+ GList *segments = NULL, *seeks = NULL;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ fail_if (comp == NULL);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [5 source1 ][2 source2 ] | 1
+ *
+ * */
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Media start : 5s
+ Priority : 1
+ */
+ source1 =
+ videotest_nle_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
+ 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 1s
+ Media start : 2s
+ Priority : 1
+ */
+ source2 = videotest_nle_src_full ("source2", 1 * GST_SECOND, 1 * GST_SECOND,
+ 2 * GST_SECOND, 2, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add sources */
+ nle_composition_add (GST_BIN (comp), source1);
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
+
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 5.5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0 * GST_SECOND, 5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND - 1, 6 * GST_SECOND - 1,
+ 6 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND, 2 * GST_SECOND,
+ 3 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2 * GST_SECOND - 1,
+ 3 * GST_SECOND - 1, 3 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2 * GST_SECOND, 3 * GST_SECOND,
+ 3 * GST_SECOND, TRUE));
+
+
+ fill_pipeline_and_check (comp, segments, seeks);
+}
+
+static void
+test_one_under_another_full (void)
+{
+ gboolean ret;
+ GstElement *comp, *source1, *source2;
+ GList *segments = NULL, *seeks = NULL;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ fail_if (comp == NULL);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [ source1 ] | 1
+ * [ source2 ] | 2
+ *
+ * */
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 2s
+ Priority : 1
+ */
+ source1 = videotest_nle_src ("source1", 0, 2 * GST_SECOND, 3, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 2s
+ Priority : 2
+ */
+ source2 = videotest_nle_src ("source2", 1 * GST_SECOND, 2 * GST_SECOND, 2, 2);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ /* Add two sources */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, GST_SECOND, 0));
+
+
+ /* Hit source1 */
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
+ 1 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0 * GST_SECOND, 0 * GST_SECOND,
+ 1 * GST_SECOND, FALSE));
+ /* Hit source1 over source2 */
+ seeks =
+ g_list_append (seeks, new_seek_info (1 * GST_SECOND, 1 * GST_SECOND,
+ 2 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (1.5 * GST_SECOND, 1.5 * GST_SECOND,
+ 2 * GST_SECOND, FALSE));
+ /* Hit source2 */
+ seeks =
+ g_list_append (seeks, new_seek_info (2 * GST_SECOND, 2 * GST_SECOND,
+ 3 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 2.5 * GST_SECOND,
+ 3 * GST_SECOND, FALSE));
+
+ fill_pipeline_and_check (comp, segments, seeks);
+}
+
+static void
+test_one_bin_after_other_full (void)
+{
+ gboolean ret = FALSE;
+ GstElement *comp, *source1, *source2;
+ GList *segments = NULL, *seeks = NULL;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_in_bin_nle_src ("source1", 0, 1 * GST_SECOND, 3, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 =
+ videotest_in_bin_nle_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 2,
+ 1);
+ fail_if (source2 == NULL);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+
+ /* Hit source1 */
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
+ GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0 * GST_SECOND, 0 * GST_SECOND,
+ GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND - 1, GST_SECOND - 1,
+ GST_SECOND, FALSE));
+ /* Hit source2 */
+ seeks =
+ g_list_append (seeks, new_seek_info (1.5 * GST_SECOND, 1.5 * GST_SECOND,
+ 2 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (GST_SECOND, GST_SECOND,
+ 2 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2 * GST_SECOND - 1,
+ 2 * GST_SECOND - 1, 2 * GST_SECOND, FALSE));
+ /* Should fail */
+ seeks =
+ g_list_append (seeks, new_seek_info (2 * GST_SECOND, GST_SECOND,
+ GST_SECOND, TRUE));
+
+ fill_pipeline_and_check (comp, segments, seeks);
+}
+
+
+GST_START_TEST (test_complex_operations)
+{
+ gboolean ret = FALSE;
+ GstElement *comp, *oper, *source1, *source2;
+ GList *segments = NULL, *seeks = NULL;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 .. 6 | Priority
+ * ----------------------------------------------------------------------------
+ * [------ oper ----------] | 1
+ * [--------------------- source1 ----------------] | 2
+ * [------------ source2 ------] | 3
+ * */
+
+ /*
+ source1
+ Start : 0s
+ Duration : 4s
+ Priority : 3
+ */
+
+ source1 = videotest_in_bin_nle_src ("source1", 0, 4 * GST_SECOND, 2, 3);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+
+ /*
+ source2
+ Start : 2s
+ Duration : 4s
+ Priority : 2
+ */
+
+ source2 =
+ videotest_in_bin_nle_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
+ 2);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 2 * GST_SECOND, 6 * GST_SECOND,
+ 4 * GST_SECOND);
+
+ /*
+ operation
+ Start : 2s
+ Duration : 2s
+ Priority : 1
+ */
+
+ oper =
+ new_operation ("oper", compositor_element, 2 * GST_SECOND, 2 * GST_SECOND,
+ 1);
+ fail_if (oper == NULL);
+ check_start_stop_duration (oper, 2 * GST_SECOND, 4 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Add source1 */
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Add source2 */
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Add operaton */
+
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
+
+ /* Seeks */
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
+ 2 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 4.5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+ /* and backwards */
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
+ 2 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 4.5 * GST_SECOND,
+ 6 * GST_SECOND, FALSE));
+
+ fill_pipeline_and_check (comp, segments, seeks);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_complex_operations_bis)
+{
+ gboolean ret = FALSE;
+ GstElement *comp, *oper, *source1, *source2;
+ GList *segments = NULL, *seeks = NULL;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 .. 6 | Priority
+ * ----------------------------------------------------------------------------
+ * [ ......................[------ oper ----------]..........] | 1 EXPANDABLE
+ * [--------------------- source1 ----------------] | 2
+ * [------------ source2 ------] | 3
+ * */
+
+
+ /*
+ source1
+ Start : 0s
+ Duration : 4s
+ Priority : 2
+ */
+
+ source1 = videotest_in_bin_nle_src ("source1", 0, 4 * GST_SECOND, 3, 2);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+
+ /*
+ source2
+ Start : 2s
+ Duration : 4s
+ Priority : 3
+ */
+
+ source2 =
+ videotest_in_bin_nle_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
+ 3);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 2 * GST_SECOND, 6 * GST_SECOND,
+ 4 * GST_SECOND);
+
+ /*
+ operation
+ Start : 2s
+ Duration : 2s
+ Priority : 1
+ EXPANDABLE
+ */
+
+ oper =
+ new_operation ("oper", compositor_element, 2 * GST_SECOND, 2 * GST_SECOND,
+ 1);
+ fail_if (oper == NULL);
+ check_start_stop_duration (oper, 2 * GST_SECOND, 4 * GST_SECOND,
+ 2 * GST_SECOND);
+ g_object_set (oper, "expandable", TRUE, NULL);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Add source1 */
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Add source2 */
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Add operaton */
+
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 4 * GST_SECOND, 4 * GST_SECOND);
+ check_start_stop_duration (source2, 2 * GST_SECOND, 6 * GST_SECOND,
+ 4 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
+ check_start_stop_duration (oper, 0 * GST_SECOND, 6 * GST_SECOND,
+ 6 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
+
+ /* Seeks */
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ /* and backwards */
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+
+ seeks =
+ g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+ seeks =
+ g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 0 * GST_SECOND,
+ 1.5 * GST_SECOND, FALSE));
+
+ fill_pipeline_and_check (comp, segments, seeks);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_simplest)
+{
+ test_simplest_full ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_one_after_other)
+{
+ test_one_after_other_full ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_one_under_another)
+{
+ test_one_under_another_full ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_one_bin_after_other)
+{
+ test_one_bin_after_other_full ();
+}
+
+GST_END_TEST;
+
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("gnonlin-seek");
+ TCase *tc_chain = tcase_create ("general");
+
+ ges_init ();
+ suite_add_tcase (s, tc_chain);
+
+ if (gst_registry_check_feature_version (gst_registry_get (), "compositor", 1,
+ 0, 0)) {
+ compositor_element = "compositor";
+ } else if (gst_registry_check_feature_version (gst_registry_get (),
+ "videomixer", 1, 0, 0)) {
+ compositor_element = "videomixer";
+
+ }
+
+ tcase_add_test (tc_chain, test_simplest);
+ tcase_add_test (tc_chain, test_one_after_other);
+ tcase_add_test (tc_chain, test_one_under_another);
+ tcase_add_test (tc_chain, test_one_bin_after_other);
+
+ if (compositor_element) {
+ tcase_add_test (tc_chain, test_complex_operations);
+ tcase_add_test (tc_chain, test_complex_operations_bis);
+ } else {
+ GST_WARNING ("No compositor element, can not run operations tests");
+ }
+
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+#include "common.h"
+
+static void
+test_simplest_full (void)
+{
+ gboolean ret = FALSE;
+ GstElement *pipeline;
+ GstElement *comp, *sink, *source1;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Media start : 5s
+ Media Duartion : 1s
+ Priority : 1
+ */
+ source1 =
+ videotest_nle_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
+ 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ fail_unless (ret);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
+
+ gst_element_link (comp, sink);
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_ERROR ("Setting pipeline to PLAYING");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ poll_the_bus (bus);
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_ERROR ("Resetted pipeline to READY");
+
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+
+ collect->seen_segments = NULL;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
+ collect->expected_base = 0;
+ collect->gotsegment = FALSE;
+
+ GST_ERROR ("Setting pipeline to PLAYING again");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus AGAIN");
+
+ poll_the_bus (bus);
+
+ fail_if (collect->expected_segments != NULL);
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_check_objects_destroyed_on_unref (pipeline, comp, source1, NULL);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+
+ collect_free (collect);
+}
+
+static void
+test_time_duration_full (void)
+{
+ gboolean ret = FALSE;
+ GstElement *comp, *source1, *source2;
+
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_nle_src ("source1", 0, 1 * GST_SECOND, 3, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 = videotest_nle_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 2, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add one source */
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ fail_unless (ret == TRUE);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Second source */
+
+ ret = FALSE;
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ fail_unless (ret == TRUE);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Remove first source */
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ gst_object_ref (source1);
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 2);
+ GST_ERROR_OBJECT (source1, "Num refs : %i", ((GObject *) source1)->ref_count);
+ nle_composition_remove (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ GST_ERROR_OBJECT (source1, "Num refs : %i", ((GObject *) source1)->ref_count);
+ check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Re-add first source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ gst_object_unref (source1);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ gst_element_set_state (comp, GST_STATE_NULL);
+ gst_object_unref (comp);
+}
+
+static void
+test_one_after_other_full (void)
+{
+ GstElement *pipeline;
+ GstElement *comp, *sink, *source1, *source2;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+
+ gboolean ret = FALSE;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Media start : 5s
+ Priority : 1
+ */
+ source1 =
+ videotest_nle_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
+ 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 1s
+ Media start : 2s
+ Priority : 1
+ */
+ source2 = videotest_nle_src_full ("source2", 1 * GST_SECOND, 1 * GST_SECOND,
+ 2 * GST_SECOND, 2, 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add one source */
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Second source */
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ fail_unless (ret);
+ check_start_stop_duration (source1, 0 * GST_SECOND, 1 * GST_SECOND,
+ 1 * GST_SECOND);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 1 * GST_SECOND));
+
+ gst_element_link (comp, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_DEBUG ("Resetted pipeline to READY");
+
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+
+ collect->seen_segments = NULL;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 1 * GST_SECOND));
+ collect->gotsegment = FALSE;
+ collect->expected_base = 0;
+
+
+ GST_DEBUG ("Setting pipeline to PLAYING again");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ carry_on = TRUE;
+
+ GST_DEBUG ("Let's poll the bus AGAIN");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (TRUE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ } else {
+ GST_DEBUG ("bus_poll responded, but there wasn't any message...");
+ }
+ }
+
+ fail_if (collect->expected_segments != NULL);
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+}
+
+static void
+test_one_under_another_full (void)
+{
+ gboolean ret = FALSE;
+ GstElement *pipeline;
+ GstElement *comp, *sink, *source1, *source2;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /* TOPOLOGY
+ *
+ * 0 1 2 3 4 5 | Priority
+ * ----------------------------------------------------------------------------
+ * [- source1 -] | 1
+ * [- source2 -] | 2
+ * */
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 2s
+ Priority : 1
+ */
+ source1 = videotest_nle_src ("source1", 0, 2 * GST_SECOND, 18, 1);
+ fail_if (source1 == NULL);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 2s
+ Priority : 2
+ */
+ source2 = videotest_nle_src ("source2", 1 * GST_SECOND, 2 * GST_SECOND, 0, 2);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ /* Add two sources */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ nle_composition_add (GST_BIN (comp), source2);
+ check_start_stop_duration (comp, 0, 0 * GST_SECOND, 0 * GST_SECOND);
+ /* Now commiting changes */
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ /* Remove second source */
+
+ gst_object_ref (source1);
+ nle_composition_remove (GST_BIN (comp), source1);
+ check_start_stop_duration (comp, 1 * GST_SECOND, 3 * GST_SECOND,
+ 2 * GST_SECOND);
+
+ /* Re-add second source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
+ gst_object_unref (source1);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, GST_SECOND, 0));
+
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, GST_SECOND, 2 * GST_SECOND,
+ GST_SECOND));
+
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
+
+ gst_element_link (comp, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* check if the segment is the correct one (0s-4s) */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_message_unref (message);
+ }
+ }
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ gst_object_unref (GST_OBJECT (sinkpad));
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+}
+
+static void
+test_one_bin_after_other_full (void)
+{
+ gboolean ret = FALSE;
+ GstElement *pipeline;
+ GstElement *comp, *sink, *source1, *source2;
+ CollectStructure *collect;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on = TRUE;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+ gst_element_set_state (comp, GST_STATE_READY);
+ fail_if (comp == NULL);
+
+ /*
+ Source 1
+ Start : 0s
+ Duration : 1s
+ Priority : 1
+ */
+ source1 = videotest_in_bin_nle_src ("source1", 0, 1 * GST_SECOND, 3, 1);
+ if (source1 == NULL) {
+ gst_object_unref (pipeline);
+ gst_object_unref (comp);
+ return;
+ }
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ /*
+ Source 2
+ Start : 1s
+ Duration : 1s
+ Priority : 1
+ */
+ source2 =
+ videotest_in_bin_nle_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 2,
+ 1);
+ fail_if (source2 == NULL);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ /* Add one source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ fail_unless (ret);
+ check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Second source */
+
+ nle_composition_add (GST_BIN (comp), source2);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
+ check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
+
+ /* Remove first source */
+
+ gst_object_ref (source1);
+ nle_composition_remove (GST_BIN (comp), source1);
+ check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
+ 1 * GST_SECOND);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ /* Re-add first source */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ gst_object_unref (source1);
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ fail_if (sink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ /* Shared data */
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+
+ gst_element_link (comp, sink);
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PLAYING");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ GST_WARNING ("Got an EOS");
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (FALSE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ GST_DEBUG ("Setting pipeline to NULL");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ GST_DEBUG ("Resetted pipeline to READY");
+
+ if (collect->seen_segments)
+ g_list_free (collect->seen_segments);
+
+ collect->seen_segments = NULL;
+
+ /* Expected segments */
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
+ collect->expected_segments = g_list_append (collect->expected_segments,
+ segment_new (1.0, GST_FORMAT_TIME,
+ 1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
+ collect->gotsegment = FALSE;
+ collect->expected_base = 0;
+
+ GST_DEBUG ("Setting pipeline to PLAYING again");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ carry_on = TRUE;
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ /* we should check if we really finished here */
+ carry_on = FALSE;
+ break;
+ case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_DONE:
+ /* We shouldn't see any segement messages, since we didn't do a segment seek */
+ GST_WARNING ("Saw a Segment start/stop");
+ fail_if (FALSE);
+ break;
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ gst_object_unref (GST_OBJECT (sinkpad));
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+}
+
+GST_START_TEST (test_simplest)
+{
+ ges_init ();
+ test_simplest_full ();
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_time_duration)
+{
+ ges_init ();
+ test_time_duration_full ();
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_one_after_other)
+{
+ ges_init ();
+ test_one_after_other_full ();
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_one_under_another)
+{
+ ges_init ();
+ test_one_under_another_full ();
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_one_bin_after_other)
+{
+ ges_init ();
+ test_one_bin_after_other_full ();
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("gnonlin-simple");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_time_duration);
+ tcase_add_test (tc_chain, test_simplest);
+ tcase_add_test (tc_chain, test_one_after_other);
+ tcase_add_test (tc_chain, test_one_under_another);
+ tcase_add_test (tc_chain, test_one_bin_after_other);
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2016 Sjors Gielen <mixml-ges@sjorsgielen.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "plugins/nle/nleobject.h"
+
+GST_START_TEST (test_tempochange)
+{
+ GstElement *pipeline;
+ GstElement *comp, *source1, *def, *sink, *oper;
+ GList *segments = NULL;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on, ret = FALSE;
+ CollectStructure *collect;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ gst_element_link (comp, sink);
+
+ /*
+ source1
+ Start : 0s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source1 = audiotest_bin_src ("source1", 0, 2 * GST_SECOND, 2, 2);
+
+ /*
+ def (default source)
+ Priority = G_MAXUINT32
+ */
+ def =
+ audiotest_bin_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, G_MAXUINT32,
+ 1);
+ g_object_set (def, "expandable", TRUE, NULL);
+
+ /* Operation */
+ oper = new_operation ("oper", "identity", 0, 2 * GST_SECOND, 1);
+ fail_if (oper == NULL);
+ ((NleObject *) oper)->media_duration_factor = 2.0;
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (def, "default", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Add source 1 */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ nle_composition_add (GST_BIN (comp), def);
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (oper, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /* Define expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0 * GST_SECOND, 4.0 * GST_SECOND, 0));
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ collect->expected_segments = segments;
+ collect->keep_expected_segments = FALSE;
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+ gst_object_unref (sinkpad);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PAUSED");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ carry_on = TRUE;
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ASYNC_DONE:
+ {
+ carry_on = FALSE;
+ GST_DEBUG ("Pipeline reached PAUSED, stopping polling");
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ {
+ GST_WARNING ("Saw EOS");
+
+ fail_if (TRUE);
+ }
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ fail_unless_equals_float (((NleObject *) source1)->media_duration_factor,
+ 1.0f);
+ fail_unless_equals_float (((NleObject *)
+ source1)->recursive_media_duration_factor, 2.0f);
+ fail_unless_equals_float (((NleObject *) oper)->media_duration_factor, 2.0f);
+ fail_unless_equals_float (((NleObject *)
+ oper)->recursive_media_duration_factor, 2.0f);
+
+ GST_DEBUG ("Setting pipeline to READY");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ collect_free (collect);
+}
+
+GST_END_TEST;
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("nle");
+ TCase *tc_chain = tcase_create ("tempochange");
+
+ if (atexit (ges_deinit) != 0) {
+ GST_ERROR ("failed to set ges_deinit as exit function");
+ }
+
+ ges_init ();
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_tempochange);
+
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)
--- /dev/null
+[junit-xml]
+always-on = True
+path = @path@
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Alexandru Băluț <alexandru.balut@gmail.com>
+# Copyright (c) 2016, Thibault Saunier
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+import gi
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GES", "1.0")
+
+from gi.repository import Gst # noqa
+from gi.repository import GES # noqa
+from gi.repository import GLib # noqa
+import contextlib # noqa
+import os #noqa
+import unittest # noqa
+import tempfile # noqa
+
+Gst.init(None)
+GES.init()
+
+
+def create_main_loop():
+ """Creates a MainLoop with a timeout."""
+ mainloop = GLib.MainLoop()
+ timed_out = False
+
+ def timeout_cb(unused):
+ nonlocal timed_out
+ timed_out = True
+ mainloop.quit()
+
+ def run(timeout_seconds=5, until_empty=False):
+ source = GLib.timeout_source_new_seconds(timeout_seconds)
+ source.set_callback(timeout_cb)
+ source.attach()
+ if until_empty:
+ GLib.idle_add(mainloop.quit)
+ GLib.MainLoop.run(mainloop)
+ source.destroy()
+ if timed_out:
+ raise Exception("Timed out after %s seconds" % timeout_seconds)
+
+ mainloop.run = run
+ return mainloop
+
+
+def create_project(with_group=False, saved=False):
+ """Creates a project with two clips in a group."""
+ project = GES.Project.new(None)
+ timeline = project.extract()
+ layer = timeline.append_layer()
+
+ if with_group:
+ clip1 = GES.TitleClip()
+ clip1.set_start(0)
+ clip1.set_duration(10)
+ layer.add_clip(clip1)
+ clip2 = GES.TitleClip()
+ clip2.set_start(100)
+ clip2.set_duration(10)
+ layer.add_clip(clip2)
+ group = GES.Container.group([clip1, clip2])
+
+ if saved:
+ uri = "file://%s" % tempfile.NamedTemporaryFile(suffix=".xges").name
+ project.save(timeline, uri, None, overwrite=True)
+
+ return timeline
+
+
+@contextlib.contextmanager
+def created_project_file(xges):
+ _, xges_path = tempfile.mkstemp(suffix=".xges")
+ with open(xges_path, "w") as f:
+ f.write(xges)
+
+ yield Gst.filename_to_uri(os.path.abspath(xges_path))
+
+ os.remove(xges_path)
+
+
+def get_asset_uri(name):
+ python_tests_dir = os.path.dirname(os.path.abspath(__file__))
+ assets_dir = os.path.join(python_tests_dir, "..", "assets")
+ return Gst.filename_to_uri(os.path.join(assets_dir, name))
+
+
+class GESTest(unittest.TestCase):
+
+ def _log(self, func, format, *args):
+ string = format
+ if args:
+ string = string % args[0]
+ func(string)
+
+ def log(self, format, *args):
+ self._log(Gst.log, format, *args)
+
+ def debug(self, format, *args):
+ self._log(Gst.debug, format, *args)
+
+ def info(self, format, *args):
+ self._log(Gst.info, format, *args)
+
+ def fixme(self, format, *args):
+ self._log(Gst.fixme, format, *args)
+
+ def warning(self, format, *args):
+ self._log(Gst.warning, format, *args)
+
+ def error(self, format, *args):
+ self._log(Gst.error, format, *args)
+
+ def check_clip_values(self, clip, start, in_point, duration):
+ for elem in [clip] + clip.get_children(False):
+ self.check_element_values(elem, start, in_point, duration)
+
+ def check_element_values(self, element, start, in_point, duration):
+ self.assertEqual(element.props.start, start, element)
+ self.assertEqual(element.props.in_point, in_point, element)
+ self.assertEqual(element.props.duration, duration, element)
+
+ def assert_effects(self, clip, *effects):
+ # Make sure there are no other effects.
+ self.assertEqual(set(clip.get_top_effects()), set(effects))
+
+ # Make sure their order is correct.
+ indexes = [clip.get_top_effect_index(effect)
+ for effect in effects]
+ self.assertEqual(indexes, list(range(len(effects))))
+
+
+class GESSimpleTimelineTest(GESTest):
+
+ def __init__(self, *args):
+ self.track_types = [GES.TrackType.AUDIO, GES.TrackType.VIDEO]
+ super(GESSimpleTimelineTest, self).__init__(*args)
+
+ def timeline_as_str(self):
+ res = "====== %s =======\n" % self.timeline
+ for layer in self.timeline.get_layers():
+ res += "Layer %04d: " % layer.get_priority()
+ for clip in layer.get_clips():
+ res += "{ %s }" % clip
+ res += '\n------------------------\n'
+
+ for group in self.timeline.get_groups():
+ res += "GROUP %s :" % group
+ for clip in group.get_children(False):
+ res += " { %s }" % clip.props.name
+ res += '\n'
+ res += "================================\n"
+ return res
+
+ def print_timeline(self):
+ print(self.timeline_as_str())
+
+ def setUp(self):
+ self.timeline = GES.Timeline.new()
+ for track_type in self.track_types:
+ self.assertIn(
+ track_type, [GES.TrackType.AUDIO, GES.TrackType.VIDEO])
+ if track_type == GES.TrackType.AUDIO:
+ self.assertTrue(self.timeline.add_track(GES.AudioTrack.new()))
+ else:
+ self.assertTrue(self.timeline.add_track(GES.VideoTrack.new()))
+
+ self.assertEqual(len(self.timeline.get_tracks()),
+ len(self.track_types))
+ self.layer = self.timeline.append_layer()
+
+ def add_clip(self, start, in_point, duration):
+ clip = GES.TestClip()
+ clip.props.start = start
+ clip.props.in_point = in_point
+ clip.props.duration = duration
+ self.assertTrue(self.layer.add_clip(clip))
+
+ return clip
+
+ def append_clip(self, layer=0):
+ layer = self.timeline.get_layers()[layer]
+ clip = GES.TestClip()
+ clip.props.start = layer.get_duration()
+ clip.props.duration = 10
+ self.assertTrue(layer.add_clip(clip))
+
+ return clip
+
+ def assertTimelineTopology(self, topology, groups=[]):
+ res = []
+ for layer in self.timeline.get_layers():
+ layer_timings = []
+ for clip in layer.get_clips():
+ layer_timings.append(
+ (type(clip), clip.props.start, clip.props.duration))
+
+ res.append(layer_timings)
+ if topology != res:
+ Gst.error(self.timeline_as_str())
+ self.assertEqual(topology, res)
+
+ timeline_groups = self.timeline.get_groups()
+ if groups and timeline_groups:
+ for i, group in enumerate(groups):
+ self.assertEqual(set(group), set(timeline_groups[i].get_children(False)))
+ self.assertEqual(len(timeline_groups), i + 1)
+
+ return res
--- /dev/null
+import os
+import gi.overrides
+
+LOCAL_OVERRIDE_PATH = "gst-editing-services/bindings/python/gi/overrides/"
+FILE = os.path.realpath(__file__)
+if not gi.overrides.__path__[0].endswith(LOCAL_OVERRIDE_PATH):
+ local_overrides = None
+ # our overrides don't take precedence, let's fix it
+ for i, path in enumerate(gi.overrides.__path__):
+ if path.endswith(LOCAL_OVERRIDE_PATH):
+ local_overrides = path
+
+ if local_overrides:
+ gi.overrides.__path__.remove(local_overrides)
+ else:
+ local_overrides = os.path.abspath(os.path.join(FILE, "../../../../../", LOCAL_OVERRIDE_PATH))
+
+ gi.overrides.__path__.insert(0, local_overrides)
+
+# Execute previously set sitecustomize.py script if it existed
+if os.environ.get("GST_ENV"):
+ old_sitecustomize = os.path.join(os.path.dirname(__file__),
+ "old.sitecustomize.gstuninstalled.py")
+ if os.path.exists(old_sitecustomize):
+ exec(compile(open(old_sitecustomize).read(), old_sitecustomize, 'exec'))
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019 Thibault Saunier <tsaunier@igalia.com>
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+from . import overrides_hack
+
+import os
+import gi
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GES", "1.0")
+
+from gi.repository import Gst # noqa
+from gi.repository import GLib # noqa
+from gi.repository import GES # noqa
+import unittest # noqa
+from unittest import mock
+
+from .common import GESSimpleTimelineTest # noqa
+
+Gst.init(None)
+GES.init()
+
+
+class TestTimeline(unittest.TestCase):
+
+ def test_request_relocated_assets_sync(self):
+ path = os.path.join(__file__, "../../../", "png.png")
+ with self.assertRaises(GLib.Error):
+ GES.UriClipAsset.request_sync(Gst.filename_to_uri(path))
+
+ GES.add_missing_uri_relocation_uri(Gst.filename_to_uri(os.path.join(__file__, "../../assets")), False)
+ path = os.path.join(__file__, "../../", "png.png")
+ self.assertEqual(GES.UriClipAsset.request_sync(Gst.filename_to_uri(path)).props.id,
+ Gst.filename_to_uri(os.path.join(__file__, "../../assets/png.png")))
\ No newline at end of file
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2015, Thibault Saunier
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+from . import overrides_hack
+
+import tempfile
+
+import gi
+gi.require_version("Gst", "1.0")
+gi.require_version("GES", "1.0")
+
+from gi.repository import Gst # noqa
+Gst.init(None) # noqa
+from gi.repository import GES # noqa
+GES.init()
+
+from . import common # noqa
+
+import unittest # noqa
+
+
+class TestCopyPaste(unittest.TestCase):
+
+ def setUp(self):
+ self.timeline = GES.Timeline.new_audio_video()
+ self.assertEqual(len(self.timeline.get_tracks()), 2)
+ self.layer = self.timeline.append_layer()
+
+ def testCopyClipRemoveAndPaste(self):
+ clip1 = GES.TestClip.new()
+ clip1.props.duration = 10
+
+ self.layer.add_clip(clip1)
+
+ self.assertEqual(len(clip1.get_children(False)), 2)
+
+ copy = clip1.copy(True)
+ self.assertEqual(len(self.layer.get_clips()), 1)
+
+ self.layer.remove_clip(clip1)
+
+ copy.paste(10)
+ self.assertEqual(len(self.layer.get_clips()), 1)
+
+ def testCopyPasteTitleClip(self):
+ clip1 = GES.TitleClip.new()
+ clip1.props.duration = 10
+
+ self.layer.add_clip(clip1)
+ self.assertEqual(len(clip1.get_children(False)), 1)
+
+ copy = clip1.copy(True)
+ self.assertEqual(len(self.layer.get_clips()), 1)
+
+ copy.paste(10)
+ self.assertEqual(len(self.layer.get_clips()), 2)
+
+
+class TestTransitionClip(unittest.TestCase):
+
+ def test_serialize_invert(self):
+ timeline = GES.Timeline.new()
+ timeline.add_track(GES.VideoTrack.new())
+ layer = timeline.append_layer()
+
+ clip1 = GES.TransitionClip.new_for_nick("crossfade")
+ clip1.props.duration = Gst.SECOND
+ self.assertTrue(layer.add_clip(clip1))
+
+ vtransition, = clip1.children
+ vtransition.set_inverted(True)
+ self.assertEqual(vtransition.props.invert, True)
+
+ with tempfile.NamedTemporaryFile() as tmpxges:
+ uri = Gst.filename_to_uri(tmpxges.name)
+ timeline.save_to_uri(uri, None, True)
+
+ timeline = GES.Timeline.new_from_uri(uri)
+ self.assertIsNotNone(timeline)
+ layer, = timeline.get_layers()
+ clip, = layer.get_clips()
+ vtransition, = clip.children
+ self.assertEqual(vtransition.props.invert, True)
+
+class TestTitleClip(unittest.TestCase):
+
+ def testSetColor(self):
+ timeline = GES.Timeline.new_audio_video()
+ clip = GES.TitleClip.new()
+ timeline.append_layer().add_clip(clip )
+ self.assertTrue(clip.set_child_property('color', 1))
+ self.assertTrue(clip.set_child_property('color', 4294967295))
+
+ def testGetPropertyNotInTrack(self):
+ title_clip = GES.TitleClip.new()
+ self.assertEqual(title_clip.props.text, "")
+ self.assertEqual(title_clip.props.font_desc, "Serif 36")
+
+ def test_split_effect(self):
+ timeline = GES.Timeline.new()
+ timeline.add_track(GES.VideoTrack.new())
+ layer = timeline.append_layer()
+
+ clip1 = GES.TitleClip.new()
+ clip1.props.duration = Gst.SECOND
+ self.assertTrue(layer.add_clip(clip1))
+
+ effect = GES.Effect.new("agingtv")
+ self.assertTrue(clip1.add(effect))
+
+ children1 = clip1.get_children(True)
+ self.assertNotEqual(children1[0].props.priority,
+ children1[1].props.priority)
+
+ clip2 = clip1.split(Gst.SECOND / 2)
+
+ children1 = clip1.get_children(True)
+ self.assertNotEqual(children1[0].props.priority,
+ children1[1].props.priority)
+
+ children2 = clip2.get_children(True)
+ self.assertNotEqual(children2[0].props.priority,
+ children2[1].props.priority)
+
+
+class TestTrackElements(common.GESTest):
+
+ def test_add_to_layer_with_effect_remove_add(self):
+ timeline = GES.Timeline.new_audio_video()
+ video_track, audio_track = timeline.get_tracks()
+ layer = timeline.append_layer()
+
+ test_clip = GES.TestClip()
+ self.assertEqual(test_clip.get_children(True), [])
+ self.assertTrue(layer.add_clip(test_clip))
+ audio_source = test_clip.find_track_element(None, GES.AudioSource)
+ video_source = test_clip.find_track_element(None, GES.VideoSource)
+
+ self.assertTrue(test_clip.set_child_property("volume", 0.0))
+ self.assertEqual(audio_source.get_child_property("volume")[1], 0.0)
+
+ effect = GES.Effect.new("agingtv")
+ test_clip.add(effect)
+ self.assertEqual(audio_source.props.track, audio_track)
+ self.assertEqual(video_source.props.track, video_track)
+ self.assertEqual(effect.props.track, video_track)
+
+ children = test_clip.get_children(True)
+ layer.remove_clip(test_clip)
+ self.assertEqual(test_clip.get_children(True), children)
+ self.assertEqual(audio_source.props.track, None)
+ self.assertEqual(video_source.props.track, None)
+ self.assertEqual(effect.props.track, None)
+
+ self.assertTrue(layer.add_clip(test_clip))
+ self.assertEqual(test_clip.get_children(True), children)
+ self.assertEqual(audio_source.props.track, audio_track)
+ self.assertEqual(video_source.props.track, video_track)
+ self.assertEqual(effect.props.track, video_track)
+
+ audio_source = test_clip.find_track_element(None, GES.AudioSource)
+ self.assertFalse(audio_source is None)
+ self.assertEqual(audio_source.get_child_property("volume")[1], 0.0)
+ self.assertEqual(audio_source.props.track, audio_track)
+ self.assertEqual(video_source.props.track, video_track)
+ self.assertEqual(effect.props.track, video_track)
+
+ def test_effects_priority(self):
+ timeline = GES.Timeline.new_audio_video()
+ layer = timeline.append_layer()
+
+ test_clip = GES.TestClip.new()
+ layer.add_clip(test_clip)
+ self.assert_effects(test_clip)
+
+ effect1 = GES.Effect.new("agingtv")
+ test_clip.add(effect1)
+ self.assert_effects(test_clip, effect1)
+
+ test_clip.set_top_effect_index(effect1, 1)
+ self.assert_effects(test_clip, effect1)
+ test_clip.set_top_effect_index(effect1, 10)
+ self.assert_effects(test_clip, effect1)
+
+ effect2 = GES.Effect.new("dicetv")
+ test_clip.add(effect2)
+ self.assert_effects(test_clip, effect1, effect2)
+
+ test_clip.remove(effect1)
+ self.assert_effects(test_clip, effect2)
+
+ def test_signal_order_when_removing_effect(self):
+ timeline = GES.Timeline.new_audio_video()
+ layer = timeline.append_layer()
+
+ test_clip = GES.TestClip.new()
+ layer.add_clip(test_clip)
+ self.assert_effects(test_clip)
+
+ effect1 = GES.Effect.new("agingtv")
+ test_clip.add(effect1)
+ effect2 = GES.Effect.new("dicetv")
+ test_clip.add(effect2)
+ self.assert_effects(test_clip, effect1, effect2)
+
+ mainloop = common.create_main_loop()
+
+ signals = []
+
+ def handler_cb(*args):
+ signals.append(args[-1])
+
+ test_clip.connect("child-removed", handler_cb, "child-removed")
+ effect2.connect("notify::priority", handler_cb, "notify::priority")
+ test_clip.remove(effect1)
+ test_clip.disconnect_by_func(handler_cb)
+ effect2.disconnect_by_func(handler_cb)
+ self.assert_effects(test_clip, effect2)
+
+ mainloop.run(until_empty=True)
+
+ self.assertEqual(signals, ["child-removed", "notify::priority"])
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2015, Thibault Saunier
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+from . import overrides_hack
+
+import gi
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GES", "1.0")
+
+from gi.repository import Gst # noqa
+from gi.repository import GES # noqa
+
+from . import common # noqa
+
+import unittest # noqa
+from unittest import mock
+
+Gst.init(None)
+GES.init()
+
+
+class TestGroup(common.GESSimpleTimelineTest):
+
+ def testCopyGroup(self):
+ clip1 = GES.TestClip.new()
+ clip1.props.duration = 10
+
+ self.layer.add_clip(clip1)
+
+ self.assertEqual(len(clip1.get_children(False)), 2)
+
+ group = GES.Group.new()
+ self.assertTrue(group.add(clip1))
+
+ self.assertEqual(len(group.get_children(False)), 1)
+
+ group_copy = group.copy(True)
+ self.assertEqual(len(group_copy.get_children(False)), 0)
+
+ self.assertTrue(group_copy.paste(10))
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 2)
+ self.assertEqual(clips[1].props.start, 10)
+
+ clips[1].edit([], 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10)
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 1)
+
+ def testPasteChangedGroup(self):
+ clip1 = GES.TestClip.new()
+ clip1.props.duration = 10
+
+ clip2 = GES.TestClip.new()
+ clip2.props.start = 20
+ clip2.props.duration = 10
+
+ self.layer.add_clip(clip1)
+ self.layer.add_clip(clip2)
+
+ self.assertEqual(len(clip1.get_children(False)), 2)
+
+ group = GES.Group.new()
+ self.assertTrue(group.add(clip1))
+
+ self.assertEqual(len(group.get_children(False)), 1)
+
+ group_copy = group.copy(True)
+ self.assertEqual(len(group_copy.get_children(False)), 0)
+
+ self.assertTrue(group.add(clip2))
+ self.assertEqual(len(group.get_children(False)), 2)
+ self.assertEqual(len(group_copy.get_children(False)), 0)
+
+ self.assertTrue(group_copy.paste(10))
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 3)
+ self.assertEqual(clips[1].props.start, 10)
+
+ def testPasteChangedGroup(self):
+ clip1 = GES.TestClip.new()
+ clip1.props.duration = 10
+
+ clip2 = GES.TestClip.new()
+ clip2.props.start = 20
+ clip2.props.duration = 10
+
+ self.layer.add_clip(clip1)
+ self.layer.add_clip(clip2)
+
+ self.assertEqual(len(clip1.get_children(False)), 2)
+
+ group = GES.Group.new()
+ self.assertTrue(group.add(clip1))
+
+ self.assertEqual(len(group.get_children(False)), 1)
+
+ group_copy = group.copy(True)
+ self.assertEqual(len(group_copy.get_children(False)), 0)
+
+ self.assertTrue(group.add(clip2))
+ self.assertEqual(len(group.get_children(False)), 2)
+ self.assertEqual(len(group_copy.get_children(False)), 0)
+
+ self.assertTrue(group_copy.paste(10))
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 3)
+ self.assertEqual(clips[1].props.start, 10)
+
+ def test_move_clips_between_layers_with_auto_transition(self):
+ self.timeline.props.auto_transition = True
+ layer2 = self.timeline.append_layer()
+ clip1 = GES.TestClip.new()
+ clip1.props.start = 0
+ clip1.props.duration = 30
+
+ clip2 = GES.TestClip.new()
+ clip2.props.start = 20
+ clip2.props.duration = 20
+
+ self.layer.add_clip(clip1)
+ self.layer.add_clip(clip2)
+
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 4)
+ self.assertEqual(layer2.get_clips(), [])
+
+ group = GES.Container.group(clips)
+ self.assertIsNotNone(group)
+
+ self.assertTrue(clip1.edit(
+ self.timeline.get_layers(), 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
+ self.assertEqual(self.layer.get_clips(), [])
+
+ clips = layer2.get_clips()
+ self.assertEqual(len(clips), 4)
+
+ def test_remove_emits_signal(self):
+ clip1 = GES.TestClip.new()
+ self.layer.add_clip(clip1)
+
+ group = GES.Group.new()
+ child_removed_cb = mock.Mock()
+ group.connect("child-removed", child_removed_cb)
+
+ group.add(clip1)
+ group.remove(clip1)
+ child_removed_cb.assert_called_once_with(group, clip1)
+
+ group.add(clip1)
+ child_removed_cb.reset_mock()
+ group.ungroup(recursive=False)
+ child_removed_cb.assert_called_once_with(group, clip1)
+
+ def test_loaded_project_has_groups(self):
+ mainloop = common.create_main_loop()
+ timeline = common.create_project(with_group=True, saved=True)
+ layer, = timeline.get_layers()
+ group, = timeline.get_groups()
+ self.assertEqual(len(layer.get_clips()), 2)
+ for clip in layer.get_clips():
+ self.assertEqual(clip.get_parent(), group)
+
+ # Reload the project, check the group.
+ project = GES.Project.new(uri=timeline.get_asset().props.uri)
+
+ loaded_called = False
+ def loaded(unused_project, unused_timeline):
+ nonlocal loaded_called
+ loaded_called = True
+ mainloop.quit()
+ project.connect("loaded", loaded)
+
+ timeline = project.extract()
+
+ mainloop.run()
+ self.assertTrue(loaded_called)
+
+ layer, = timeline.get_layers()
+ group, = timeline.get_groups()
+ self.assertEqual(len(layer.get_clips()), 2)
+ for clip in layer.get_clips():
+ self.assertEqual(clip.get_parent(), group)
+
+ def test_moving_group_with_transition(self):
+ self.timeline.props.auto_transition = True
+ clip1 = GES.TestClip.new()
+ clip1.props.start = 0
+ clip1.props.duration = 30
+
+ clip2 = GES.TestClip.new()
+ clip2.props.start = 20
+ clip2.props.duration = 20
+
+ self.layer.add_clip(clip1)
+ self.layer.add_clip(clip2)
+
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 4)
+
+ video_transition = None
+ audio_transition = None
+ for clip in clips:
+ if isinstance(clip, GES.TransitionClip):
+ if isinstance(clip.get_children(False)[0], GES.VideoTransition):
+ video_transition = clip
+ else:
+ audio_transition = clip
+ self.assertIsNotNone(audio_transition)
+ self.assertIsNotNone(video_transition)
+
+ self.assertEqual(video_transition.props.start, 20)
+ self.assertEqual(video_transition.props.duration, 10)
+ self.assertEqual(audio_transition.props.start, 20)
+ self.assertEqual(audio_transition.props.duration, 10)
+
+ group = GES.Container.group(clips)
+ self.assertIsNotNone(group)
+
+ self.assertTrue(clip2.edit(
+ self.timeline.get_layers(), 0,
+ GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 25))
+ clip2.props.start = 25
+
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 4)
+ self.assertEqual(clip1.props.start, 5)
+ self.assertEqual(clip1.props.duration, 30)
+ self.assertEqual(clip2.props.start, 25)
+ self.assertEqual(clip2.props.duration, 20)
+
+ self.assertEqual(video_transition.props.start, 25)
+ self.assertEqual(video_transition.props.duration, 10)
+ self.assertEqual(audio_transition.props.start, 25)
+ self.assertEqual(audio_transition.props.duration, 10)
+
+ def test_moving_group_snapping_from_the_middle(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ snapped_positions = []
+ def snapping_started_cb(timeline, first_element, second_element,
+ position, snapped_positions):
+ snapped_positions.append(position)
+
+ self.timeline.props.snapping_distance = 5
+ self.timeline.connect("snapping-started", snapping_started_cb,
+ snapped_positions)
+
+ for start in range(0, 20, 5):
+ clip = GES.TestClip.new()
+ clip.props.start = start
+ clip.props.duration = 5
+ self.layer.add_clip(clip)
+
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 4)
+
+ group = GES.Container.group(clips[1:3])
+ self.assertIsNotNone(group)
+
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 5),
+ (GES.TestClip, 5, 5),
+ (GES.TestClip, 10, 5),
+ (GES.TestClip, 15, 5),
+ ],
+ ], groups=[clips[1:3]])
+
+ self.assertEqual(clips[1].props.start, 5)
+ self.assertEqual(clips[2].props.start, 10)
+ clips[2].edit([], 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 11)
+
+ self.assertEqual(snapped_positions[0], 5)
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 5),
+ (GES.TestClip, 5, 5),
+ (GES.TestClip, 10, 5),
+ (GES.TestClip, 15, 5),
+ ],
+ ], groups=[clips[1:3]])
+
+ def test_rippling_with_group(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ for _ in range(4):
+ self.append_clip()
+
+ snapped_positions = []
+ def snapping_started_cb(timeline, first_element, second_element,
+ position, snapped_positions):
+ snapped_positions.append(position)
+
+ self.timeline.props.snapping_distance = 5
+ self.timeline.connect("snapping-started", snapping_started_cb,
+ snapped_positions)
+
+ clips = self.layer.get_clips()
+ self.assertEqual(len(clips), 4)
+
+ group_clips = clips[1:3]
+ GES.Container.group(group_clips)
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ],
+ ], groups=[group_clips])
+
+ self.assertFalse(clips[2].edit([], 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 5))
+
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ],
+ ], groups=[group_clips])
+
+ # Negative start...
+ self.assertFalse(clips[2].edit([], 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 1))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ],
+ ], groups=[group_clips])
+
+ self.assertTrue(clips[2].edit([], 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 20))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ ],
+ [
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ],
+ ], groups=[group_clips])
\ No newline at end of file
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Alexandru Băluț <alexandru.balut@gmail.com>
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+from . import overrides_hack
+
+import gi
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GES", "1.0")
+
+from gi.repository import Gst # noqa
+from gi.repository import GES # noqa
+import unittest # noqa
+from unittest import mock
+
+from . import common # noqa
+
+Gst.init(None)
+GES.init()
+
+
+class TestTimeline(common.GESSimpleTimelineTest):
+
+ def test_signals_not_emitted_when_loading(self):
+ mainloop = common.create_main_loop()
+ timeline = common.create_project(with_group=True, saved=True)
+
+ # Reload the project, check the group.
+ project = GES.Project.new(uri=timeline.get_asset().props.uri)
+
+ loaded_called = False
+
+ def loaded(unused_project, unused_timeline):
+ nonlocal loaded_called
+ loaded_called = True
+ mainloop.quit()
+ project.connect("loaded", loaded)
+
+ timeline = project.extract()
+
+ signals = ["layer-added", "group-added", "track-added"]
+ handle = mock.Mock()
+ for signal in signals:
+ timeline.connect(signal, handle)
+
+ mainloop.run()
+ self.assertTrue(loaded_called)
+ handle.assert_not_called()
+
+ def test_timeline_duration(self):
+ self.append_clip()
+ self.append_clip()
+ clips = self.layer.get_clips()
+
+ self.assertEqual(self.timeline.props.duration, 20)
+ self.layer.remove_clip(clips[1])
+ self.assertEqual(self.timeline.props.duration, 10)
+
+ self.append_clip()
+ self.append_clip()
+ clips = self.layer.get_clips()
+ self.assertEqual(self.timeline.props.duration, 30)
+
+ group = GES.Container.group(clips[1:])
+ self.assertEqual(self.timeline.props.duration, 30)
+
+ group1 = GES.Container.group([])
+ group1.add(group)
+ self.assertEqual(self.timeline.props.duration, 30)
+
+ def test_spliting_with_auto_transition_on_the_left(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+
+ self.timeline.props.auto_transition = True
+ clip1 = self.add_clip(0, 0, 100)
+ clip2 = self.add_clip(50, 0, 100)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 100),
+ (GES.TransitionClip, 50, 50),
+ (GES.TestClip, 50, 100)
+ ]
+ ])
+
+ clip1.split(25)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 25),
+ (GES.TestClip, 25, 75),
+ (GES.TransitionClip, 50, 50),
+ (GES.TestClip, 50, 100),
+ ]
+ ])
+
+ clip2.split(125)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 25),
+ (GES.TestClip, 25, 75),
+ (GES.TransitionClip, 50, 50),
+ (GES.TestClip, 50, 75),
+ (GES.TestClip, 125, 25),
+ ]
+ ])
+
+
+class TestEditing(common.GESSimpleTimelineTest):
+
+ def test_transition_disappears_when_moving_to_another_layer(self):
+ self.timeline.props.auto_transition = True
+ unused_clip1 = self.add_clip(0, 0, 100)
+ clip2 = self.add_clip(50, 0, 100)
+ self.assertEqual(len(self.layer.get_clips()), 4)
+
+ layer2 = self.timeline.append_layer()
+ clip2.edit([], layer2.get_priority(), GES.EditMode.EDIT_NORMAL,
+ GES.Edge.EDGE_NONE, clip2.props.start)
+ self.assertEqual(len(self.layer.get_clips()), 1)
+ self.assertEqual(len(layer2.get_clips()), 1)
+
+ def activate_snapping(self):
+ self.timeline.set_snapping_distance(5)
+ self.snapped_at = []
+
+ def _snapped_cb(timeline, elem1, elem2, position):
+ self.snapped_at.append(position)
+ Gst.error('%s' % position)
+
+ def _snapped_end_cb(timeline, elem1, elem2, position):
+ if self.snapped_at: # Ignoring first snap end.
+ self.snapped_at.append(Gst.CLOCK_TIME_NONE)
+ Gst.error('%s' % position)
+
+ self.timeline.connect("snapping-started", _snapped_cb)
+ self.timeline.connect("snapping-ended", _snapped_end_cb)
+
+ def test_snap_start_snap_end(self):
+ clip = self.append_clip()
+ self.append_clip()
+
+ self.activate_snapping()
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ clip.props.start = 18
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+ self.assertEqual(self.snapped_at, [20])
+
+ clip.props.start = 30
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 30, 10),
+ ]
+ ])
+ self.assertEqual(self.snapped_at, [20, Gst.CLOCK_TIME_NONE])
+
+ clip.props.start = 18
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+ self.assertEqual(self.snapped_at, [20, Gst.CLOCK_TIME_NONE,
+ Gst.CLOCK_TIME_NONE, 20])
+ clip.props.start = 19
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+ self.assertEqual(self.snapped_at, [20, Gst.CLOCK_TIME_NONE,
+ Gst.CLOCK_TIME_NONE, 20])
+
+ def test_rippling_snaps(self):
+ self.timeline.props.auto_transition = True
+ self.append_clip()
+ clip = self.append_clip()
+
+ self.activate_snapping()
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ clip.edit([], 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 15)
+ self.assertEqual(self.snapped_at, [10])
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ clip.edit([], 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 20)
+ self.assertEqual(self.snapped_at, [10, Gst.CLOCK_TIME_NONE])
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+
+ def test_transition_moves_when_rippling_to_another_layer(self):
+ self.timeline.props.auto_transition = True
+ clip1 = self.add_clip(0, 0, 100)
+ clip2 = self.add_clip(50, 0, 100)
+ all_clips = self.layer.get_clips()
+ self.assertEqual(len(all_clips), 4)
+
+ layer2 = self.timeline.append_layer()
+ clip1.edit([], layer2.get_priority(), GES.EditMode.EDIT_RIPPLE,
+ GES.Edge.EDGE_NONE, clip1.props.start)
+ self.assertEqual(self.layer.get_clips(), [])
+ self.assertEqual(set(layer2.get_clips()), set(all_clips))
+
+ def test_transition_rippling_after_next_clip_stays(self):
+ self.timeline.props.auto_transition = True
+ clip1 = self.add_clip(0, 0, 100)
+ clip2 = self.add_clip(50, 0, 100)
+ all_clips = self.layer.get_clips()
+ self.assertEqual(len(all_clips), 4)
+
+ clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE,
+ GES.Edge.EDGE_NONE, clip2.props.start + 1)
+ self.assertEqual(set(self.layer.get_clips()), set(all_clips))
+
+ def test_transition_rippling_over_does_not_create_another_transition(self):
+ self.timeline.props.auto_transition = True
+
+ clip1 = self.add_clip(0, 0, 17 * Gst.SECOND)
+ clip2 = clip1.split(7.0 * Gst.SECOND)
+ # Make a transition between the two clips
+ clip1.edit([], self.layer.get_priority(),
+ GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 4.5 * Gst.SECOND)
+
+ # Rippl clip1 and check that transitions ar always the sames
+ all_clips = self.layer.get_clips()
+ self.assertEqual(len(all_clips), 4)
+ clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE,
+ GES.Edge.EDGE_NONE, 41.5 * Gst.SECOND)
+ self.assertEqual(len(self.layer.get_clips()), 4)
+ clip1.edit([], self.layer.get_priority(),
+ GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 35 * Gst.SECOND)
+ self.assertEqual(len(self.layer.get_clips()), 4)
+
+ def test_trim_transition(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+
+ self.timeline.props.auto_transition = True
+ self.add_clip(0, 0, 10)
+ self.add_clip(5, 0, 10)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TransitionClip, 5, 5),
+ (GES.TestClip, 5, 10),
+ ]
+ ])
+ transition = self.layer.get_clips()[1]
+ self.assertTrue(transition.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 7))
+
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TransitionClip, 7, 3),
+ (GES.TestClip, 7, 8),
+ ]
+ ])
+
+ def test_trim_start(self):
+ clip = self.append_clip()
+ self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_NONE, 0))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ def test_ripple_end(self):
+ clip = self.append_clip()
+ clip.set_max_duration(20)
+ self.append_clip().set_max_duration(10)
+ self.append_clip().set_max_duration(10)
+ self.print_timeline()
+ self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 20))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 20),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ]
+ ])
+
+ self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 15))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 15),
+ (GES.TestClip, 15, 10),
+ (GES.TestClip, 25, 10),
+ ]
+ ])
+
+ def test_move_group_full_overlap(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+
+ for _ in range(4):
+ self.append_clip()
+ clips = self.layer.get_clips()
+
+ self.assertTrue(clips[0].ripple(20))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ (GES.TestClip, 40, 10),
+ (GES.TestClip, 50, 10),
+ ]
+ ])
+ group = GES.Container.group(clips[1:])
+ self.print_timeline()
+ self.assertFalse(group.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
+ self.print_timeline()
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ (GES.TestClip, 40, 10),
+ (GES.TestClip, 50, 10),
+ ]
+ ])
+
+ self.assertFalse(clips[1].edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
+ self.print_timeline()
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ (GES.TestClip, 40, 10),
+ (GES.TestClip, 50, 10),
+ ]
+ ])
+
+ def test_trim_inside_group(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+
+ for _ in range(2):
+ self.append_clip()
+ clips = self.layer.get_clips()
+ group = GES.Container.group(clips)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+ self.assertEqual(group.props.start, 0)
+ self.assertEqual(group.props.duration, 20)
+
+ clips[0].trim(5)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 5, 5),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+ self.assertEqual(group.props.start, 5)
+ self.assertEqual(group.props.duration, 15)
+
+ def test_trim_end_past_max_duration(self):
+ clip = self.append_clip()
+ max_duration = clip.props.duration
+ clip.set_max_duration(max_duration)
+ self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 5))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 5, 5),
+ ]
+ ])
+
+ self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 15))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 5, 5),
+ ]
+ ])
+
+
+class TestInvalidOverlaps(common.GESSimpleTimelineTest):
+
+ def test_adding_or_moving(self):
+ clip1 = self.add_clip(start=10, in_point=0, duration=3)
+ self.assertIsNotNone(clip1)
+
+ def check_add_move_clip(start, duration):
+ self.timeline.props.auto_transition = True
+ self.layer.props.auto_transition = True
+ clip2 = GES.TestClip()
+ clip2.props.start = start
+ clip2.props.duration = duration
+ self.assertFalse(self.layer.add_clip(clip2))
+ self.assertEqual(len(self.layer.get_clips()), 1)
+
+ # Add the clip at a different position.
+ clip2.props.start = 25
+ self.assertTrue(self.layer.add_clip(clip2))
+ self.assertEqual(clip2.props.start, 25)
+
+ # Try to move the second clip by editing it.
+ self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, start))
+ self.assertEqual(clip2.props.start, 25)
+
+ # Try to put it in a group and move the group.
+ clip3 = GES.TestClip()
+ clip3.props.start = 20
+ clip3.props.duration = 1
+ self.assertTrue(self.layer.add_clip(clip3))
+ group = GES.Container.group([clip3, clip2])
+ self.assertTrue(group.props.start, 20)
+ self.assertFalse(group.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, start - 5))
+ self.assertEqual(group.props.start, 20)
+ self.assertEqual(clip3.props.start, 20)
+ self.assertEqual(clip2.props.start, 25)
+
+ for clip in group.ungroup(False):
+ self.assertTrue(self.layer.remove_clip(clip))
+
+ # clip1 contains...
+ check_add_move_clip(start=10, duration=1)
+ check_add_move_clip(start=11, duration=1)
+ check_add_move_clip(start=12, duration=1)
+
+ def test_splitting(self):
+ clip1 = self.add_clip(start=9, in_point=0, duration=3)
+ clip2 = self.add_clip(start=10, in_point=0, duration=4)
+ clip3 = self.add_clip(start=12, in_point=0, duration=3)
+
+ self.assertIsNone(clip1.split(13))
+ self.assertIsNone(clip1.split(8))
+
+ self.assertIsNone(clip3.split(12))
+ self.assertIsNone(clip3.split(15))
+
+ def test_changing_duration(self):
+ clip1 = self.add_clip(start=9, in_point=0, duration=2)
+ clip2 = self.add_clip(start=10, in_point=0, duration=2)
+
+ self.assertFalse(clip1.set_start(10))
+ self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, clip2.props.start + clip2.props.duration))
+ self.assertFalse(clip1.ripple_end(clip2.props.start + clip2.props.duration))
+ self.assertFalse(clip1.roll_end(clip2.props.start + clip2.props.duration))
+
+ # clip2's end edge to the left, to decrease its duration.
+ self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, clip1.props.start + clip1.props.duration))
+ self.assertFalse(clip2.ripple_end(clip1.props.start + clip1.props.duration))
+ self.assertFalse(clip2.roll_end(clip1.props.start + clip1.props.duration))
+
+ # clip2's start edge to the left, to increase its duration.
+ self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, clip1.props.start))
+ self.assertFalse(clip2.trim(clip1.props.start))
+
+ # clip1's start edge to the right, to decrease its duration.
+ self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, clip2.props.start))
+ self.assertFalse(clip1.trim(clip2.props.start))
+
+ def test_rippling_backward(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ self.maxDiff = None
+ for i in range(4):
+ self.append_clip()
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ]
+ ])
+
+ clip = self.layer.get_clips()[2]
+ self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip.props.start - 20))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ]
+ ])
+ self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip.props.start + 10))
+
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 30, 10),
+ (GES.TestClip, 40, 10),
+ ]
+ ])
+
+ self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip.props.start -20))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ (GES.TestClip, 30, 10),
+ (GES.TestClip, 40, 10),
+ ]
+ ])
+
+ def test_rolling(self):
+ clip1 = self.add_clip(start=9, in_point=0, duration=2)
+ clip2 = self.add_clip(start=10, in_point=0, duration=2)
+ clip3 = self.add_clip(start=11, in_point=0, duration=2)
+
+ # Rolling clip1's end -1 would lead to clip3 to overlap 100% with clip2.
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 9, 2),
+ (GES.TestClip, 10, 2),
+ (GES.TestClip, 11, 2)
+ ]
+ ])
+ self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, clip1.props.start + clip1.props.duration - 1))
+ self.assertFalse(clip1.roll_end(13))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 9, 2),
+ (GES.TestClip, 10, 2),
+ (GES.TestClip, 11, 2)
+ ]
+ ])
+
+ # Rolling clip3's start +1 would lead to clip1 to overlap 100% with clip2.
+ self.assertFalse(clip3.edit([], -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 12))
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 9, 2),
+ (GES.TestClip, 10, 2),
+ (GES.TestClip, 11, 2)
+ ]
+ ])
+
+ def test_layers(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ self.maxDiff = None
+ self.timeline.append_layer()
+
+ for i in range(2):
+ self.append_clip()
+ self.append_clip(1)
+
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ],
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ clip = self.layer.get_clips()[0]
+ self.assertFalse(clip.edit([], 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ],
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ def test_rippling(self):
+ self.timeline.remove_track(self.timeline.get_tracks()[0])
+ clip1 = self.add_clip(start=9, in_point=0, duration=2)
+ clip2 = self.add_clip(start=10, in_point=0, duration=2)
+ clip3 = self.add_clip(start=11, in_point=0, duration=2)
+
+ # Rippling clip2's start -2 would bring clip3 exactly on top of clip1.
+ self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 8))
+ self.assertFalse(clip2.ripple(8))
+
+ # Rippling clip1's end -1 would bring clip3 exactly on top of clip2.
+ self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 8))
+ self.assertFalse(clip1.ripple_end(8))
+
+ def test_move_group_to_layer(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ self.append_clip()
+ self.append_clip()
+ self.append_clip()
+
+ clips = self.layer.get_clips()
+
+ clips[1].props.start += 2
+ group = GES.Container.group(clips[1:])
+ self.assertTrue(clips[1].edit([], 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE,
+ group.props.start))
+
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ ],
+ [
+ (GES.TestClip, 12, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+
+ clips[0].props.start = 15
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 15, 10),
+ ],
+ [
+ (GES.TestClip, 12, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+
+ self.assertFalse(clips[1].edit([], 0, GES.EditMode.EDIT_NORMAL,
+ GES.Edge.EDGE_NONE, group.props.start))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 15, 10),
+ ],
+ [
+ (GES.TestClip, 12, 10),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+
+ def test_copy_paste_overlapping(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ clip = self.append_clip()
+
+ copy = clip.copy(True)
+ self.assertIsNone(copy.paste(copy.props.start))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+
+ ]
+ ])
+ copy = clip.copy(True)
+ pasted = copy.paste(copy.props.start + 1)
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 1, 10),
+ ]
+ ])
+
+ pasted.move_to_layer(self.timeline.append_layer())
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ ],
+ [
+ (GES.TestClip, 1, 10),
+ ]
+ ])
+
+ copy = pasted.copy(True)
+ self.assertIsNotNone(copy.paste(pasted.props.start - 1))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ ],
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 1, 10),
+ ],
+ ])
+
+ group = GES.Group.new()
+ group.add(clip)
+
+ copied_group = group.copy(True)
+ self.assertFalse(copied_group.paste(group.props.start))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ ],
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 1, 10),
+ ],
+ ])
+
+ def test_move_group_with_overlaping_clips(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+ self.append_clip()
+ self.append_clip()
+ self.append_clip()
+
+ self.timeline.props.auto_transition = True
+ clips = self.layer.get_clips()
+
+ clips[1].props.start += 5
+ group = GES.Container.group(clips[1:])
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 15, 10),
+ (GES.TransitionClip, 20, 5),
+ (GES.TestClip, 20, 10),
+ ]
+ ])
+
+ clips[0].props.start = 30
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 15, 10),
+ (GES.TransitionClip, 20, 5),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ]
+ ])
+
+ # the 3 clips would overlap
+ self.assertFalse(clips[1].edit([], 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 25))
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 15, 10),
+ (GES.TransitionClip, 20, 5),
+ (GES.TestClip, 20, 10),
+ (GES.TestClip, 30, 10),
+ ]
+ ])
+
+
+class TestSnapping(common.GESSimpleTimelineTest):
+
+ def test_snapping(self):
+ self.timeline.props.auto_transition = True
+ self.timeline.set_snapping_distance(1)
+ clip1 = self.add_clip(0, 0, 100)
+
+ # Split clip1.
+ split_position = 50
+ clip2 = clip1.split(split_position)
+ self.assertEqual(len(self.layer.get_clips()), 2)
+ self.assertEqual(clip1.props.duration, split_position)
+ self.assertEqual(clip2.props.start, split_position)
+
+ # Make sure snapping prevents clip2 to be moved to the left.
+ clip2.edit([], self.layer.get_priority(), GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE,
+ clip2.props.start - 1)
+ self.assertEqual(clip2.props.start, split_position)
+
+ def test_trim_snapps_inside_group(self):
+ self.track_types = [GES.TrackType.AUDIO]
+ super().setUp()
+
+ self.timeline.props.auto_transition = True
+ self.timeline.set_snapping_distance(5)
+
+ snaps = []
+ def snapping_started_cb(timeline, element1, element2, dist, self):
+ snaps.append(set([element1, element2]))
+
+ self.timeline.connect('snapping-started', snapping_started_cb, self)
+ clip = self.append_clip()
+ clip1 = self.append_clip()
+
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+
+ clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 15)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 10, 10),
+ ]
+ ])
+ self.assertEqual(snaps[0], set([clip.get_children(False)[0], clip1.get_children(False)[0]]))
+
+ clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 16)
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 0, 10),
+ (GES.TestClip, 16, 4),
+ ]
+ ])
+
+ def test_trim_no_snapping_on_same_clip(self):
+ self.timeline.props.auto_transition = True
+ self.timeline.set_snapping_distance(1)
+
+ not_called = []
+ def snapping_started_cb(timeline, element1, element2, dist, self):
+ not_called.append("No snapping should happen")
+
+ self.timeline.connect('snapping-started', snapping_started_cb, self)
+ clip = self.append_clip()
+ clip.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 5)
+ self.assertEqual(not_called, [])
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 5, 5),
+ ]
+ ])
+
+ clip.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 4)
+ self.assertEqual(not_called, [])
+ self.assertTimelineTopology([
+ [ # Unique layer
+ (GES.TestClip, 4, 6),
+ ]
+ ])
+
+ def test_no_snapping_on_split(self):
+ self.timeline.props.auto_transition = True
+ self.timeline.set_snapping_distance(1)
+
+ not_called = []
+ def snapping_started_cb(timeline, element1, element2, dist, self):
+ not_called.append("No snapping should happen")
+
+ self.timeline.connect('snapping-started', snapping_started_cb, self)
+ clip1 = self.add_clip(0, 0, 100)
+
+ # Split clip1.
+ split_position = 50
+ clip2 = clip1.split(split_position)
+ self.assertEqual(not_called, [])
+ self.assertEqual(len(self.layer.get_clips()), 2)
+ self.assertEqual(clip1.props.duration, split_position)
+ self.assertEqual(clip2.props.start, split_position)
+
+
+class TestTransitions(common.GESSimpleTimelineTest):
+
+ def test_emission_order_for_transition_clip_added_signal(self):
+ self.timeline.props.auto_transition = True
+ unused_clip1 = self.add_clip(0, 0, 100)
+ clip2 = self.add_clip(100, 0, 100)
+
+ # Connect to signals to track in which order they are emitted.
+ signals = []
+
+ def clip_added_cb(layer, clip):
+ self.assertIsInstance(clip, GES.TransitionClip)
+ signals.append("clip-added")
+ self.layer.connect("clip-added", clip_added_cb)
+
+ def property_changed_cb(clip, pspec):
+ self.assertEqual(clip, clip2)
+ self.assertEqual(pspec.name, "start")
+ signals.append("notify::start")
+ clip2.connect("notify::start", property_changed_cb)
+
+ # Move clip2 to create a transition with clip1.
+ clip2.edit([], self.layer.get_priority(),
+ GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 50)
+ # The clip-added signal is emitted twice, once for the video
+ # transition and once for the audio transition.
+ self.assertEqual(
+ signals, ["notify::start", "clip-added", "clip-added"])
+
+ def create_xges(self):
+ uri = common.get_asset_uri("png.png")
+ return """<ges version='0.4'>
+ <project properties='properties;' metadatas='metadatas, author=(string)"", render-scale=(double)100, format-version=(string)0.4;'>
+ <ressources>
+ <asset id='GESTitleClip' extractable-type-name='GESTitleClip' properties='properties;' metadatas='metadatas;' />
+ <asset id='bar-wipe-lr' extractable-type-name='GESTransitionClip' properties='properties;' metadatas='metadatas, description=(string)GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR;' />
+ <asset id='%(uri)s' extractable-type-name='GESUriClip' properties='properties, supported-formats=(int)4, duration=(guint64)18446744073709551615;' metadatas='metadatas, video-codec=(string)PNG, file-size=(guint64)73294;' />
+ <asset id='crossfade' extractable-type-name='GESTransitionClip' properties='properties;' metadatas='metadatas, description=(string)GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE;' />
+ </ressources>
+ <timeline properties='properties, auto-transition=(boolean)true, snapping-distance=(guint64)31710871;' metadatas='metadatas, duration=(guint64)13929667032;'>
+ <track caps='video/x-raw(ANY)' track-type='4' track-id='0' properties='properties, async-handling=(boolean)false, message-forward=(boolean)true, caps=(string)"video/x-raw\(ANY\)", restriction-caps=(string)"video/x-raw\,\ width\=\(int\)1920\,\ height\=\(int\)1080\,\ framerate\=\(fraction\)30/1", mixing=(boolean)true;' metadatas='metadatas;'/>
+ <track caps='audio/x-raw(ANY)' track-type='2' track-id='1' properties='properties, async-handling=(boolean)false, message-forward=(boolean)true, caps=(string)"audio/x-raw\(ANY\)", restriction-caps=(string)"audio/x-raw\,\ format\=\(string\)S32LE\,\ channels\=\(int\)2\,\ rate\=\(int\)44100\,\ layout\=\(string\)interleaved", mixing=(boolean)true;' metadatas='metadatas;'/>
+ <layer priority='0' properties='properties, auto-transition=(boolean)true;' metadatas='metadatas, volume=(float)1;'>
+ <clip id='0' asset-id='%(uri)s' type-name='GESUriClip' layer-priority='0' track-types='6' start='0' duration='4558919302' inpoint='0' rate='0' properties='properties, name=(string)uriclip25263, mute=(boolean)false, is-image=(boolean)false;' />
+ <clip id='1' asset-id='bar-wipe-lr' type-name='GESTransitionClip' layer-priority='0' track-types='4' start='3225722559' duration='1333196743' inpoint='0' rate='0' properties='properties, name=(string)transitionclip84;' children-properties='properties, GESVideoTransition::border=(uint)0, GESVideoTransition::invert=(boolean)false;'/>
+ <clip id='2' asset-id='%(uri)s' type-name='GESUriClip' layer-priority='0' track-types='6' start='3225722559' duration='3479110239' inpoint='4558919302' rate='0' properties='properties, name=(string)uriclip25265, mute=(boolean)false, is-image=(boolean)false;' />
+ </layer>
+ <layer priority='1' properties='properties, auto-transition=(boolean)true;' metadatas='metadatas, volume=(float)1;'>
+ <clip id='3' asset-id='%(uri)s' type-name='GESUriClip' layer-priority='1' track-types='4' start='8566459322' duration='1684610449' inpoint='0' rate='0' properties='properties, name=(string)uriclip25266, mute=(boolean)false, is-image=(boolean)true;' />
+ <clip id='4' asset-id='GESTitleClip' type-name='GESTitleClip' layer-priority='1' track-types='6' start='8566459322' duration='4500940746' inpoint='0' rate='0' properties='properties, name=(string)titleclip69;' />
+ <clip id='5' asset-id='%(uri)s' type-name='GESUriClip' layer-priority='1' track-types='4' start='9566459322' duration='4363207710' inpoint='0' rate='0' properties='properties, name=(string)uriclip25275, mute=(boolean)false, is-image=(boolean)true;' />
+ </layer>
+ <groups>
+ </groups>
+ </timeline>
+</project>
+</ges>""" % {"uri": uri}
+
+ def test_auto_transition(self):
+ xges = self.create_xges()
+ with common.created_project_file(xges) as proj_uri:
+ project = GES.Project.new(proj_uri)
+ timeline = project.extract()
+
+ mainloop = common.create_main_loop()
+ mainloop.run(until_empty=True)
+
+ layers = timeline.get_layers()
+ self.assertEqual(len(layers), 2)
+
+ self.assertTrue(layers[0].props.auto_transition)
+ self.assertTrue(layers[1].props.auto_transition)
+
+ def test_transition_type(self):
+ xges = self.create_xges()
+ with common.created_project_file(xges) as proj_uri:
+ project = GES.Project.new(proj_uri)
+ timeline = project.extract()
+
+ mainloop = common.create_main_loop()
+ mainloop.run(until_empty=True)
+
+ layers = timeline.get_layers()
+ self.assertEqual(len(layers), 2)
+
+ clips = layers[0].get_clips()
+ clip1 = clips[0]
+ clip2 = clips[-1]
+ # There should be a transition because clip1 intersects clip2
+ self.assertLess(clip1.props.start, clip2.props.start)
+ self.assertLess(clip2.props.start, clip1.props.start + clip1.props.duration)
+ self.assertLess(clip1.props.start + clip1.props.duration, clip2.props.start + clip2.props.duration)
+ self.assertEqual(len(clips), 3)
+
+ # 3 clips would be overlapping, 1 of them wasn't added!
+ clips = layers[1].get_clips()
+ self.assertEqual(len(clips), 3)
+
+
+class TestPriorities(common.GESSimpleTimelineTest):
+
+ def test_clips_priorities(self):
+ clip = self.add_clip(0, 0, 100)
+ clip1 = self.add_clip(100, 0, 100)
+ self.timeline.commit()
+
+ self.assertLess(clip.props.priority, clip1.props.priority)
+
+ clip.props.start = 101
+ self.timeline.commit()
+ self.assertGreater(clip.props.priority, clip1.props.priority)
+
+
+class TestTimelineElement(common.GESSimpleTimelineTest):
+
+ def test_set_child_property(self):
+ clip = self.add_clip(0, 0, 100)
+ source = clip.find_track_element(None, GES.VideoSource)
+ self.assertTrue(source.set_child_property("height", 5))
+ self.assertEqual(clip.get_child_property("height"), (True, 5))
--- /dev/null
+# FIXME: make check work on windows
+if host_machine.system() != 'windows' and gstcheck_dep.found()
+ subdir('check')
+endif
+
+subdir('validate')
+subdir('benchmarks')
\ No newline at end of file
--- /dev/null
+SUBDIRS = scenarios
+
+validatedir=${libdir}/gst-validate-launcher/python/launcher/apps/
+
+validate_DATA = geslaunch.py
+
+EXTRA_DIST = geslaunch.py
--- /dev/null
+#!/usr/bin/env python2
+#
+# Copyright (c) 2013,Thibault Saunier <thibault.saunier@collabora.com>
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+import os
+import sys
+import urllib.parse
+import subprocess
+from launcher import utils
+from urllib.parse import unquote
+import xml.etree.ElementTree as ET
+from launcher.baseclasses import GstValidateTest, TestsManager, ScenarioManager, MediaFormatCombination, \
+ MediaDescriptor, GstValidateEncodingTestInterface
+
+GES_DURATION_TOLERANCE = utils.GST_SECOND / 2
+
+GES_LAUNCH_COMMAND = "ges-launch-1.0"
+if "win32" in sys.platform:
+ GES_LAUNCH_COMMAND += ".exe"
+
+
+GES_ENCODING_TARGET_COMBINATIONS = [
+ MediaFormatCombination("ogg", "vorbis", "theora"),
+ MediaFormatCombination("ogg", "opus", "theora"),
+ MediaFormatCombination("webm", "vorbis", "vp8"),
+ MediaFormatCombination("webm", "opus", "vp8"),
+ MediaFormatCombination("mp4", "aac", "h264"),
+ MediaFormatCombination("mp4", "ac3", "h264"),
+ MediaFormatCombination("quicktime", "aac", "jpeg"),
+ MediaFormatCombination("mkv", "opus", "h264"),
+ MediaFormatCombination("mkv", "vorbis", "h264"),
+ MediaFormatCombination("mkv", "opus", "jpeg"),
+ MediaFormatCombination("mkv", "vorbis", "jpeg")
+]
+
+
+def quote_uri(uri):
+ """
+ Encode a URI/path according to RFC 2396, without touching the file:/// part.
+ """
+ # Split off the "file:///" part, if present.
+ parts = urllib.parse.urlsplit(uri, allow_fragments=False)
+ # Make absolutely sure the string is unquoted before quoting again!
+ raw_path = unquote(parts.path)
+ return utils.path2url(raw_path)
+
+
+class XgesProjectDescriptor(MediaDescriptor):
+ def __init__(self, uri):
+ super(XgesProjectDescriptor, self).__init__()
+
+ self._uri = uri
+ self._xml_path = utils.url2path(uri)
+ self._root = ET.parse(self._xml_path)
+ self._duration = None
+
+ def get_media_filepath(self):
+ return self._xml_path
+
+ def get_path(self):
+ return self._xml_path
+
+ def get_caps(self):
+ raise NotImplemented
+
+ def get_uri(self):
+ return self._uri
+
+ def get_duration(self):
+ if self._duration:
+ return self._duration
+
+ for l in self._root.iter():
+ if l.tag == "timeline":
+ self._duration=int(l.attrib['metadatas'].split("duration=(guint64)")[1].split(" ")[0].split(";")[0])
+ break
+
+ if not self._duration:
+ self.error("%s does not have duration! (setting 2mins)" % self._uri)
+ self._duration = 2 * 60
+
+ return self._duration
+
+ def get_protocol(self):
+ return Protocols.FILE
+
+ def is_seekable(self):
+ return True
+
+ def is_image(self):
+ return False
+
+ def get_num_tracks(self, track_type):
+ num_tracks = 0
+ for l in self._root.iter():
+ if l.tag == "track":
+ if track_type in l.attrib["caps"]:
+ num_tracks += 1
+ return num_tracks
+
+
+class GESTest(GstValidateTest):
+ def __init__(self, classname, options, reporter, project, scenario=None,
+ combination=None, expected_failures=None):
+
+ super(GESTest, self).__init__(GES_LAUNCH_COMMAND, classname, options, reporter,
+ scenario=scenario)
+
+ self.project = project
+
+ def set_sample_paths(self):
+ if not self.options.paths:
+ if self.options.disable_recurse:
+ return
+ if self.project:
+ paths = [os.path.dirname(self.project.get_media_filepath())]
+ else:
+ paths = []
+ else:
+ paths = self.options.paths
+
+ if not isinstance(paths, list):
+ paths = [paths]
+
+ for path in paths:
+ # We always want paths separator to be cut with '/' for ges-launch
+ path = path.replace("\\", "/")
+ if not self.options.disable_recurse:
+ self.add_arguments("--ges-sample-path-recurse", quote_uri(path))
+ else:
+ self.add_arguments("--ges-sample-paths", quote_uri(path))
+
+ def build_arguments(self):
+ GstValidateTest.build_arguments(self)
+
+ if self.options.mute:
+ self.add_arguments("--mute")
+
+ self.set_sample_paths()
+
+ if self.project:
+ self.add_arguments("-l", self.project.get_uri())
+
+
+class GESPlaybackTest(GESTest):
+ def __init__(self, classname, options, reporter, project, scenario):
+ super(GESPlaybackTest, self).__init__(classname, options, reporter,
+ project, scenario=scenario)
+
+ def get_current_value(self):
+ return self.get_current_position()
+
+class GESScenarioTest(GESTest):
+ def __init__(self, classname, options, reporter, scenario):
+ super().__init__(classname, options, reporter, None, scenario=scenario)
+
+ def build_arguments(self):
+ super().build_arguments()
+ self.add_arguments("--set-scenario", self.scenario.path)
+
+ def get_subproc_env(self):
+ scenario = self.scenario
+ self.scenario = None
+ res = super().get_subproc_env()
+ self.scenario = scenario
+
+ return res
+
+ def get_current_value(self):
+ return self.get_current_position()
+
+
+class GESRenderTest(GESTest, GstValidateEncodingTestInterface):
+ def __init__(self, classname, options, reporter, project, combination):
+ GESTest.__init__(self, classname, options, reporter, project)
+
+ GstValidateEncodingTestInterface.__init__(self, combination, self.project)
+
+ def build_arguments(self):
+ GESTest.build_arguments(self)
+ self._set_rendering_info()
+
+ def run_external_checks(self):
+ reference_file_path = urllib.parse.urlsplit(self.media_descriptor.get_uri()).path + ".expected_result"
+ if os.path.exists(reference_file_path):
+ self.run_iqa_test(utils.path2url(reference_file_path))
+
+ def _set_rendering_info(self):
+ self.dest_file = path = os.path.join(self.options.dest,
+ self.classname.replace(".render.", os.sep).
+ replace(".", os.sep))
+ utils.mkdir(os.path.dirname(urllib.parse.urlsplit(self.dest_file).path))
+ if not utils.isuri(self.dest_file):
+ self.dest_file = utils.path2url(self.dest_file)
+
+ profile = self.get_profile()
+ self.add_arguments("-f", profile, "-o", self.dest_file)
+
+ def check_results(self):
+ if self.result in [Result.PASSED, Result.NOT_RUN] and self.scenario is None:
+ if self.process.returncode != 0:
+ return super().check_results()
+
+ res, msg = self.check_encoded_file()
+ self.set_result(res, msg)
+ else:
+ if self.result == utils.Result.TIMEOUT:
+ missing_eos = False
+ try:
+ if utils.get_duration(self.dest_file) == self.project.get_duration():
+ missing_eos = True
+ except Exception as e:
+ pass
+
+ if missing_eos is True:
+ self.set_result(utils.Result.TIMEOUT, "The rendered file had right duration, MISSING EOS?\n",
+ "failure")
+ else:
+ GstValidateTest.check_results(self)
+
+ def get_current_value(self):
+ size = self.get_current_size()
+ if size is None:
+ return self.get_current_position()
+
+ return size
+
+
+class GESTestsManager(TestsManager):
+ name = "ges"
+
+ _scenarios = ScenarioManager()
+
+ def __init__(self):
+ super(GESTestsManager, self).__init__()
+
+ def init(self):
+ try:
+ if "--set-scenario=" in subprocess.check_output([GES_LAUNCH_COMMAND, "--help"]).decode():
+
+ return True
+ else:
+ self.warning("Can not use ges-launch, it seems not to be compiled against"
+ " gst-validate")
+ except subprocess.CalledProcessError as e:
+ self.warning("Can not use ges-launch: %s" % e)
+ except OSError as e:
+ self.warning("Can not use ges-launch: %s" % e)
+
+ def add_options(self, parser):
+ group = parser.add_argument_group("GStreamer Editing Services specific option"
+ " and behaviours",
+ description="""
+The GStreamer Editing Services launcher will be usable only if GES has been compiled against GstValidate
+You can simply run scenarios specifying project as args. For example the following will run all available
+and activated scenarios on project.xges:
+
+ $gst-validate-launcher ges /some/ges/project.xges
+
+
+Available options:""")
+ group.add_argument("-P", "--projects-paths", dest="projects_paths",
+ default=os.path.join(utils.DEFAULT_GST_QA_ASSETS,
+ "ges",
+ "ges-projects"),
+ help="Paths in which to look for moved medias")
+ group.add_argument("--ges-scenario-paths", dest="scenarios_path",
+ default=None,
+ help="Paths in which to look for moved medias")
+ group.add_argument("-r", "--disable-recurse-paths", dest="disable_recurse",
+ default=False, action="store_true",
+ help="Whether to recurse into paths to find medias")
+
+ def set_settings(self, options, args, reporter):
+ TestsManager.set_settings(self, options, args, reporter)
+ self._scenarios.config = self.options
+
+ try:
+ os.makedirs(utils.url2path(options.dest)[0])
+ except OSError:
+ pass
+
+ def list_tests(self):
+ return self.tests
+
+ def register_defaults(self, project_paths=None, scenarios_path=None):
+ projects = list()
+ all_scenarios = list()
+ if not self.args:
+ if project_paths == None:
+ path = self.options.projects_paths
+ else:
+ path = project_paths
+
+ for root, dirs, files in os.walk(path):
+ for f in files:
+ if not f.endswith(".xges"):
+ continue
+ projects.append(utils.path2url(os.path.join(path, root, f)))
+
+ if self.options.scenarios_path:
+ scenarios_path = self.options.scenarios_path
+
+ if scenarios_path:
+ for root, dirs, files in os.walk(scenarios_path):
+ for f in files:
+ if not f.endswith(".scenario"):
+ continue
+ all_scenarios.append(os.path.join(root, f))
+ else:
+ for proj_uri in self.args:
+ if not utils.isuri(proj_uri):
+ proj_uri = utils.path2url(proj_uri)
+
+ if os.path.exists(proj_uri):
+ projects.append(proj_uri)
+
+ if self.options.long_limit != 0:
+ scenarios = ["none",
+ "scrub_forward_seeking",
+ "scrub_backward_seeking"]
+ else:
+ scenarios = ["play_15s",
+ "scrub_forward_seeking_full",
+ "scrub_backward_seeking_full"]
+ for proj_uri in projects:
+ # First playback casses
+ project = XgesProjectDescriptor(proj_uri)
+ for scenario_name in scenarios:
+ scenario = self._scenarios.get_scenario(scenario_name)
+ if scenario is None:
+ continue
+
+ if scenario.get_min_media_duration() >= (project.get_duration() / utils.GST_SECOND):
+ continue
+
+ classname = "playback.%s.%s" % (scenario.name,
+ os.path.basename(proj_uri).replace(".xges", ""))
+ self.add_test(GESPlaybackTest(classname,
+ self.options,
+ self.reporter,
+ project,
+ scenario=scenario)
+ )
+
+ # And now rendering casses
+ for comb in GES_ENCODING_TARGET_COMBINATIONS:
+ classname = "render.%s.%s" % (str(comb).replace(' ', '_'),
+ os.path.splitext(os.path.basename(proj_uri))[0])
+ self.add_test(GESRenderTest(classname, self.options,
+ self.reporter, project,
+ combination=comb)
+ )
+ if all_scenarios:
+ for scenario in self._scenarios.discover_scenarios(all_scenarios):
+ classname = "scenario.%s" % scenario.name
+ self.add_test(GESScenarioTest(classname,
+ self.options,
+ self.reporter,
+ scenario=scenario))
\ No newline at end of file
--- /dev/null
+subdir ('scenarios')
+
+install_data (['geslaunch.py'],
+ install_dir : join_paths(get_option('libdir'),
+ 'gst-validate-launcher', 'python', 'launcher', 'apps'))
--- /dev/null
+scenariosdir=${datadir}/gstreamer-$(GST_API_VERSION)/validate/scenarios
+
+scenarios_DATA = ges-edit-clip-while-paused.scenario
+
+EXTRA_DIST = ges-edit-clip-while-paused.scenario
--- /dev/null
+description, duration=4.0
+pause, playback_time=0.0
+wait, duration=1.0
+edit-clip, playback_time=0.0, clip-name="uriclip0", position=3.0, edit-mode="edit_trim", edge="edge_end"
+play, playback_time=0.0
--- /dev/null
+install_data (['ges-edit-clip-while-paused.scenario'],
+ install_dir : join_paths(get_option('datadir'),
+ 'gstreamer-1.0', 'validate', 'scenarios'))
--- /dev/null
+bin_PROGRAMS = ges-launch-@GST_API_VERSION@
+
+AM_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS) $(GST_VALIDATE_CFLAGS)
+LDADD = $(top_builddir)/ges/libges-@GST_API_VERSION@.la $(GST_PBUTILS_LIBS) $(GST_LIBS) $(GIO_LIBS) $(GST_VALIDATE_LIBS)
+
+noinst_HEADERS = ges-validate.h ges-launcher.h utils.h
+
+ges_launch_@GST_API_VERSION@_SOURCES = ges-validate.c ges-launch.c ges-launcher.c utils.c
+
+man_MANS = ges-launch-1.0.1
+
+EXTRA_DIST = ges-launch-1.0.1
--- /dev/null
+.TH GES\-LAUNCH\-1.0 "1" "December 2016" "GStreamer Editing Services API version 1.0" "User Commands"
+.SH NAME
+ges\-launch\-1.0 \- create and render multimedia timelines
+.SH "SYNOPSIS"
+\fBges\-launch\-1.0\fR \fI[OPTION...]\fR
+.SH DESCRIPTION
+ges\-launch\-1.0 creates a multimedia timeline and plays it back,
+or renders it to the specified format.
+.PP
+It can load a timeline from an existing project, or create one
+from the specified commands.
+.PP
+You can learn more about individual ges\-launch\-1.0 commands with
+"ges\-launch\-1.0 help command".
+.PP
+By default, ges\-launch\-1.0 is in "playback\-mode".
+.SH "OPTIONS"
+For the full list of \fIges\-launch\-1.0\fP options see the command line help:
+.TP 8
+\fB\-h\fR, \fB\-\-help\fR
+Show help options
+.TP 8
+\fB\-\-help\-all\fR
+Show all help options
+.TP 8
+\fB\-\-help\-gst\fR
+Show GStreamer Options
+.TP 8
+\fB\-\-help\-GES\fR
+Show GES Options
+.TP 8
+\fB\-\-help\-project\fR
+Show project\-related options
+.TP 8
+\fB\-\-help\-rendering\fR
+Show rendering options
+.TP 8
+\fB\-\-help\-playback\fR
+Show playback options
+.TP 8
+\fB\-\-help\-informative\fR
+Show informative options
+.SH "SEE ALSO"
+For more detailed info and some examples of use, also check out the on-line documentation:
+.br
+https://gstreamer.freedesktop.org/documentation/tools/ges-launch.html
+.SH "AUTHOR"
+Antonio Ospite https://ao2.it
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <locale.h> /* for LC_ALL */
+#include "ges-launcher.h"
+
+static void
+_print_all_commands (gint nargs, gchar ** commands)
+{
+ gchar *help;
+
+ if (nargs == 0)
+ g_print ("Available ges-launch-1.0 commands:\n\n");
+
+ help = ges_command_line_formatter_get_help (nargs, commands);
+
+ g_print ("%s", help);
+
+ g_free (help);
+}
+
+static void
+_check_command_help (int argc, gchar ** argv)
+{
+/**
+ * gchar *page = NULL;
+ *
+ * if (argc == 2)
+ * page = g_strdup ("ges-launch-1.0");
+ * else if (!g_strcmp0 (argv[2], "all"))
+ */
+
+ if (!g_strcmp0 (argv[1], "help")) {
+ gint nargs = 0;
+ gchar **commands = NULL;
+
+ if (argc > 2) {
+ nargs = argc - 2;
+ commands = &argv[2];
+ }
+
+ _print_all_commands (nargs, commands);
+ exit (0);
+ }
+
+/* else
+ * page = g_strconcat ("ges-launch-1.0", "-", argv[2], NULL);
+ *
+ * if (page) {
+ * execlp ("man", "man", page, NULL);
+ * g_free (page);
+ * }
+ *
+ * an error is raised by execlp it will be displayed in the terminal
+ * exit (0);
+ * }
+ */
+}
+
+int
+main (int argc, gchar ** argv)
+{
+ GESLauncher *launcher;
+ gint ret;
+
+ _check_command_help (argc, argv);
+ setlocale (LC_ALL, "");
+
+ launcher = ges_launcher_new ();
+
+ ret = g_application_run (G_APPLICATION (launcher), argc, argv);
+
+ if (!ret)
+ ret = ges_launcher_get_exit_status (launcher);
+
+ g_object_unref (launcher);
+ gst_deinit ();
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.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 <glib.h>
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef G_OS_UNIX
+#include <glib-unix.h>
+#endif
+#include "ges-launcher.h"
+#include "ges-validate.h"
+#include "utils.h"
+
+typedef struct
+{
+ gboolean mute;
+ gboolean disable_mixing;
+ gchar *save_path;
+ gchar *save_only_path;
+ gchar *load_path;
+ GESTrackType track_types;
+ gboolean needs_set_state;
+ gboolean smartrender;
+ gchar *scenario;
+ gchar *format;
+ gchar *outputuri;
+ gchar *encoding_profile;
+ gchar *videosink;
+ gchar *audiosink;
+ gboolean list_transitions;
+ gboolean inspect_action_type;
+ gchar *sanitized_timeline;
+ const gchar *video_track_caps;
+ const gchar *audio_track_caps;
+} ParsedOptions;
+
+struct _GESLauncherPrivate
+{
+ GESTimeline *timeline;
+ GESPipeline *pipeline;
+ gboolean seenerrors;
+#ifdef G_OS_UNIX
+ guint signal_watch_id;
+#endif
+ ParsedOptions parsed_options;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GESLauncher, ges_launcher, G_TYPE_APPLICATION);
+
+static const gchar *HELP_SUMMARY =
+ "ges-launch-1.0 creates a multimedia timeline and plays it back,\n"
+ " or renders it to the specified format.\n\n"
+ " It can load a timeline from an existing project, or create one\n"
+ " from the specified commands.\n\n"
+ " Updating an existing project can be done through --set-scenario\n"
+ " if ges-launch-1.0 has been compiled with gst-validate, see\n"
+ " ges-launch-1.0 --inspect-action-type for the available commands.\n\n"
+ " You can learn more about individual ges-launch-1.0 commands with\n"
+ " \"ges-launch-1.0 help command\".\n\n"
+ " By default, ges-launch-1.0 is in \"playback-mode\".";
+
+static gboolean
+_parse_track_type (const gchar * option_name, const gchar * value,
+ GESLauncher * self, GError ** error)
+{
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ opts->track_types = get_flags_from_string (GES_TYPE_TRACK_TYPE, value);
+
+ if (opts->track_types == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_track_restriction_caps (GESTrack * track, const gchar * caps_str)
+{
+ GstCaps *caps;
+
+ if (!caps_str)
+ return TRUE;
+
+ caps = gst_caps_from_string (caps_str);
+
+ if (!caps) {
+ g_printerr ("Could not create caps for %s from: %s",
+ G_OBJECT_TYPE_NAME (track), caps_str);
+
+ return FALSE;
+ }
+
+ ges_track_set_restriction_caps (track, caps);
+
+ gst_caps_unref (caps);
+ return TRUE;
+}
+
+static gboolean
+_timeline_set_user_options (GESLauncher * self, GESTimeline * timeline,
+ const gchar * load_path)
+{
+ GList *tmp;
+ GESTrack *tracka, *trackv;
+ gboolean has_audio = FALSE, has_video = FALSE;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+retry:
+ for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
+
+ if (GES_TRACK (tmp->data)->type == GES_TRACK_TYPE_VIDEO)
+ has_video = TRUE;
+ else if (GES_TRACK (tmp->data)->type == GES_TRACK_TYPE_AUDIO)
+ has_audio = TRUE;
+
+ if (opts->disable_mixing)
+ ges_track_set_mixing (tmp->data, FALSE);
+
+ if (!(GES_TRACK (tmp->data)->type & opts->track_types)) {
+ ges_timeline_remove_track (timeline, tmp->data);
+ goto retry;
+ }
+ }
+
+ if (opts->scenario && !load_path) {
+ if (!has_video && opts->track_types & GES_TRACK_TYPE_VIDEO) {
+ trackv = GES_TRACK (ges_video_track_new ());
+
+ if (!_set_track_restriction_caps (trackv, opts->video_track_caps))
+ return FALSE;
+
+ if (opts->disable_mixing)
+ ges_track_set_mixing (trackv, FALSE);
+
+ if (!(ges_timeline_add_track (timeline, trackv)))
+ return FALSE;
+ }
+
+ if (!has_audio && opts->track_types & GES_TRACK_TYPE_AUDIO) {
+ tracka = GES_TRACK (ges_audio_track_new ());
+
+ if (!_set_track_restriction_caps (tracka, opts->audio_track_caps))
+ return FALSE;
+
+ if (opts->disable_mixing)
+ ges_track_set_mixing (tracka, FALSE);
+
+ if (!(ges_timeline_add_track (timeline, tracka)))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_project_loaded_cb (GESProject * project, GESTimeline * timeline,
+ GESLauncher * self)
+{
+ gchar *project_uri = NULL;
+ ParsedOptions *opts = &self->priv->parsed_options;
+ GST_INFO ("Project loaded, playing it");
+
+ if (opts->save_path) {
+ gchar *uri;
+ GError *error = NULL;
+
+ if (g_strcmp0 (opts->save_path, "+r") == 0) {
+ uri = ges_project_get_uri (project);
+ } else if (!(uri = ensure_uri (opts->save_path))) {
+ g_error ("couldn't create uri for '%s", opts->save_path);
+
+ self->priv->seenerrors = TRUE;
+ g_application_quit (G_APPLICATION (self));
+ }
+
+ g_print ("\nSaving project to %s\n", uri);
+ ges_project_save (project, timeline, uri, NULL, TRUE, &error);
+ g_free (uri);
+
+ g_assert_no_error (error);
+ if (error) {
+ self->priv->seenerrors = TRUE;
+ g_error_free (error);
+ g_application_quit (G_APPLICATION (self));
+ }
+ }
+
+ project_uri = ges_project_get_uri (project);
+ _timeline_set_user_options (self, timeline, project_uri);
+
+ if (self->priv->parsed_options.load_path && project_uri
+ && ges_validate_activate (GST_PIPELINE (self->priv->pipeline),
+ opts->scenario, &opts->needs_set_state) == FALSE) {
+ g_error ("Could not activate scenario %s", opts->scenario);
+ self->priv->seenerrors = TRUE;
+ g_application_quit (G_APPLICATION (self));
+ }
+
+ g_free (project_uri);
+
+ if (!self->priv->seenerrors && opts->needs_set_state &&
+ gst_element_set_state (GST_ELEMENT (self->priv->pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
+ g_error ("Failed to start the pipeline\n");
+ }
+}
+
+static void
+_error_loading_asset_cb (GESProject * project, GError * error,
+ const gchar * failed_id, GType extractable_type, GESLauncher * self)
+{
+ g_printerr ("Error loading asset %s: %s\n", failed_id, error->message);
+ self->priv->seenerrors = TRUE;
+
+ g_application_quit (G_APPLICATION (self));
+}
+
+static gboolean
+_create_timeline (GESLauncher * self, const gchar * serialized_timeline,
+ const gchar * proj_uri, const gchar * scenario)
+{
+ GESProject *project;
+
+ GError *error = NULL;
+
+ if (proj_uri != NULL) {
+ project = ges_project_new (proj_uri);
+ } else if (scenario == NULL) {
+ GST_INFO ("serialized timeline is %s", serialized_timeline);
+ project = ges_project_new (serialized_timeline);
+ } else {
+ project = ges_project_new (NULL);
+ }
+
+ g_signal_connect (project, "error-loading-asset",
+ G_CALLBACK (_error_loading_asset_cb), self);
+ g_signal_connect (project, "loaded", G_CALLBACK (_project_loaded_cb), self);
+
+ self->priv->timeline =
+ GES_TIMELINE (ges_asset_extract (GES_ASSET (project), &error));
+
+ if (error) {
+ g_printerr ("\nERROR: Could not create timeline because: %s\n\n",
+ error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef void (*sinkSettingFunction) (GESPipeline * pipeline,
+ GstElement * element);
+
+static gboolean
+_set_sink (GESLauncher * self, const gchar * sink_desc,
+ sinkSettingFunction set_func)
+{
+ if (sink_desc != NULL) {
+ GError *err = NULL;
+ GstElement *sink = gst_parse_bin_from_description (sink_desc, TRUE, &err);
+ if (sink == NULL) {
+ GST_ERROR ("could not create the requested videosink %s (err: %s), "
+ "exiting", err ? err->message : "", sink_desc);
+ if (err)
+ g_error_free (err);
+ return FALSE;
+ }
+ set_func (self->priv->pipeline, sink);
+ }
+ return TRUE;
+}
+
+static gboolean
+_set_playback_details (GESLauncher * self)
+{
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ if (!_set_sink (self, opts->videosink, ges_pipeline_preview_set_video_sink) ||
+ !_set_sink (self, opts->audiosink, ges_pipeline_preview_set_audio_sink))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+bus_message_cb (GstBus * bus, GstMessage * message, GESLauncher * self)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_WARNING:{
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->priv->pipeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch.warning");
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *err = NULL;
+ gchar *dbg_info = NULL;
+
+ gst_message_parse_error (message, &err, &dbg_info);
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->priv->pipeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch-error");
+ g_printerr ("ERROR from element %s: %s\n", GST_OBJECT_NAME (message->src),
+ err->message);
+ g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
+ g_clear_error (&err);
+ g_free (dbg_info);
+ self->priv->seenerrors = TRUE;
+ g_application_quit (G_APPLICATION (self));
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ g_printerr ("\nDone\n");
+ g_application_quit (G_APPLICATION (self));
+ break;
+ case GST_MESSAGE_STATE_CHANGED:
+ if (GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (self->priv->pipeline)) {
+ gchar *dump_name;
+ GstState old, new, pending;
+ gchar *state_transition_name;
+
+ gst_message_parse_state_changed (message, &old, &new, &pending);
+ state_transition_name = g_strdup_printf ("%s_%s",
+ gst_element_state_get_name (old), gst_element_state_get_name (new));
+ dump_name = g_strconcat ("ges-launch.", state_transition_name, NULL);
+
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->priv->pipeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
+
+ g_free (dump_name);
+ g_free (state_transition_name);
+ }
+ break;
+ case GST_MESSAGE_REQUEST_STATE:
+ ges_validate_handle_request_state_change (message, G_APPLICATION (self));
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef G_OS_UNIX
+static gboolean
+intr_handler (GESLauncher * self)
+{
+ g_print ("interrupt received.\n");
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->priv->pipeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch.interupted");
+
+ g_application_quit (G_APPLICATION (self));
+
+ /* remove signal handler */
+ return TRUE;
+}
+#endif /* G_OS_UNIX */
+
+static gboolean
+_save_timeline (GESLauncher * self)
+{
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ if (opts->save_only_path) {
+ gchar *uri;
+
+ if (!(uri = ensure_uri (opts->save_only_path))) {
+ g_error ("couldn't create uri for '%s", opts->save_only_path);
+ return FALSE;
+ }
+
+ return ges_timeline_save_to_uri (self->priv->timeline, uri, NULL, TRUE,
+ NULL);
+ }
+
+ if (opts->save_path && !opts->load_path) {
+ gchar *uri;
+ if (!(uri = ensure_uri (opts->save_path))) {
+ g_error ("couldn't create uri for '%s", opts->save_path);
+ return FALSE;
+ }
+
+ return ges_timeline_save_to_uri (self->priv->timeline, uri, NULL, TRUE,
+ NULL);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_run_pipeline (GESLauncher * self)
+{
+ GstBus *bus;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ if (!opts->load_path) {
+ if (ges_validate_activate (GST_PIPELINE (self->priv->pipeline),
+ opts->scenario, &opts->needs_set_state) == FALSE) {
+ g_error ("Could not activate scenario %s", opts->scenario);
+ return FALSE;
+ }
+
+ if (!_timeline_set_user_options (self, self->priv->timeline, NULL)) {
+ g_printerr ("Could not properly set tracks\n");
+ return FALSE;
+ }
+ }
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), self);
+
+ if (!opts->load_path) {
+ if (opts->needs_set_state
+ && gst_element_set_state (GST_ELEMENT (self->priv->pipeline),
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
+ g_error ("Failed to start the pipeline\n");
+ return FALSE;
+ }
+ }
+ g_application_hold (G_APPLICATION (self));
+
+ return TRUE;
+}
+
+static gboolean
+_set_rendering_details (GESLauncher * self)
+{
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ /* Setup profile/encoding if needed */
+ if (opts->smartrender || opts->outputuri) {
+ GstEncodingProfile *prof = NULL;
+
+ if (!opts->format) {
+ GESProject *proj =
+ GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (self->
+ priv->timeline)));
+ const GList *profiles = ges_project_list_encoding_profiles (proj);
+
+ if (profiles) {
+ prof = profiles->data;
+ if (opts->encoding_profile)
+ for (; profiles; profiles = profiles->next)
+ if (g_strcmp0 (opts->encoding_profile,
+ gst_encoding_profile_get_name (profiles->data)) == 0)
+ prof = profiles->data;
+ }
+ }
+
+ if (!prof) {
+ if (opts->format == NULL)
+ opts->format =
+ g_strdup ("application/ogg:video/x-theora:audio/x-vorbis");
+
+ prof = parse_encoding_profile (opts->format);
+ }
+
+ if (opts->outputuri)
+ opts->outputuri = ensure_uri (opts->outputuri);
+
+ if (!prof
+ || !ges_pipeline_set_render_settings (self->priv->pipeline,
+ opts->outputuri, prof)
+ || !ges_pipeline_set_mode (self->priv->pipeline,
+ opts->smartrender ? GES_PIPELINE_MODE_SMART_RENDER :
+ GES_PIPELINE_MODE_RENDER)) {
+ return FALSE;
+ }
+
+ gst_encoding_profile_unref (prof);
+ } else {
+ ges_pipeline_set_mode (self->priv->pipeline, GES_PIPELINE_MODE_PREVIEW);
+ }
+ return TRUE;
+}
+
+static gboolean
+_create_pipeline (GESLauncher * self, const gchar * serialized_timeline)
+{
+ gchar *uri = NULL;
+ gboolean res = TRUE;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ /* Timeline creation */
+ if (opts->load_path) {
+ g_printf ("Loading project from : %s\n", opts->load_path);
+
+ if (!(uri = ensure_uri (opts->load_path))) {
+ g_error ("couldn't create uri for '%s'", opts->load_path);
+ goto failure;
+ }
+ }
+
+ self->priv->pipeline = ges_pipeline_new ();
+
+ if (!_create_timeline (self, serialized_timeline, uri, opts->scenario)) {
+ GST_ERROR ("Could not create the timeline");
+ goto failure;
+ }
+
+ if (!opts->load_path)
+ ges_timeline_commit (self->priv->timeline);
+
+ /* save project if path is given. we do this now in case GES crashes or
+ * hangs during playback. */
+ if (!_save_timeline (self))
+ goto failure;
+
+ if (opts->save_only_path)
+ goto done;
+
+ /* In order to view our timeline, let's grab a convenience pipeline to put
+ * our timeline in. */
+
+ if (opts->mute) {
+ GstElement *sink = gst_element_factory_make ("fakesink", NULL);
+
+ g_object_set (sink, "sync", TRUE, NULL);
+ ges_pipeline_preview_set_audio_sink (self->priv->pipeline, sink);
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ g_object_set (sink, "sync", TRUE, NULL);
+ ges_pipeline_preview_set_video_sink (self->priv->pipeline, sink);
+ }
+
+ /* Add the timeline to that pipeline */
+ if (!ges_pipeline_set_timeline (self->priv->pipeline, self->priv->timeline))
+ goto failure;
+
+done:
+ if (uri)
+ g_free (uri);
+
+ return res;
+
+failure:
+ {
+ if (self->priv->timeline)
+ gst_object_unref (self->priv->timeline);
+ if (self->priv->pipeline)
+ gst_object_unref (self->priv->pipeline);
+ self->priv->pipeline = NULL;
+ self->priv->timeline = NULL;
+
+ res = FALSE;
+ goto done;
+ }
+}
+
+static void
+_print_transition_list (void)
+{
+ print_enum (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
+}
+
+static GOptionGroup *
+ges_launcher_get_project_option_group (GESLauncher * self)
+{
+ GOptionGroup *group;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ GOptionEntry options[] = {
+ {"load", 'l', 0, G_OPTION_ARG_STRING, &opts->load_path,
+ "Load project from file. The project can be saved "
+ "again with the --save option.",
+ "<path>"},
+ {"save", 's', 0, G_OPTION_ARG_STRING, &opts->save_path,
+ "Save project to file before rendering. "
+ "It can then be loaded with the --load option",
+ "<path>"},
+ {"save-only", 0, 0, G_OPTION_ARG_STRING, &opts->save_only_path,
+ "Same as save project, except exit as soon as the timeline "
+ "is saved instead of playing it back",
+ "<path>"},
+ {NULL}
+ };
+ group = g_option_group_new ("project", "Project Options",
+ "Show project-related options", NULL, NULL);
+
+ g_option_group_add_entries (group, options);
+
+ return group;
+}
+
+static GOptionGroup *
+ges_launcher_get_info_option_group (GESLauncher * self)
+{
+ GOptionGroup *group;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ GOptionEntry options[] = {
+#ifdef HAVE_GST_VALIDATE
+ {"inspect-action-type", 0, 0, G_OPTION_ARG_NONE, &opts->inspect_action_type,
+ "Inspect the available action types that can be defined in a scenario "
+ "set with --set-scenario. "
+ "Will list all action-types if action-type is empty.",
+ "<[action-type]>"},
+#endif
+ {"list-transitions", 0, 0, G_OPTION_ARG_NONE, &opts->list_transitions,
+ "List all valid transition types and exit. "
+ "See ges-launch-1.0 help transition for more information.",
+ NULL},
+ {NULL}
+ };
+
+ group = g_option_group_new ("informative", "Informative Options",
+ "Show informative options", NULL, NULL);
+
+ g_option_group_add_entries (group, options);
+
+ return group;
+}
+
+static GOptionGroup *
+ges_launcher_get_rendering_option_group (GESLauncher * self)
+{
+ GOptionGroup *group;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ GOptionEntry options[] = {
+ {"outputuri", 'o', 0, G_OPTION_ARG_STRING, &opts->outputuri,
+ "If set, ges-launch-1.0 will render the timeline instead of playing "
+ "it back. The default rendering format is ogv, containing theora and vorbis.",
+ "<URI>"},
+ {"format", 'f', 0, G_OPTION_ARG_STRING, &opts->format,
+ "Set an encoding profile on the command line. "
+ "See ges-launch-1.0 help profile for more information. "
+ "This will have no effect if no outputuri has been specified.",
+ "<profile>"},
+ {"encoding-profile", 'e', 0, G_OPTION_ARG_STRING, &opts->encoding_profile,
+ "Set an encoding profile from a preset file. "
+ "See ges-launch-1.0 help profile for more information. "
+ "This will have no effect if no outputuri has been specified.",
+ "<profile-name>"},
+ {NULL}
+ };
+
+ group = g_option_group_new ("rendering", "Rendering Options",
+ "Show rendering options", NULL, NULL);
+
+ g_option_group_add_entries (group, options);
+
+ return group;
+}
+
+static GOptionGroup *
+ges_launcher_get_playback_option_group (GESLauncher * self)
+{
+ GOptionGroup *group;
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ GOptionEntry options[] = {
+ {"videosink", 'v', 0, G_OPTION_ARG_STRING, &opts->videosink,
+ "Set the videosink used for playback.", "<videosink>"},
+ {"audiosink", 'a', 0, G_OPTION_ARG_STRING, &opts->audiosink,
+ "Set the audiosink used for playback.", "<audiosink>"},
+ {"mute", 'm', 0, G_OPTION_ARG_NONE, &opts->mute,
+ "Mute playback output. This has no effect when rendering.", NULL},
+ {NULL}
+ };
+
+ group = g_option_group_new ("playback", "Playback Options",
+ "Show playback options", NULL, NULL);
+
+ g_option_group_add_entries (group, options);
+
+ return group;
+}
+
+static gboolean
+_local_command_line (GApplication * application, gchar ** arguments[],
+ gint * exit_status)
+{
+ GESLauncher *self = GES_LAUNCHER (application);
+ GError *error = NULL;
+ gchar **argv;
+ gint argc;
+ GOptionContext *ctx;
+ ParsedOptions *opts = &self->priv->parsed_options;
+ GOptionGroup *main_group;
+ GOptionEntry options[] = {
+ {"disable-mixing", 0, 0, G_OPTION_ARG_NONE, &opts->disable_mixing,
+ "Do not use mixing elements to mix layers together.", NULL},
+ {"track-types", 't', 0, G_OPTION_ARG_CALLBACK, &_parse_track_type,
+ "Specify the track types to be created. "
+ "When loading a project, only relevant tracks will be added to the timeline.",
+ "<track-types>"},
+ {"video-caps", 0, 0, G_OPTION_ARG_STRING, &opts->video_track_caps,
+ "Specify the track restriction caps of the video track.",},
+ {"audio-caps", 0, 0, G_OPTION_ARG_STRING, &opts->audio_track_caps,
+ "Specify the track restriction caps of the audio track.",},
+ {"track-types", 't', 0, G_OPTION_ARG_CALLBACK, &_parse_track_type,
+ "Specify the track types to be created. "
+ "When loading a project, only relevant tracks will be added to the timeline.",
+ "<track-types>"},
+#ifdef HAVE_GST_VALIDATE
+ {"set-scenario", 0, 0, G_OPTION_ARG_STRING, &opts->scenario,
+ "ges-launch-1.0 exposes gst-validate functionalities, such as scenarios."
+ " Scenarios describe actions to execute, such as seeks or setting of properties. "
+ "GES implements editing-specific actions such as adding or removing clips. "
+ "See gst-validate-1.0 --help for more info about validate and scenarios, "
+ "and --inspect-action-type.",
+ "<scenario_name>"},
+#endif
+ {NULL}
+ };
+
+ ctx = g_option_context_new ("- plays or renders a timeline.");
+ g_option_context_set_summary (ctx, HELP_SUMMARY);
+
+ main_group =
+ g_option_group_new ("launcher", "launcher options",
+ "Main launcher options", self, NULL);
+ g_option_group_add_entries (main_group, options);
+ g_option_context_set_main_group (ctx, main_group);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+ g_option_context_add_group (ctx, ges_init_get_option_group ());
+ g_option_context_add_group (ctx,
+ ges_launcher_get_project_option_group (self));
+ g_option_context_add_group (ctx,
+ ges_launcher_get_rendering_option_group (self));
+ g_option_context_add_group (ctx,
+ ges_launcher_get_playback_option_group (self));
+ g_option_context_add_group (ctx, ges_launcher_get_info_option_group (self));
+ g_option_context_set_ignore_unknown_options (ctx, TRUE);
+
+ argv = *arguments;
+ argc = g_strv_length (argv);
+ *exit_status = 0;
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
+ gst_init (NULL, NULL);
+ g_printerr ("Error initializing: %s\n", error->message);
+ g_option_context_free (ctx);
+ g_error_free (error);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ if (opts->inspect_action_type) {
+ ges_validate_print_action_types ((const gchar **) argv + 1, argc - 1);
+ return TRUE;
+ }
+
+ if (!opts->load_path && !opts->scenario && !opts->list_transitions
+ && (argc <= 1)) {
+ g_printf ("%s", g_option_context_get_help (ctx, TRUE, NULL));
+ g_option_context_free (ctx);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_option_context_free (ctx);
+
+ opts->sanitized_timeline = sanitize_timeline_description (argc, argv);
+
+ if (!g_application_register (application, NULL, &error)) {
+ *exit_status = 1;
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+_startup (GApplication * application)
+{
+ GESLauncher *self = GES_LAUNCHER (application);
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+#ifdef G_OS_UNIX
+ self->priv->signal_watch_id =
+ g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, self);
+#endif
+
+ /* Initialize the GStreamer Editing Services */
+ if (!ges_init ()) {
+ g_printerr ("Error initializing GES\n");
+ goto done;
+ }
+
+ if (opts->list_transitions) {
+ _print_transition_list ();
+ goto done;
+ }
+
+ if (!_create_pipeline (self, opts->sanitized_timeline))
+ goto failure;
+
+ if (opts->save_only_path)
+ goto done;
+
+ if (!_set_playback_details (self))
+ goto failure;
+
+ if (!_set_rendering_details (self))
+ goto failure;
+
+ if (!_run_pipeline (self))
+ goto failure;
+
+done:
+ G_APPLICATION_CLASS (ges_launcher_parent_class)->startup (application);
+
+ return;
+
+failure:
+ self->priv->seenerrors = TRUE;
+
+ goto done;
+}
+
+static void
+_shutdown (GApplication * application)
+{
+ gint validate_res = 0;
+ GESLauncher *self = GES_LAUNCHER (application);
+ ParsedOptions *opts = &self->priv->parsed_options;
+
+ _save_timeline (self);
+
+ if (self->priv->pipeline) {
+ gst_element_set_state (GST_ELEMENT (self->priv->pipeline), GST_STATE_NULL);
+ validate_res = ges_validate_clean (GST_PIPELINE (self->priv->pipeline));
+ }
+
+ if (self->priv->seenerrors == FALSE)
+ self->priv->seenerrors = validate_res;
+
+#ifdef G_OS_UNIX
+ g_source_remove (self->priv->signal_watch_id);
+#endif
+
+ g_free (opts->sanitized_timeline);
+
+ G_APPLICATION_CLASS (ges_launcher_parent_class)->shutdown (application);
+}
+
+static void
+ges_launcher_class_init (GESLauncherClass * klass)
+{
+ G_APPLICATION_CLASS (klass)->local_command_line = _local_command_line;
+ G_APPLICATION_CLASS (klass)->startup = _startup;
+ G_APPLICATION_CLASS (klass)->shutdown = _shutdown;
+}
+
+static void
+ges_launcher_init (GESLauncher * self)
+{
+ self->priv = ges_launcher_get_instance_private (self);
+ self->priv->parsed_options.track_types =
+ GES_TRACK_TYPE_AUDIO | GES_TRACK_TYPE_VIDEO;
+}
+
+gint
+ges_launcher_get_exit_status (GESLauncher * self)
+{
+ return self->priv->seenerrors;
+}
+
+GESLauncher *
+ges_launcher_new (void)
+{
+ return GES_LAUNCHER (g_object_new (ges_launcher_get_type (), "application-id",
+ "org.gstreamer.geslaunch", "flags",
+ G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_COMMAND_LINE, NULL));
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.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 _GES_LAUNCHER
+#define _GES_LAUNCHER
+
+#include <ges/ges.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_LAUNCHER ges_launcher_get_type()
+
+#define GES_LAUNCHER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_LAUNCHER, GESLauncher))
+
+#define GES_LAUNCHER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_LAUNCHER, GESLauncherClass))
+
+#define GES_IS_LAUNCHER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_LAUNCHER))
+
+#define GES_IS_LAUNCHER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_LAUNCHER))
+
+#define GES_LAUNCHER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_LAUNCHER, GESLauncherClass))
+
+typedef struct _GESLauncherPrivate GESLauncherPrivate;
+typedef struct _GESLauncher GESLauncher;
+typedef struct _GESLauncherClass GESLauncherClass;
+
+struct _GESLauncher {
+ GApplication parent;
+
+ /*< private >*/
+ GESLauncherPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESLauncherClass {
+ /*< private >*/
+ GApplicationClass parent_class;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GType ges_launcher_get_type (void);
+
+GESLauncher* ges_launcher_new (void);
+gint ges_launcher_get_exit_status (GESLauncher *self);
+
+G_END_DECLS
+
+#endif /* _GES_LAUNCHER */
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ges-validate.h"
+
+#include <string.h>
+#include <ges/ges.h>
+
+#ifdef HAVE_GST_VALIDATE
+#include <gst/validate/gst-validate-scenario.h>
+#include <gst/validate/validate.h>
+#include <gst/validate/gst-validate-utils.h>
+#include <gst/validate/gst-validate-element-monitor.h>
+
+#define MONITOR_ON_PIPELINE "validate-monitor"
+#define RUNNER_ON_PIPELINE "runner-monitor"
+#define WRONG_DECODER_ADDED g_quark_from_static_string ("ges::wrong-decoder-added")
+
+static void
+_validate_report_added_cb (GstValidateRunner * runner,
+ GstValidateReport * report, GstPipeline * pipeline)
+{
+ if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
+ GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch--validate-error");
+ }
+}
+
+static void
+bin_element_added (GstTracer * runner, GstClockTime ts,
+ GstBin * bin, GstElement * element, gboolean result)
+{
+ GstObject *parent;
+ GstValidateElementMonitor *monitor =
+ g_object_get_data (G_OBJECT (element), "validate-monitor");
+
+ if (!monitor)
+ return;
+
+ if (!monitor->is_decoder)
+ return;
+
+
+ parent = gst_object_get_parent (GST_OBJECT (element));
+ do {
+ if (GES_IS_TRACK (parent)) {
+ GstElementClass *klass = GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element));
+ const gchar *klassname =
+ gst_element_class_get_metadata (klass, GST_ELEMENT_METADATA_KLASS);
+
+ if (GES_IS_AUDIO_TRACK (parent) && strstr (klassname, "Audio") == NULL) {
+ GST_VALIDATE_REPORT (monitor, WRONG_DECODER_ADDED,
+ "Adding non audio decoder %s in audio track %s.",
+ GST_OBJECT_NAME (element), GST_OBJECT_NAME (parent));
+ } else if (GES_IS_VIDEO_TRACK (parent)
+ && strstr (klassname, "Video") == NULL
+ && strstr (klassname, "Image") == NULL) {
+ GST_VALIDATE_REPORT (monitor, WRONG_DECODER_ADDED,
+ "Adding non video decoder %s in video track %s.",
+ GST_OBJECT_NAME (element), GST_OBJECT_NAME (parent));
+
+ }
+ gst_object_unref (parent);
+ break;
+ }
+
+ gst_object_unref (parent);
+ parent = gst_object_get_parent (parent);
+ } while (parent);
+}
+
+static void
+ges_validate_register_issues (void)
+{
+ gst_validate_issue_register (gst_validate_issue_new (WRONG_DECODER_ADDED,
+ "Wrong decoder type added to track.",
+ "In a specific track type we should never create decoders"
+ " for some other types (No audio decoder should be added"
+ " in a Video track).", GST_VALIDATE_REPORT_LEVEL_CRITICAL));
+}
+
+
+gboolean
+ges_validate_activate (GstPipeline * pipeline, const gchar * scenario,
+ gboolean * needs_setting_state)
+{
+ GstValidateRunner *runner = NULL;
+ GstValidateMonitor *monitor = NULL;
+
+ ges_validate_register_action_types ();
+ ges_validate_register_issues ();
+
+ if (scenario) {
+ if (g_strcmp0 (scenario, "none")) {
+ gchar *scenario_name = g_strconcat (scenario, "->gespipeline*", NULL);
+ g_setenv ("GST_VALIDATE_SCENARIO", scenario_name, TRUE);
+ g_free (scenario_name);
+ }
+ }
+
+ runner = gst_validate_runner_new ();
+ gst_tracing_register_hook (GST_TRACER (runner), "bin-add-post",
+ G_CALLBACK (bin_element_added));
+ g_signal_connect (runner, "report-added",
+ G_CALLBACK (_validate_report_added_cb), pipeline);
+ monitor =
+ gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline), runner,
+ NULL);
+
+ gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor));
+
+ g_object_get (monitor, "handles-states", needs_setting_state, NULL);
+ *needs_setting_state = !*needs_setting_state;
+ g_object_set_data (G_OBJECT (pipeline), MONITOR_ON_PIPELINE, monitor);
+ g_object_set_data (G_OBJECT (pipeline), RUNNER_ON_PIPELINE, runner);
+
+ return TRUE;
+}
+
+gint
+ges_validate_clean (GstPipeline * pipeline)
+{
+ gint res = 0;
+ GstValidateMonitor *monitor =
+ g_object_get_data (G_OBJECT (pipeline), MONITOR_ON_PIPELINE);
+ GstValidateRunner *runner =
+ g_object_get_data (G_OBJECT (pipeline), RUNNER_ON_PIPELINE);
+
+ if (runner)
+ res = gst_validate_runner_exit (runner, TRUE);
+
+ gst_object_unref (pipeline);
+ if (runner) {
+ gst_object_unref (runner);
+ if (monitor)
+ gst_object_unref (monitor);
+ }
+
+ return res;
+}
+
+void
+ges_validate_handle_request_state_change (GstMessage * message,
+ GApplication * application)
+{
+ GstState state;
+
+ gst_message_parse_request_state (message, &state);
+
+ if (GST_IS_VALIDATE_SCENARIO (GST_MESSAGE_SRC (message))
+ && state == GST_STATE_NULL) {
+ gst_validate_printf (GST_MESSAGE_SRC (message),
+ "State change request NULL, " "quiting application\n");
+ g_application_quit (application);
+ }
+}
+
+gint
+ges_validate_print_action_types (const gchar ** types, gint num_types)
+{
+ ges_validate_register_action_types ();
+
+ if (!gst_validate_print_action_types (types, num_types)) {
+ GST_ERROR ("Could not print all wanted types");
+ return 1;
+ }
+
+ return 0;
+}
+
+#else
+static gboolean
+_print_position (GstElement * pipeline)
+{
+ gint64 position = 0, duration = -1;
+
+ if (pipeline) {
+ gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+ &position);
+ gst_element_query_duration (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+ &duration);
+
+ g_print ("<position: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
+ "/>\r", GST_TIME_ARGS (position), GST_TIME_ARGS (duration));
+ }
+
+ return TRUE;
+}
+
+gboolean
+ges_validate_activate (GstPipeline * pipeline, const gchar * scenario,
+ gboolean * needs_setting_state)
+{
+ if (scenario) {
+ GST_WARNING ("Trying to run scenario %s, but gst-validate not supported",
+ scenario);
+
+ return FALSE;
+ }
+
+ g_object_set_data (G_OBJECT (pipeline), "pposition-id",
+ GUINT_TO_POINTER (g_timeout_add (200,
+ (GSourceFunc) _print_position, pipeline)));
+
+ *needs_setting_state = TRUE;
+
+ return TRUE;
+}
+
+gint
+ges_validate_clean (GstPipeline * pipeline)
+{
+ g_source_remove (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pipeline),
+ "pposition-id")));
+
+ gst_object_unref (pipeline);
+
+ return 0;
+}
+
+void
+ges_validate_handle_request_state_change (GstMessage * message,
+ GApplication * application)
+{
+ return;
+}
+
+gint
+ges_validate_print_action_types (const gchar ** types, gint num_types)
+{
+ return 0;
+}
+
+#endif
--- /dev/null
+/* GStreamer Editing Services
+ *
+ * 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 _GES_VALIDATE_
+#define _GES_VALIDATE_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+gboolean
+ges_validate_activate (GstPipeline *pipeline, const gchar *scenario, gboolean *needs_set_state);
+void ges_launch_validate_uri (const gchar *nid);
+
+gint
+ges_validate_clean (GstPipeline *pipeline);
+
+void ges_validate_handle_request_state_change (GstMessage *message, GApplication *application);
+gint ges_validate_print_action_types (const gchar **types, gint num_types);
+
+G_END_DECLS
+
+#endif /* _GES_VALIDATE */
--- /dev/null
+deps = [ges_dep, gstpbutils_dep, gio_dep]
+
+ges_tool_args = [ges_c_args]
+if gstvalidate_dep.found()
+ deps = deps + [gstvalidate_dep]
+ ges_tool_args += ['-DGST_USE_UNSTABLE_API']
+endif
+
+executable('ges-launch-@0@'.format(apiversion),
+ 'ges-validate.c', 'ges-launch.c', 'ges-launcher.c', 'utils.c',
+ c_args : [ges_tool_args],
+ dependencies : deps,
+ install: true
+)
+
+install_man('ges-launch-1.0.1')
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <glib/gprintf.h>
+#include <string.h>
+#include <gst/gst.h>
+#include "utils.h"
+
+#define IS_ALPHANUM(c) (g_ascii_isalnum((c)) || ((c) == '-') || ((c) == '+'))
+
+/* g_free after usage */
+static gchar *
+_sanitize_argument (gchar * arg)
+{
+ gboolean has_non_alphanum = FALSE;
+ char *equal_index = strstr (arg, "=");
+ gchar *new_string, *tmp_string;
+
+ for (tmp_string = arg; *tmp_string != '\0'; tmp_string++) {
+ if (!IS_ALPHANUM (*tmp_string)) {
+ has_non_alphanum = TRUE;
+
+ break;
+ }
+ }
+
+ if (!has_non_alphanum)
+ return g_strdup (arg);
+
+ if (!equal_index)
+ return g_strdup_printf ("\"%s\"", arg);
+
+ tmp_string = new_string = g_malloc (sizeof (gchar) * (strlen (arg) + 3));
+ for (; *arg != '\0'; arg++) {
+ *tmp_string = *arg;
+ tmp_string += 1;
+ if (*arg == '=') {
+ *tmp_string = '"';
+ tmp_string += 1;
+ }
+ }
+ *tmp_string = '"';
+ tmp_string += 1;
+ *tmp_string = '\0';
+
+ return new_string;
+}
+
+gchar *
+sanitize_timeline_description (int argc, char **argv)
+{
+ gint i;
+
+ gchar *string = g_strdup (" ");
+
+ for (i = 1; i < argc; i++) {
+ gchar *new_string;
+ gchar *sanitized = _sanitize_argument (argv[i]);
+
+ new_string = g_strconcat (string, " ", sanitized, NULL);
+
+ g_free (sanitized);
+ g_free (string);
+ string = new_string;
+ }
+
+ return string;
+}
+
+guint
+get_flags_from_string (GType type, const gchar * str_flags)
+{
+ guint i;
+ gint flags = 0;
+ GFlagsClass *class = g_type_class_ref (type);
+
+ for (i = 0; i < class->n_values; i++) {
+ if (g_strrstr (str_flags, class->values[i].value_nick)) {
+ flags |= class->values[i].value;
+ }
+ }
+ g_type_class_unref (class);
+
+ return flags;
+}
+
+gchar *
+ensure_uri (const gchar * location)
+{
+ if (gst_uri_is_valid (location))
+ return g_strdup (location);
+ else
+ return gst_filename_to_uri (location, NULL);
+}
+
+GstEncodingProfile *
+parse_encoding_profile (const gchar * format)
+{
+ GstEncodingProfile *profile;
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, GST_TYPE_ENCODING_PROFILE);
+
+ if (!gst_value_deserialize (&value, format)) {
+ g_value_reset (&value);
+
+ return NULL;
+ }
+
+ profile = g_value_dup_object (&value);
+ g_value_reset (&value);
+
+ return profile;
+}
+
+void
+print_enum (GType enum_type)
+{
+ GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (enum_type));
+ guint i;
+
+ for (i = 0; i < enum_class->n_values; i++) {
+ g_printf ("%s\n", enum_class->values[i].value_nick);
+ }
+
+ g_type_class_unref (enum_class);
+}
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gst/pbutils/encoding-profile.h>
+
+gchar * sanitize_timeline_description (int argc, char **argv);
+guint get_flags_from_string (GType type, const gchar * str_flags);
+gchar * ensure_uri (const gchar * location);
+GstEncodingProfile * parse_encoding_profile (const gchar * format);
+void print_enum (GType enum_type);