--- /dev/null
+*.o
+*.so
+*.so.*
+config.mak
+config.h
+.depend
--- /dev/null
+MLT framework was developed by:
+Charles Yates <charles.yates@pandora.be>
+
+MLT framework is maintained by:
+Dan Dennedy <dan@dennedy.org>
+
+MLT module authors and maintainers:
+
+Charles Yates <charles.yates@pandora.be>
+Dan Dennedy <dan@dennedy.org>
+Stephane Fillod (effectv)
+Marco Gittler <g.marco@freenet.de> (frei0r, oldfilm)
+Jean-Baptiste Mardelle <jb@ader.ch> (kdenlive, qimage)
+Zachary Drew (motion_est)
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\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 combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\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 with
+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 Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\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
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+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
+2009-04-14 Dan Dennedy <dan@dennedy.org>
+
+ * src/modules/avformat/configure: Fix build for --avformat-svn to use FFmpeg
+ v0.5 and HEAD build to not use --enable-swscale.
+
+2009-04-15 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, Makefile: Update ChangeLog and remove svn log from the make
+ install target.
+
+ * NEWS, configure, src/framework/mlt.h, src/modules/avformat/configure: bump
+ to version 0.3.8
+
+2009-04-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.ini: fezzik.ini: workaround scaling resolution
+ limitation with swscale filter by making it the lowest priority
+
+ * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c:
+ interpret negative speed as reverse
+
+2009-04-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ building on some older versions.
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat: bugfix
+ (kdenlive-677) to make interlaced coding automatic if profile is not
+ progressive and coding not explicit by ildct and ilme properties.
+
+2009-04-07 Ray Lehtiniemi <rayl@mail.com>
+
+ * src/modules/kdenlive/filter_boxblur.c: Fix a 64-bit segfault in kdenlive
+ To reproduce: - create a new project - create a color clip - add clip to
+ timeline - set an in point on the clip - add the box blur effect The
+ segfault happens because we take the negative of an unsigned integer. This
+ works out to a signed 32 bit value on a 64 bit platform, which causes the rgb
+ array bounds to be exceeded.
+
+ * src/framework/mlt_consumer.c, src/miracle/miracle_connection.c,
+ src/modules/kino/riff.cc: Fix up a few ignored return values
+
+ * src/framework/mlt_pool.c: Fix warning: pointer of type ‘void *’ used in
+ arithmetic
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c, src/modules/westley/producer_westley.c:
+ Constness changes
+
+ * src/framework/mlt_properties.c, src/humperdink/client.c,
+ src/miracle/miracle_connection.c, src/modules/avformat/consumer_avformat.c,
+ src/modules/core/filter_data_show.c, src/modules/kino/filehandler.cc,
+ src/valerie/valerie_response.c, src/valerie/valerie_response.h: Constness
+ changes
+
+ * src/framework/mlt_tokeniser.c, src/framework/mlt_tokeniser.h,
+ src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+ src/valerie/valerie.c, src/valerie/valerie.h: Constness changes
+
+ * src/humperdink/io.c, src/humperdink/io.h,
+ src/modules/core/transition_composite.c, src/modules/gtk2/producer_pango.c,
+ src/modules/westley/consumer_westley.c, src/valerie/valerie.c,
+ src/valerie/valerie.h, src/valerie/valerie_parser.c,
+ src/valerie/valerie_parser.h, src/valerie/valerie_socket.c,
+ src/valerie/valerie_socket.h: Constness changes
+
+ * src/framework/mlt_events.c, src/framework/mlt_events.h, src/inigo/inigo.c,
+ src/modules/avformat/factory.c, src/modules/plus/transition_affine.c,
+ src/modules/westley/producer_westley.c, src/modules/xine/deinterlace.c,
+ src/modules/xine/deinterlace.h: Constness changes
+
+ * src/miracle/miracle_local.c, src/valerie/valerie.c, src/valerie/valerie.h,
+ src/valerie/valerie_status.c, src/valerie/valerie_tokeniser.c,
+ src/valerie/valerie_tokeniser.h: Constness changes
+
+ * src/humperdink/client.c, src/humperdink/io.c, src/humperdink/io.h,
+ src/miracle/miracle_log.c, src/miracle/miracle_log.h, src/valerie/valerie.c,
+ src/valerie/valerie.h, src/valerie/valerie_response.c,
+ src/valerie/valerie_response.h: Constness changes
+
+ * src/framework/mlt_multitrack.c, src/modules/effectv/image.c,
+ src/modules/gtk2/producer_pango.c, src/modules/jackrack/jack_rack.c,
+ src/modules/motion_est/filter_motion_est.c, src/modules/xine/xineutils.h:
+ Constness changes
+
+2009-03-31 Ray Lehtiniemi <rayl@mail.com>
+
+ * src/framework/mlt_properties.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Constness changes
+
+2009-03-04 Ray Lehtiniemi <rayl@mail.com>
+
+ * src/framework/mlt_events.c, src/framework/mlt_events.h,
+ src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_repository.c, src/framework/mlt_repository.h,
+ src/valerie/valerie_response.c, src/valerie/valerie_response.h: Constness
+ changes
+
+ * .gitignore: Add a .gitignore file
+
+2009-04-05 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c: producer_westley.c: Don't prepend
+ westley document root to empty properties
+
+2009-04-03 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_crop.c: filter_crop.c: bugfix chroma alignment
+
+2009-03-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: add /usr/lib64 to the
+ default frei0r plugin path
+
+2009-03-15 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: transition_composite.c: allow
+ removing of luma file by passing an empty name
+
+2009-03-14 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: transition_composite.c: make luma
+ and luma_invert properties mutable
+
+2009-03-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: add
+ backwards compatibility macro for PIX_FMT_YUYV422
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/filter_swscale.c: avformat: fix compilation due to
+ recent PIX_FMT changes in libavutil v50.
+
+2009-03-08 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: Fix
+ producer out position
+
+2009-03-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_log.h: mlt_log.h: add convenience macros
+
+2009-03-03 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/riff.cc: kino/riff.cc: suppress compiler warning
+
+ * src/modules/frei0r/factory.c, src/modules/frei0r/producer_frei0r.c:
+ frei0r/factory.c, producer_frei0r.c: suppress compiler warnings
+
+ * src/framework/mlt_property.c: mlt_property.c: suppress compiler warning
+
+2009-02-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/producer_colour.c: producer_colour.c: improve previous
+ patch
+
+ * src/modules/core/producer_colour.c: producer_colour.c: bugfix reading color
+ value after westley has prepended the document path to the resource property
+
+2009-02-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: consumer_sdl*.c: apply patch from
+ Jean-Baptiste Mardelle to add window_background property
+
+2009-02-20 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c:
+ filter_chroma.c: update to use new property-based color value
+
+ * src/modules/vmfx/filter_chroma_hold.c: filter_chroma_hold.c: update to use
+ new property-based color value
+
+ * src/modules/core/producer_colour.c: producer_colour.c: update to use new
+ property-based color parsing.
+
+ * src/framework/mlt_property.c: mlt_property.c: interpret hex int as unsigned
+
+ * src/modules/frei0r/frei0r_helper.c: frei0r_helper.c: cleanup color parser
+ to use new code in mlt_property.c
+
+ * src/framework/mlt_property.c: mlt_property.c: added parsing for color
+ values beginning with #
+
+2009-02-20 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/producer_frei0r.c: modules/frei0r: added missing
+ producer_frei0r.c -This line, and those below, will be ignored-- A
+ producer_frei0r.c
+
+ * src/modules/frei0r/Makefile, src/modules/frei0r/factory.c,
+ src/modules/frei0r/frei0r_helper.c: added frei0r producers (patch from jb)
+ thx to jb
+
+2009-02-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/Makefile, src/humperdink/Makefile, src/inigo/Makefile,
+ src/miracle/Makefile: albino/Makefile, inigo/Makefile, humperdink/Makefile,
+ miracle/Makefile: apply patch from Alberto Villa to fix underlinking on
+ FreeBSD
+
+2009-02-16 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c:
+ frei0r/factory.c, frei0r_helper.c: add support for color parameter type with
+ whitespace cleanup courtesy of eclipse.
+
+2009-02-14 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_affine.c: filter_affine.c: remove silly default
+ rotate animation for new kdenlive pan and zoom effect (kdenlive-565)
+
+ * src/modules/core/Makefile, src/modules/core/factory.c,
+ src/modules/core/filter_crop.c, src/modules/fezzik.ini: filter_crop.c: add
+ cropping filter (kdenlive-509)
+
+ * configure: configure: relax optimization level slightly to improve debugger
+ backtraces in bug reports
+
+ * src/modules/plus/transition_affine.c: transition_affine.c: bugfix chroma
+ alignment
+
+2009-02-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_brightness.c: filter_brightness.c: fix the
+ wonkiness by filtering chroma as well.
+
+2009-02-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * profiles/cif_15, profiles/qcif_15, profiles/quarter_15: profiles/*_15: add
+ some 15fps profiles
+
+ * src/modules/qimage/configure: qimage/configure: let qimage first attempt to
+ use Qt4 through pkg-config (canonical) without having to specify directories
+ or QTDIR
+
+ * src/modules/sox/configure: sox/configure: give pkg-config priority over
+ libst-config
+
+2009-02-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/configure: qimage/configure: fix build on OS X
+
+ * src/modules/avformat/filter_avdeinterlace.c: filter_avdeinterlace.c: bugfix
+ (kdenlive-672) deinterlace only works on left half of image
+
+ * src/modules/qimage/producer_qimage.c,
+ src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+ producer_qimage.c, qimage_wrapper.{h,cpp}: enhance qimage producer to use the
+ new mlt_cache (kdenlive-575)
+
+ * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: enhance pixbuf
+ producer to use new mlt_cache (kdenlive-575)
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c,
+ src/modules/vorbis/producer_vorbis.c: producer_vorbis.c, producer_avformat.c,
+ consumer_avformat.c: update headers in services for framework changes with
+ addition of mlt_cache
+
+ * configure, src/framework/Makefile, src/framework/mlt.h,
+ src/framework/mlt_cache.c, src/framework/mlt_cache.h,
+ src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_types.h: mlt_cache.[hc], mlt_types.h, mlt_service.[hc],
+ mlt_factory.[hc], mlt.h: add mlt_cache and related service functions
+ (kdenlive-575)
+
+ * Doxyfile: Doxyfile: set tab width to 4 spaces
+
+ * src/framework/mlt_properties.c: mlt_properties.c: update doxygen comments
+ for some out params
+
+ * src/framework/mlt_property.c: mlt_property.c: update a doxygen comment to
+ label param as out
+
+2009-02-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * debian/changelog, debian/control, debian/copyright, debian/rules: remove
+ the debian package subdirectory (they provide their own)
+
+2009-02-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/framework/mlt.h, src/modules/avformat/configure: bump to
+ version 0.3.6
+
+ * NEWS: NEWS: add release notes for 0.3.6
+
+2009-02-01 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: add more default locations
+ for locating plugins including one for MacPorts
+
+2009-01-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c: inigo.c: make usage fit in 80 columns
+
+2009-01-29 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/filter_swscale.c: filter_swscale.c: Fix compilation
+ (typo introduced in rev. 1330)
+
+2009-01-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik/producer_fezzik.c: producer_fezzik.c: do not use the
+ swscale filter on images wider than 2048 loaded by the sdl_image producer.
+
+ * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c,
+ src/modules/sdl/producer_sdl_image.c: producer_pixbuf.c, producer_qimage.c,
+ producer_sdl_image.c: bugfix (kdenlive-575) large memory consumption loading
+ many pictures.
+
+ * src/modules/avformat/filter_swscale.c: filter_swscale.c: throw assert if
+ creation of swscale context fails.
+
+ * src/modules/avformat/factory.c: avformat/factory.c: set ffmpeg logging to
+ the same level as MLT's
+
+2009-01-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix
+ (kdenlive-575) memory leak
+
+ * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix
+ (kdenlive-575) memory leak
+
+2009-01-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: add version
+ check to use AVCodec->long_name
+
+2009-01-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: consumer_sdl.c: bugfix segfault on
+ unchecked pointer
+
+ * src/modules/inigo/producer_inigo.c: producer_inigo.c: bugfix segfault on
+ unchecked pointer
+
+2009-01-21 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/inigo.txt: inigo.txt: update usage info
+
+ * docs/policies.txt: policies.txt: add instruction to update version in
+ mlt.h\!
+
+ * src/framework/mlt.h, src/framework/mlt_consumer.c,
+ src/framework/mlt_consumer.h, src/framework/mlt_deque.c,
+ src/framework/mlt_deque.h, src/framework/mlt_events.c,
+ src/framework/mlt_events.h, src/framework/mlt_factory.c,
+ src/framework/mlt_factory.h, src/framework/mlt_field.c,
+ src/framework/mlt_field.h, src/framework/mlt_filter.c,
+ src/framework/mlt_filter.h, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/framework/mlt_multitrack.c,
+ src/framework/mlt_multitrack.h, src/framework/mlt_parser.c,
+ src/framework/mlt_parser.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_pool.c,
+ src/framework/mlt_pool.h, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_profile.c,
+ src/framework/mlt_profile.h, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/framework/mlt_property.c,
+ src/framework/mlt_property.h, src/framework/mlt_repository.c,
+ src/framework/mlt_repository.h, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_tokeniser.c,
+ src/framework/mlt_tokeniser.h, src/framework/mlt_tractor.c,
+ src/framework/mlt_tractor.h, src/framework/mlt_transition.c,
+ src/framework/mlt_transition.h, src/framework/mlt_types.h: Add doxygen
+ documentation for mlt_profile, mlt_pool, mlt_repository, and mlt_factory.
+ Update copyrights to 2009. Add cross references from files to data structures
+ in doxygen.
+
+2009-01-14 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c: inigo/inigo.c: add -debug and -verbose options to turn
+ on additional logging.
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: set consumer
+ buffer prefill to 1 by default.
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+ (kdenlive-450) bad timestamps in MPEG-2 Transport Stream and possibly quite a
+ few other formats.
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: add support
+ for an=1, vn=1, acodec=none, and vcodec=none options (kdenlive-533)
+
+2009-01-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/policies.txt: docs/policies.txt: Add policy about not using stdout,
+ messages, and recommending the new log API.
+
+ * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_consumer.c,
+ src/framework/mlt_events.c, src/framework/mlt_log.c, src/framework/mlt_log.h,
+ src/framework/mlt_pool.c, src/framework/mlt_producer.c,
+ src/framework/mlt_properties.c, src/framework/mlt_repository.c,
+ src/framework/mlt_tractor.c, src/framework/mlt_transition.c: mlt_log.[hc],
+ mlt_transition.c, mlt_tractor.c, mlt_repository.c, mlt_properties.c,
+ mlt_producer.c, mlt_pool.c, mlt_events.c, mlt_consumer.c, mlt.h, Makefile:
+ add logging system based on FFmpeg's.
+
+ * configure: configure: separate -march (suitable on x86) and -mcpu (suitable
+ on ppc, arm, and sparc)
+
+2009-01-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Doxyfile: Doxyfile: strip the path to the source files
+
+ * src/modules/core/producer_consumer.c: producer_consumer.c: bugfix setting
+ in point
+
+ * src/framework/mlt_frame.h, src/framework/mlt_multitrack.c,
+ src/framework/mlt_multitrack.h, src/framework/mlt_playlist.h,
+ src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+ src/framework/mlt_tractor.h: mlt_tractor.[ch], mlt_multitrack.[ch]: improve
+ doxygen documentation for the tractor and mulitrack classes
+
+2009-01-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c,
+ src/modules/avformat/producer_avformat.yml: producer_avformat.{c,yml}:
+ support special constructor argument values to list available demuxers and
+ decoders: f-list[[,]acodec-list][[,]vcodec-list]
+
+ * src/inigo/inigo.c: inigo/inigo.c: fix the usage help within 80 characters
+ wide.
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: report list
+ of muxers when f=list and codecs when acodec=list or vcodec=list.
+
+ * src/framework/mlt_repository.c: mlt_repository.c: report reason when dlopen
+ fails.
+
+2009-01-05 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+ src/framework/mlt_frame.h, src/framework/mlt_multitrack.c,
+ src/framework/mlt_multitrack.h, src/framework/mlt_producer.c,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_transition.c, src/framework/mlt_transition.h:
+ mlt_filter.[ch], mlt_transition.[ch], mlt_consumer.[ch]: improve doxygen for
+ filter, transition, and consumer
+
+2009-01-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: avformat/configure: add -lbz2 automatically
+ for --avformat-svn
+
+2008-12-31 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/modules/avformat/producer_avformat.c: producer_avformat.c:
+ fix build on older versions of ffmpeg; whitespace cleanup by eclipse.
+
+2008-12-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Doxyfile: Doxyfile: bump version
+
+2008-12-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * NEWS, configure: NEWS, configure: set version to 0.3.4 and add release
+ notes
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: further
+ analysis and testing reveals the DV encoder does not need the special aspect
+ ratio overrides. It expects a generic input. Only the DV decoder produces the
+ special, proper aspect ratios for which MLT is not yet prepared.
+
+2008-12-28 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/filter_sox.c: filter_sox.c: fix crash when trying to create
+ a sox filter with wrong name
+
+2008-12-28 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: avformat/configure: update the recommended
+ ffmpeg revision
+
+ * src/modules/avformat/producer_avformat.c,
+ src/modules/core/filter_rescale.c, src/modules/core/producer_consumer.c,
+ src/modules/dv/producer_libdv.c: filter_rescale.c, producer_avformat.c,
+ producer_libdv.c, producer_consumer.c: coerce a deinterlace when scaling an
+ interlaced source.
+
+2008-12-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt-config-template: mlt-config-template: send deprecation warning to
+ stderr to prevent breaking legacy scripts and makefiles
+
+ * src/modules/core/filter_luma.c: filter_luma.c: prevent the first
+ application of the nested luma transition from being applied to a test card
+ image. This makes slideshows start without a transition at the beginning,
+ which is nicer and more expected.
+
+ * src/modules/core/transition_luma.c: transition_luma.c: bugfix
+ (kdenlive-496) floating point exception when a slideshow using filter luma is
+ added to a multitrack.
+
+2008-12-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c: producer_westley.c: silence
+ compilation warning on uninitialized variable.
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: producer_avformat.c,
+ consumer_avformat.c: use av_set_string3 where available (gets rid of
+ deprecation warning).
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+ rendering to widescreen PAL DV.
+
+2008-12-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c: add meta.media.
+ properties.
+
+ * src/modules/dv/producer_libdv.c: producer_libdv.c: add meta.media.
+ properties.
+
+ * src/modules/avformat/Makefile: avformat/Makefile: suppress error on
+ uninstall target
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c: avformat/configure, avformat/Makfile,
+ avformat/factory.c: Add a --avformat-no-filters configure option to
+ facilitate building a codecs and muxers only module. Change the module
+ filename for a no-codecs build to libmltffmpeg.so to prevent a clash with a
+ no-filters module (libmltavformat.so).
+
+2008-12-21 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: add a bunch
+ of metadata about the media under the properties key prefix "meta.media."
+
+2008-12-21 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/producer_sdl_image.c: producer_sdl_image.c: Fix crash when
+ attempting to play a folder without images
+
+2008-12-20 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: consumer_sdl.c: let it work without
+ filter_avcolour_space
+
+ * src/modules/core/producer_consumer.c: producer_consumer.c: use parent
+ profile if none specified; accept real_time properties from parent producer.
+
+ * src/modules/core/producer_consumer.c: producer_consumer.c: set our length
+ from the length of the nested producer so we can terminate at the end of
+ rendering.
+
+ * src/framework/mlt_properties.c: mlt_properties.c: fix some documentation
+
+ * src/modules/core/Makefile, src/modules/core/factory.c,
+ src/modules/core/producer_consumer.c: core/Makefile, core/factory.c,
+ core/producer_consumer.c: add new producer_consumer that will consume from an
+ encapsulated producer under a different profile that the parent producer
+ (kdenlive-323).
+
+ * src/modules/core/transition_region.c: transition_region.c: bugfix
+ regression with in built circle region
+
+ * src/modules/avformat/filter_swscale.c: avformat/filter_swscale.c: add
+ support for scaling the alpha channel (needs further testing)
+
+ * src/modules/avformat/Makefile, src/modules/avformat/factory.c,
+ src/modules/avformat/filter_swscale.c, src/modules/fezzik.ini:
+ avformat/Makefile, avformat/factory.c, avformat/filter_swscale.c: add new
+ image scaler using FFmpeg libswcale. fezzik.ini: add swscale at higher
+ priority than gtk2/rescale.
+
+2008-12-19 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict: fezzik.dict: let qimage be a producer for svg
+
+2008-12-18 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c: avformat/configure, avformat/Makefile,
+ avformat/factory.c: add configure option --avformat-no-codecs, which will
+ build the avformat module without the producer and consumer - useful to
+ people who want to make a version entirely without including FFmpeg's codecs,
+ which present patent royalty licensing issues.
+
+ * src/modules/avformat/configure: avformat/configure: checkout
+ (--avformat-svn) or recommend (--help) a specific FFmpeg revision if this is
+ a release version of MLT (last field of version is even).
+
+ * configure: configure: --disable-mmx implies --disable-sse
+
+ * src/modules/avformat/Makefile, src/modules/avformat/factory.c,
+ src/modules/avformat/filter_avdeinterlace.c: avformat/Makefile,
+ avformat/factory.c, avformat/filter_avdeinterlace.c: Fix and enable the
+ avdeinterlace filter for a non-MMX configuration.
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: add support
+ for AVOptions as properties.
+
+2008-12-16 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_events.c, src/framework/mlt_field.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_producer.c, src/framework/mlt_tractor.c: mlt_producer.c,
+ mlt_playlist.h, mlt_field.h, mlt_playlist.c, mlt_tractor.c, mlt_events.c: add
+ doxygen docs for events, field, and playlist.
+
+2008-12-14 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ (kdenlive-432) segfault when reusing previous AVFrame (paused or idling on
+ last frame) but the previos AVFrame was invalid (not got_picture before
+ erroring out).
+
+2008-12-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/filter_motion_est.c: motion_est/filter_motion_est.c:
+ the sse compilation flag logic was inverted
+
+ * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c: gtk2/pixops.c,
+ gtk2/Makefile: prevent MMX on all x86_64, not just OS X
+
+ * configure: configure: add make flag and define for ARCH_X86_64 for all OSes
+
+ * configure: configure: fix mmx/sse detection on OS X and add detection of
+ x86_64 to define ARCH_X86_64
+
+ * src/modules/xine/Makefile, src/modules/xine/configure,
+ src/modules/xine/deinterlace.c, src/modules/xine/xineutils.h: xine/Makefile,
+ xine/xineutils.h, xine/deinterlace.c: respect mmx compilation flag instead of
+ using own detection xine/configure: remove, no longer necessary
+
+ * src/modules/motion_est/filter_motion_est.c: filtedr_motion_est.c: respect
+ new --disable-sse configure flag and whitespace cleanup
+
+ * src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+ src/modules/gtk2/pixops.c: gtk2/Makefile, gtk2/configure, gtk2/pixops.c:
+ disable MMX parts on OS X - does not build
+
+ * src/modules/kino/configure: kino/configure: automatically disable on OS X -
+ does not build due to missing headers
+
+ * configure: configure: add --disable-sse and add mmx/sse detection for OS X
+
+2008-12-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * profiles/atsc_1080i_50: profiles/atsc_1080i_50: added new profile for
+ square pixel 1080i at a PAL-like rate
+
+ * Doxyfile: Doxyfile: add doxygen config file
+
+ * src/valerie/valerie_status.h: valerie_status.h: take stdio.h header from
+ system include path
+
+ * docs/install.txt: docs/install.txt: fix license info on humperdink and
+ valerie
+
+ * configure: configure: bump the version
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_deque.c, src/framework/mlt_deque.h,
+ src/framework/mlt_events.c, src/framework/mlt_events.h,
+ src/framework/mlt_field.c, src/framework/mlt_field.h,
+ src/framework/mlt_filter.h, src/framework/mlt_frame.h,
+ src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+ src/framework/mlt_parser.c, src/framework/mlt_parser.h,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_pool.c, src/framework/mlt_pool.h,
+ src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+ src/framework/mlt_profile.c, src/framework/mlt_profile.h,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.c, src/framework/mlt_property.h,
+ src/framework/mlt_repository.c, src/framework/mlt_repository.h,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_tokeniser.c, src/framework/mlt_tokeniser.h,
+ src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+ src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+ src/framework/mlt_types.h: src/framework/*: improve the doxygen documentation
+ (work in progress). This also includes removal of superfluous white space.
+
+2008-12-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c,
+ src/modules/sdl/producer_sdl_image.c: producer_pixbuf.c, producer_qimage.c,
+ producer_sdl_image.c: bugfix (kdenlive-422) not validating input file for
+ image producers.
+
+ * src/modules/inigo/producer_inigo.c: producer_inigo.c: display a warning
+ when failed to load a file.
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: clean up the
+ dual pass log at the end of the second pass.
+
+2008-11-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix r1242
+ segfault due to improper audio decoder memory allocation. Also fix logical
+ bug with resampling on channels > 2
+
+ * src/modules/avformat/audioconvert.h,
+ src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ (kdenlive-297) audio distortion with audio formats other than signed 16-bit.
+
+2008-11-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict: fezzik.dict: added support for .tif equivalent to
+ .tiff
+
+2008-11-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/albino.c: albino.c: fix playout with SDL on Mac OS X
+
+ * src/modules/sox/filter_sox.c: filter_sox.c: bugfix (2263114) build on sox
+ 14.2.0.
+
+2008-11-13 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/filter_freeze.c: filter_freeze.c: fix detection of
+ current frame position in a playlist
+
+2008-11-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ (kdenlive-347) segfault when resolution is not known until after first frame
+ is decoded. Also, bugfix segfault when video_index or audio_index are -1
+ (invalid).
+
+2008-11-13 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/filter_freeze.c: filter_freeze.c: update frozen frame
+ if freeze position is changed on the fly
+
+2008-11-13 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/oldfilm/filter_vignette.c,
+ src/modules/oldfilm/filter_vignette.yml: filter_vignette.{c,yml}: better
+ standard values and correct start param name
+
+2008-11-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * NEWS, configure: configure, NEWS: bump to version 0.3.2 and update release
+ notes
+
+2008-11-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_affine.c: filter_affine.c: bugfix (kdenlive-235)
+ rendering when used inside a multitrack.
+
+2008-11-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * profiles/hdv_720_50p: profiles/hdv_720_50p: fix frame rate in description
+
+ * profiles/atsc_1080p_2398, profiles/atsc_1080p_24, profiles/atsc_1080p_25,
+ profiles/atsc_1080p_2997, profiles/atsc_1080p_30, profiles/hdv_1080_25p,
+ profiles/hdv_1080_30p, profiles/hdv_720_50p, profiles/hdv_720_60p:
+ profiles/hdv_*, profiles/atsc_*: added more HD progressive mode profiles
+
+ * src/modules/oldfilm/filter_dust.yml: filter_dust.yml: apply description fix
+ patch from Mads Dydensborg.
+
+ * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: bugfix
+ segfault in construction with null argument.
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ detection of aspect ratio for DV AVI (applies to raw and quicktime files as
+ well).
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: rework the
+ aspect ratio detection to try to fetch it from the codec and/or the stream in
+ newer versions of ffmpeg. This fixes aspect handling for raw DV but still not
+ yet for DV AVIs without the vprp chunk.
+
+2008-11-07 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: fix build on BSD
+
+ * src/modules/core/transition_mix.c: transition_mix.c: prevent serializing
+ previous_mix and reset previous_mix on seeking.
+
+ * src/modules/normalize/filter_volume.c: filter_volume.c: prevent serializing
+ previous_gain and reset previous_gain on seeking.
+
+2008-11-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt: services.txt: minor corrections to documentation for
+ producer_avformat
+
+2008-11-05 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/Makefile, src/modules/kdenlive/factory.c,
+ src/modules/kdenlive/filter_freeze.c: kdenlive/filter_freeze.c: added simple
+ freeze filter
+
+2008-10-30 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/oldfilm/filter_vignette.c,
+ src/modules/oldfilm/filter_vignette.yml: oldfilm/filter_vignette*: filter is
+ now usable with keyframes
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: set min/max values in
+ metadata to defined min/max from frei0r.h
+
+ * src/modules/frei0r/frei0r_helper.c: frei0r/frei0r_helper.c: frei0r double
+ and bool params are now useable with keyframes (mlt_geometry)
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: yml files can be used now
+ for critical plugins
+
+2008-10-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * profiles/Makefile: profiles/Makefile: fix removal of turd (*~) files on
+ install.
+
+ * docs/TODO: docs/TODO: refer to wiki page
+
+ * Makefile: Makefile: suppress warning on ldconfig failure.
+
+2008-10-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/albino.c, src/inigo/inigo.c: albino.c, inigo.c: disable realtime
+ scheduling (kdenlive-180).
+
+2008-10-27 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: Fix crash /
+ corruption when changing audio or video index
+
+2008-10-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: optimize
+ slightly the stream index bugfix and update the video informational
+ properties on the producer when the video index changes.
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ regression with audio_index and video_index in last release when I added the
+ feature to close file on init with re-open on first use. Also, added some
+ exception handling around index values.
+
+ * src/modules/vmfx/filter_mono.c: filter_mono.c: cleanup code to made it more
+ consistent between cases (use_alpha).
+
+ * src/modules/vmfx/filter_mono.c: filter_mono.c: bugfix (kdenlive-234)
+ threshold filter inverting image and add invert property to revert to old
+ behavior.
+
+2008-10-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/modules/kino/endian_types.h, src/modules/kino/riff.cc,
+ src/modules/sox/configure: configure, kino/enadian_types.h, kino/riff.c,
+ sox/configure: apply patch from Alberto Villa to fix build on FreeBSD and to
+ fix a sh expression bug in sox/configure.
+
+2008-10-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c:
+ improve delimiter parsing to allow '?' in filename argument
+
+ * mlt-config-template: mlt-config-template: add deprecation warning
+
+ * src/modules/sox/filter_sox.c: filter_sox.c: bugfix recent build regression
+ on older versions of sox
+
+2008-10-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c: inigo.c: improve the usage help and add -silent and
+ -progress options
+
+ * src/modules/inigo/producer_inigo.c: producer_inigo.c: bugfix (2164436)
+ processing unknown command line options causes infinite loop
+
+ * src/inigo/Makefile, src/inigo/inigo.c: inigo.c: added -version option
+
+ * src/modules/sox/filter_sox.c: filter_sox.c: bugfix (2040035) segfault with
+ libsox 14.1.0
+
+ * configure: configure: -O3 is the maximum optimization level, not -O4
+
+2008-10-21 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: fix
+ deprecated warning on av_set_string
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: fix build on
+ older libavformat versions
+
+2008-10-20 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix Ogg
+ Vorbis files and possibly others that report invalid pts on some packets
+
+ * src/modules/xine/configure: xine/configure: disable module on ppc64
+
+ * src/modules/xine/configure: xine/configure: disable module on ppc64
+
+2008-10-08 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: Fix crash
+ introduced by FFmpeg revision 15367 (check that muxer and encoder have same
+ aspect ratio)
+
+2008-10-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ reading file over http.
+
+2008-09-22 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp:
+ producer_pixbuf.c, qimage_wrapper.c: Add "force_reload" option to force image
+ reloading in the image producers
+
+2008-09-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+ (2106941) compilation against recent ffmpeg changes
+
+2008-09-07 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/filehandler.cc: modules/kino/filehandler.cc: compilation
+ fix
+
+2008-08-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/configure, src/modules/sox/filter_sox.c: sox/configure,
+ filter_sox.c: fix building against sox 14.1.0.
+
+2008-08-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/modules/sdl/consumer_sdl.c: consumer_sdl.c: added support
+ for fullscreen with no mouse through the "fullscreen" property.
+
+2008-08-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure: bump versions for 0.3.0 release
+
+ * Makefile: improve make dist target
+
+ * AUTHORS: add AUTHORS file
+
+ * NEWS: Add release notes file
+
+ * demo/mlt_news: demo/mlt_news: small typo
+
+2008-08-05 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/producer_framebuffer.c:
+ kdenlive/producer_framebuffer.c: keep resource file in producer and use '?'
+ instead of ':' to separate filename from speed, because it caused some
+ problems with other MLT functions
+
+2008-08-03 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c: framework/mlt_playlist.c: check length before
+ inserting blank, which fixes one frame blanks that were sometimes inserted
+ where not needed.
+
+2008-07-31 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: use float values for
+ "double vars" in frei0r
+
+2008-07-28 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/configure: frei0r/configure: removed unneeded newlines
+
+2008-07-27 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: Fix
+ aspect ratio with slowmotion producer
+
+2008-07-24 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/configure: qimage/configure: Fix Qt3 detection and
+ compilation
+
+2008-07-22 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/producer_qimage.c,
+ src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+ qimage module: add mutex, fix caching and use alpha only if necessary (mostly
+ borrowed from producer_pixbuf)
+
+2008-07-14 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/configure: qimage/configure: Fix Qt4 detection
+
+2008-07-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+ recent regression with setting aspect ratio. Now it takes it from the profile
+ by default using the quotient properties for best accuracy. Now, one can also
+ override the aspect ratio using the same property name as the ffmpeg command
+ line utility ("aspect") for even greater symmetry.
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ segfault when fail to open or read file in init.
+
+2008-07-10 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/configure, src/modules/qimage/producer_qimage.c,
+ src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+ qimage module: add support for Qt4 (you can force compile against Qt3 with
+ --force-qt3)
+
+2008-07-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c: bugfix regression
+ with introduction of mlt_profile causing length of vorbis producer to always
+ yield zero.
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ regression playing audio-only files.
+
+2008-07-01 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/oldfilm/filter_vignette.c,
+ src/modules/oldfilm/filter_vignette.yml: oldfilm/filter_vignette.{c,yml}:
+ change format for parameters, to avoid converting problems with different
+ locales
+
+2008-06-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c, src/framework/mlt_service.c:
+ mlt_properties.c, mlt_service.c: bugfix to make reference counting and
+ service closure truly thread-safe. As it was, reference count increment and
+ decrement operations were not atomic and not protected comprehensively.
+
+ * src/framework/mlt_consumer.c: mlt_consumer.c: added ability to set priority
+ of the read-ahead thread through a new "priority" property. This only works
+ if you have permission; fails to execute properly otherwise - not sure how to
+ make it fail over gracefully. Do not set this property if you do not have
+ permission.
+
+2008-06-25 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_field.c: mlt_field.c: correctly reconnect transitions
+ after a service disconnect
+
+ * src/framework/mlt_service.c: mlt_service.c: fix bad identification for some
+ services (eg. transitions)
+
+2008-06-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ segfault up the call stack when no image could be decoded for a frame by
+ producing the "test card" image.
+
+2008-06-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c: mlt_playlist.c: return error on
+ mlt_playlist_get_clip_info if producer is null.
+
+2008-06-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik/producer_fezzik.c: producer_fezzik.c: let other services
+ prevent Fezzik from attaching filters by passing the "fezzik_normalised"
+ property.
+
+ * src/framework/mlt_repository.c: mlt_repository.c: bugfix memory leak on
+ getting directory list of MLT_REPOSITORY.
+
+2008-06-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c: mlt_consumer.c: make the realtime
+ frame-dropping heuristic based on actual frame rate instead of 25fps
+
+2008-06-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ segfault in unprotected libavcodec call that is clearly marked not thread
+ safe!
+
+2008-06-15 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: transition_composite.c: slightly
+ more accurate positioning when using crop panning and horizontal position is
+ adjusted to align chroma channels.
+
+2008-06-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: close the
+ file at the end of object creation, then re-open the file on-demand. This
+ presented a file descriptor limit issue when loading very large playlists.
+
+2008-06-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c: mlt_playlist.c: remove some unncessary and
+ inefficient accounting code.
+
+2008-06-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: transition_composite.c: add repeat
+ processing to crop property.
+
+ * src/modules/core/transition_composite.c: transition_composite.c: add
+ animatable geometry "pan" property. This suppresses implicit scaling of the
+ b-frame and makes the compositing rectangle crop. Then, it uses the x and y
+ geometry information to pan the b-frame within the composite rectangle. For
+ example, a negative x pans the image to the left and that portion of the b
+ frame left of the composite rectangle is cropped. w and h of the pan geometry
+ is not implemented at this time, but the plan is to implement scaling of the
+ b-frame. In the end, this can provide a Ken Burns effect for still images - a
+ much requested feature.
+
+2008-06-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: remove
+ multi-threaded audio decoding option. It does not provide any advantage at
+ this time and just wastes resources.
+
+ * src/framework/mlt_playlist.c: mlt_playlist.c: added an "autoclose" property
+ for sequential processing of very large playlists. If set, it automatically
+ closes previous producers to reduce resources (file handles and threads if
+ using producer_avformat with threads).
+
+2008-06-01 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pango.c: producer_pango.c: make the size property
+ an absolute height in pixels for greater compositing and quality control.
+
+ * src/modules/core/filter_resize.c, src/modules/core/transition_composite.c:
+ filter_resize,c, filter_composite.c: bugfix redundant rounding.
+
+ * src/modules/core/filter_watermark.c: filter_watermark.c: bugfix propogation
+ of output_ratio as a double - was causing incorrect calculations in
+ transition_composite.c with non-square-pixel watermark sources.
+
+ * src/framework/mlt_properties.c: mlt_properties.c: make arithmetic processor
+ use floating point instead of integer so that '/' is meaningful. I am not
+ totally certain of the consequences of this change because I am not aware of
+ where the feature is used. However, I am using it to specify the aspect ratio
+ of certain things like bitmap graphics that were not designed for square
+ pixels. And being able to specify a fraction allows for accurate detection of
+ equivalent aspect ratios between different sources, particularly compositing.
+
+ * src/framework/mlt_profile.c: mlt_profile.c: make fallback default sample
+ aspect ratio the same as the revised profile's sample aspect ratio
+
+2008-05-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: apply the in point
+ to the position in the image sequence
+
+2008-05-15 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: avformat/configure: fix compilation against
+ shared ffmpeg for a headers configuration that has appeared.
+
+ * profiles/dv_ntsc, profiles/dv_ntsc_wide, profiles/dv_pal,
+ profiles/dv_pal_wide, src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: profiles/dv_*, consumer_avformat.c,
+ producer_avformat.c: bugfix (1912796) to override FFmpeg notion of sample
+ aspect for DV. The values it uses might be more proper in certain contexts,
+ but not in the way MLT currently operates. This change improves performance
+ and quality when outputting to one of the "dv" profiles when using DV or
+ other ITU-R 601-based video sources such as MPEG-2 for DVD Video and
+ broadcast.
+
+2008-05-12 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_luma.c: Correctly update the luma file if the
+ resource was modified
+
+2008-05-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c:
+ avformat/configure: fix compiling against shared ffmpeg due to changes in
+ ffmpeg pkg-config
+
+2008-05-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_resize.c: filter_resize.c: fix field order
+ correction
+
+ * src/modules/xine/configure: xine/configure: relax restrictions to let
+ OSX/Intel use xine deinterlace
+
+ * src/framework/mlt_field.c, src/framework/mlt_field.h: mlt_field.[hc]: added
+ mlt_field_disconnect_service
+
+ * src/modules/dgraft/Makefile, src/modules/dgraft/factory.c,
+ src/modules/dgraft/filter_telecide.c: modules/dgraft: added module for ports
+ of Donald Graft's GPL filters.
+
+ * src/modules/sox/configure: sox/configure: make inclusion of libsfx dynamic
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ reporting of top_field_first on frame.
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure:
+ avformat/Makefile, configure: fix --avformat-swscale and the removal of the
+ ffmpeg 'lib' make target.
+
+ * src/modules/core/filter_data_show.c: filter_data_show.c: bugfix
+ interpreting timecode, due to invalid fps on mlt_profiles API changes
+
+2008-04-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c: filter_watermark.c,
+ filter_composite.c: support explicit deinterlace of composited image.
+
+2008-04-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/lumas/create_lumas: lumas/create_lumas: bugfix (1940387)
+ bash-ism in script
+
+ * configure, src/modules/motion_est/configure: configure,
+ motion_est/configure: remove module-specific crud from top-level configure
+ script, and enable motion_est now by default.
+
+ * src/modules/kino/avi.cc, src/modules/kino/filehandler.cc,
+ src/modules/kino/kino_wrapper.cc: kino/kino_wrapper.cc, kino/filehandler.cc,
+ kino/avi.cc: bugfix (1936991) compilation with gcc 4.3.
+
+2008-04-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix image
+ sequences
+
+2008-03-22 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/transition_frei0r.c:
+ frei0r/{frei0r_helper,transition_frei0r}.c: fixed wrong scaling and memory
+ leak
+
+2008-03-18 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: avformat/configure: improve chances of
+ successful linking with -svn and -static options
+
+2008-03-07 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/riff.cc: kino/riff.c: fix failure to dlopen due to symbol
+ signature mismatch on make_fourcc
+
+ * src/modules/frei0r/configure, src/modules/frei0r/factory.c:
+ frei0r/configure: use CFLAGS so I can tell the test where to find frei0r.h
+ frei0r/factory.c: add metadata_schema value to metadata
+
+ * src/framework/mlt_repository.c: mlt_repository.c: clear up warning due to
+ const return from getenv_locale()
+
+2008-03-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_repository.c: mlt_repository.c: fix to previous string
+ const fix in mlt_repository_languages
+
+ * src/framework/mlt_repository.c: mlt_repository.[hc]: fix modifying const
+ string in mlt_repository_languages
+
+ * src/framework/mlt_repository.c, src/framework/mlt_repository.h:
+ mlt_repository.[hc]: add mlt_repository_languages helper function for
+ localizing metadata
+
+2008-03-05 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/valerie/Makefile: src/valerie/Makefile: fix overwriting libmlt.0.dylib
+ on libvalerie install on OS X
+
+ * src/modules/sox/configure: sox/configure: add OS X and Debian (future?)
+ pkg-config support to sox configuration
+
+2008-03-04 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/factory.c: src/modules/frei0r/factory.c: load metadata
+ on request (thx for patch from Dan Dennedy) added "tags" metadata with type
+ "Video" for frei0r plugins
+
+ * src/modules/frei0r/factory.c: modules/frei0r/factory.c: also register
+ transitions, added "tags" to metadata
+
+ * src/modules/oldfilm/filter_vignette.c: oldfilm/filter_vignette.c: speedup
+
+ * src/modules/oldfilm/fdust.svg, src/modules/oldfilm/filter_dust.yml,
+ src/modules/oldfilm/filter_grain.yml, src/modules/oldfilm/filter_lines.yml,
+ src/modules/oldfilm/filter_oldfilm.yml,
+ src/modules/oldfilm/filter_tcolor.yml,
+ src/modules/oldfilm/filter_vignette.yml, src/modules/oldfilm/grain.svg,
+ src/modules/oldfilm/lines.svg, src/modules/oldfilm/oldfilm.svg,
+ src/modules/oldfilm/tcolor.svg, src/modules/oldfilm/vignette.svg:
+ modules/oldfilm: yml files without icon, icon as separate file
+
+2008-03-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/Makefile, src/modules/sox/configure: sox/configure,
+ Makefile: try to make sox build smarter about library dependencies (pending
+ Darwin compatibilty)
+
+ * src/framework/metaschema.yaml, src/modules/avformat/producer_avformat.yml:
+ metaschema.yaml, producer_avformat.yml: reset schema_version to 0.1 since we
+ have not release anything yet with schema let alone metadata
+
+ * src/modules/frei0r/factory.c: frei0r/factory.c: apply destructors and
+ serialiser to metadata mlt_properties
+
+ * src/inigo/inigo.c: inigo.c: fix querying on specific filter or transition
+
+2008-03-03 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_resize.c: filter_rescale.c: if input width or
+ height are zero, infer them from the profile
+
+2008-02-28 blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/frei0r/configure: test not only if header frei0r.h exists, also
+ use an item
+
+ * src/modules/frei0r/Makefile, src/modules/frei0r/configure,
+ src/modules/frei0r/factory.c, src/modules/frei0r/filter_frei0r.c,
+ src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h,
+ src/modules/frei0r/transition_frei0r.c: initial frei0r support
+
+ * src/modules/oldfilm/Makefile, src/modules/oldfilm/dust1.svg,
+ src/modules/oldfilm/dust2.svg, src/modules/oldfilm/dust3.svg,
+ src/modules/oldfilm/dust4.svg, src/modules/oldfilm/dust5.svg,
+ src/modules/oldfilm/factory.c, src/modules/oldfilm/filter_dust.c,
+ src/modules/oldfilm/filter_dust.yml, src/modules/oldfilm/filter_grain.c,
+ src/modules/oldfilm/filter_grain.yml, src/modules/oldfilm/filter_lines.c,
+ src/modules/oldfilm/filter_lines.yml, src/modules/oldfilm/filter_oldfilm.c,
+ src/modules/oldfilm/filter_oldfilm.yml, src/modules/oldfilm/filter_tcolor.c,
+ src/modules/oldfilm/filter_tcolor.yml, src/modules/oldfilm/filter_vignette.c,
+ src/modules/oldfilm/filter_vignette.yml: updated oldfilm module + 2 new
+ filters
+
+2008-02-28 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/Makefile, src/framework/metaschema.yaml,
+ src/modules/avformat/producer_avformat.yml: framework/Makefile,
+ metaschema.yaml: add a Kwalify schema for metadata producer_avformat.yml:
+ update to schema
+
+2008-02-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: avformat/Makefile: compilation fix for
+ latest FFmpeg update
+
+2008-02-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c: inigo.c: add -query option to inigo for service and
+ metadata lookup.
+
+ * src/modules/avformat/Makefile, src/modules/avformat/factory.c,
+ src/modules/avformat/producer_avformat.yml: avformat/factory.c,
+ producer_avformat.yml, avformat/Makefile: add metadata for producer:avformat.
+
+ * src/tests/Makefile, src/tests/dan.c: dan.c: example showing how to use the
+ new yaml parsing and serialisation and the new registry metadata system
+
+ * src/framework/mlt_properties.c, src/framework/mlt_properties.h:
+ mlt_properties.[hc]: added really simply YAML Tiny parser and serialiser,
+ mainly to support the registry metadata system.
+
+ * src/framework/mlt_repository.c, src/framework/mlt_repository.h:
+ mlt_repository.[hc]: implement the metadata registration and lookup interface
+
+2008-02-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/Makefile: sox/Makefile: helpful note for Ubuntu (and
+ Debian?)
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure:
+ avformat/configure, avformat/Makefile: add libavdevice for newer versions of
+ ffmpeg when using --avformat-svn or --avformat-static
+
+ * src/framework/mlt_repository.c: mlt_repository.c: throw warning on failure
+ to load module
+
+2008-02-16 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_repository.c, src/framework/mlt_repository.h:
+ mlt_consumer.[hc]: added new functions mlt_repository_consumers,
+ mlt_repository_filters, mlt_repository_producers, mlt_repository_transitions,
+ mlt_repository_register_metadata, and mlt_repository_metadata
+
+2008-02-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: avformat/configure: use pkg-config with
+ --avformat-shared
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: - Convert to
+ ffmpeg and AVOptions exclusively. This makes ALL AVOptions as seen from
+ \'ffmpeg -h\' available to MLT. Instead of ffmpeg\'s \'-option value\' use
+ inigo\'s property syntax \'option=value\" - Add dual pass encoding. - Use
+ multi-threading even with non-threaded codecs by separating producer and
+ consumer threads. - Whitespace cleanup.
+
+ * src/framework/mlt_consumer.c: mlt_consumer.c: let consumers use read-ahead
+ processing thread without frame dropping with real_time=-1
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: cleanup
+ whitespace
+
+2008-02-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: consumer_avformat.c,
+ producer_avformat.c: add FFmpeg multi-thread support via "threads" property
+ or MLT_AVFORMAT_THREADS environment variable
+
+2008-02-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/dv/producer_libdv.c: producer_libdv.c: fix test for framerate
+ matching profile
+
+2008-02-07 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/framework/Makefile, src/framework/mlt.h,
+ src/miracle/Makefile, src/valerie/Makefile: configure: add soversion
+ variable, move version variables to top for easier access framework/Makefile,
+ miracle/Makefile, valerie/Makefile: improve library versioning by linking on
+ interface version (soversion) mlt.h: add version info to header so apps can
+ have build time adaptations
+
+ * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_repository.c, src/framework/mlt_repository.h: cleanup some
+ names since we are changing the interface mlt_repository.[hc]: change
+ mlt_repository_fetch to mlt_repository_create mlt_factory.[hc]: change
+ mlt_factory_prefix to mlt_factory_directory
+
+2008-02-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt.h, src/framework/mlt_factory.c,
+ src/framework/mlt_factory.h, src/framework/mlt_properties.c,
+ src/framework/mlt_repository.c, src/framework/mlt_repository.h,
+ src/modules/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c, src/modules/configure,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/dv/configure, src/modules/dv/factory.c,
+ src/modules/effectv/configure, src/modules/effectv/factory.c,
+ src/modules/fezzik/configure, src/modules/fezzik/factory.c,
+ src/modules/gtk2/configure, src/modules/gtk2/factory.c,
+ src/modules/inigo/configure, src/modules/inigo/factory.c,
+ src/modules/inigo/producer_inigo.c, src/modules/jackrack/configure,
+ src/modules/jackrack/factory.c, src/modules/kdenlive/configure,
+ src/modules/kdenlive/factory.c, src/modules/kino/configure,
+ src/modules/kino/factory.c, src/modules/motion_est/configure,
+ src/modules/motion_est/factory.c, src/modules/normalize/configure,
+ src/modules/normalize/factory.c, src/modules/oldfilm/configure,
+ src/modules/oldfilm/factory.c, src/modules/plus/configure,
+ src/modules/plus/factory.c, src/modules/qimage/configure,
+ src/modules/qimage/factory.c, src/modules/resample/configure,
+ src/modules/resample/factory.c, src/modules/sdl/configure,
+ src/modules/sdl/factory.c, src/modules/sox/configure,
+ src/modules/sox/factory.c, src/modules/valerie/configure,
+ src/modules/valerie/factory.c, src/modules/vmfx/configure,
+ src/modules/vmfx/factory.c, src/modules/vorbis/configure,
+ src/modules/vorbis/factory.c, src/modules/westley/configure,
+ src/modules/westley/factory.c, src/modules/xine/configure,
+ src/modules/xine/factory.c: mlt_repository.[hc]: - dynamically locate and
+ register modules instead of reading .dat files - added
+ mlt_repository_register() and macros for modules and apps(!) to register
+ their service factory functions mlt_factory.[hc]: change mlt_factory_init()
+ to return mlt_repository to app mlt_properties.c: let
+ mlt_properties_dir_list() take a NULL filter pattern src/modules/*: - adapt
+ to new module registration system - much simpler! - remove unncessary
+ configure scripts (now optional!)
+
+2008-02-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/config.h: remove config.h
+
+ * Makefile, setenv, src/framework/Makefile, src/framework/mlt_consumer.c,
+ src/framework/mlt_factory.c, src/framework/mlt_filter.c,
+ src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_parser.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+ src/framework/mlt_property.c, src/framework/mlt_service.c,
+ src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+ src/modules/Makefile, src/modules/avformat/Makefile,
+ src/modules/avformat/configure, src/modules/core/Makefile,
+ src/modules/core/filter_data_show.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/dv/Makefile,
+ src/modules/effectv/Makefile, src/modules/feeds/Makefile,
+ src/modules/fezzik/Makefile, src/modules/fezzik/producer_fezzik.c,
+ src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+ src/modules/jackrack/Makefile, src/modules/kdenlive/Makefile,
+ src/modules/kino/Makefile, src/modules/lumas/Makefile,
+ src/modules/motion_est/Makefile, src/modules/normalize/Makefile,
+ src/modules/oldfilm/Makefile, src/modules/plus/Makefile,
+ src/modules/qimage/Makefile, src/modules/resample/Makefile,
+ src/modules/sdl/Makefile, src/modules/sox/Makefile,
+ src/modules/sox/configure, src/modules/valerie/Makefile,
+ src/modules/vmfx/Makefile, src/modules/vmfx/filter_shape.c,
+ src/modules/vorbis/Makefile, src/modules/westley/Makefile,
+ src/modules/xine/Makefile: move binary modules to libdir - affects
+ MLT_REPOSITORY added MLT_DATA environment variable to refer to share dir
+ remove need for config.h
+
+2008-02-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_factory.c, src/framework/mlt_profile.c: mlt_factory.c:
+ guard against accessing mlt_environment before it is ready mlt_profile.c: fix
+ setting legacy MLT_NORMALISATION on mlt_environment
+
+ * src/framework/mlt_factory.c, src/framework/mlt_profile.c: mlt_factory.c:
+ guard against setting mlt_environment before it is available mlt_profile.c:
+ use getenv instead of mlt_environment in case profile is created before
+ factory
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_filter.c, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/framework/mlt_geometry.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_profile.c,
+ src/framework/mlt_profile.h, src/framework/mlt_repository.c,
+ src/framework/mlt_repository.h, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_tractor.c, src/inigo/inigo.c,
+ src/miracle/miracle_connection.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit_commands.c,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/consumer_avformat.h, src/modules/avformat/factory.c,
+ src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avcolour_space.h,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/filter_avdeinterlace.h,
+ src/modules/avformat/filter_avresample.c,
+ src/modules/avformat/filter_avresample.h,
+ src/modules/avformat/producer_avformat.c,
+ src/modules/avformat/producer_avformat.h, src/modules/core/consumer_null.c,
+ src/modules/core/consumer_null.h, src/modules/core/factory.c,
+ src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.h,
+ src/modules/core/filter_channelcopy.c, src/modules/core/filter_channelcopy.h,
+ src/modules/core/filter_data.h, src/modules/core/filter_data_feed.c,
+ src/modules/core/filter_data_show.c, src/modules/core/filter_gamma.c,
+ src/modules/core/filter_gamma.h, src/modules/core/filter_greyscale.c,
+ src/modules/core/filter_greyscale.h, src/modules/core/filter_luma.c,
+ src/modules/core/filter_luma.h, src/modules/core/filter_mirror.c,
+ src/modules/core/filter_mirror.h, src/modules/core/filter_mono.c,
+ src/modules/core/filter_mono.h, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_obscure.h, src/modules/core/filter_region.c,
+ src/modules/core/filter_region.h, src/modules/core/filter_rescale.c,
+ src/modules/core/filter_rescale.h, src/modules/core/filter_resize.c,
+ src/modules/core/filter_resize.h, src/modules/core/filter_transition.c,
+ src/modules/core/filter_transition.h, src/modules/core/filter_watermark.c,
+ src/modules/core/filter_watermark.h, src/modules/core/producer_colour.c,
+ src/modules/core/producer_colour.h, src/modules/core/producer_noise.c,
+ src/modules/core/producer_noise.h, src/modules/core/producer_ppm.c,
+ src/modules/core/producer_ppm.h, src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h, src/modules/core/transition_luma.c,
+ src/modules/core/transition_luma.h, src/modules/core/transition_mix.c,
+ src/modules/core/transition_mix.h, src/modules/core/transition_region.c,
+ src/modules/core/transition_region.h, src/modules/dv/consumer_libdv.c,
+ src/modules/dv/consumer_libdv.h, src/modules/dv/factory.c,
+ src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.h,
+ src/modules/effectv/factory.c, src/modules/effectv/filter_burn.c,
+ src/modules/effectv/filter_burn.h, src/modules/fezzik/factory.c,
+ src/modules/fezzik/producer_fezzik.c, src/modules/fezzik/producer_fezzik.h,
+ src/modules/fezzik/producer_hold.c, src/modules/fezzik/producer_hold.h,
+ src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/consumer_gtk2.h,
+ src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/filter_rescale.h, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pango.h, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/gtk2/producer_pixbuf.h, src/modules/inigo/factory.c,
+ src/modules/inigo/producer_inigo.c, src/modules/inigo/producer_inigo.h,
+ src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.c,
+ src/modules/jackrack/filter_jackrack.h, src/modules/jackrack/filter_ladspa.c,
+ src/modules/jackrack/filter_ladspa.h, src/modules/kdenlive/factory.c,
+ src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.h,
+ src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/filter_wave.h,
+ src/modules/kdenlive/producer_framebuffer.c,
+ src/modules/kdenlive/producer_framebuffer.h, src/modules/kino/factory.c,
+ src/modules/kino/producer_kino.c, src/modules/kino/producer_kino.h,
+ src/modules/motion_est/factory.c,
+ .../motion_est/filter_autotrack_rectangle.c,
+ src/modules/motion_est/filter_crop_detect.c,
+ src/modules/motion_est/filter_motion_est.c,
+ src/modules/motion_est/filter_motion_est.h,
+ src/modules/motion_est/filter_vismv.c,
+ src/modules/motion_est/producer_slowmotion.c,
+ src/modules/normalize/factory.c, src/modules/normalize/filter_volume.c,
+ src/modules/normalize/filter_volume.h, src/modules/oldfilm/factory.c,
+ src/modules/oldfilm/filter_dust.c, src/modules/oldfilm/filter_dust.h,
+ src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_grain.h,
+ src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_lines.h,
+ src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_oldfilm.h,
+ src/modules/plus/factory.c, src/modules/plus/filter_affine.c,
+ src/modules/plus/filter_affine.h, src/modules/plus/filter_charcoal.c,
+ src/modules/plus/filter_charcoal.h, src/modules/plus/filter_invert.c,
+ src/modules/plus/filter_invert.h, src/modules/plus/filter_sepia.c,
+ src/modules/plus/filter_sepia.h, src/modules/plus/transition_affine.c,
+ src/modules/plus/transition_affine.h, src/modules/qimage/factory.c,
+ src/modules/qimage/producer_qimage.c, src/modules/qimage/producer_qimage.h,
+ src/modules/qimage/qimage_wrapper.cpp, src/modules/resample/factory.c,
+ src/modules/resample/filter_resample.c,
+ src/modules/resample/filter_resample.h, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl.h, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c, src/modules/sdl/factory.c,
+ src/modules/sdl/producer_sdl_image.c, src/modules/sdl/producer_sdl_image.h,
+ src/modules/sox/configure, src/modules/sox/factory.c,
+ src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.h,
+ src/modules/valerie/consumer_valerie.c,
+ src/modules/valerie/consumer_valerie.h, src/modules/valerie/factory.c,
+ src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c,
+ src/modules/vmfx/filter_chroma.h, src/modules/vmfx/filter_chroma_hold.c,
+ src/modules/vmfx/filter_chroma_hold.h, src/modules/vmfx/filter_mono.c,
+ src/modules/vmfx/filter_mono.h, src/modules/vmfx/filter_shape.c,
+ src/modules/vmfx/filter_shape.h, src/modules/vmfx/producer_pgm.c,
+ src/modules/vmfx/producer_pgm.h, src/modules/vorbis/factory.c,
+ src/modules/vorbis/producer_vorbis.c, src/modules/vorbis/producer_vorbis.h,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/consumer_westley.h, src/modules/westley/factory.c,
+ src/modules/westley/producer_westley.c,
+ src/modules/westley/producer_westley.h, src/modules/xine/factory.c,
+ src/modules/xine/filter_deinterlace.c, src/modules/xine/filter_deinterlace.h,
+ src/valerie/valerie_remote.c: framework: remove global profile, rather share
+ one mlt_profile across a service network and make it available from anywhere
+ through mlt_service_profile(). miracle, valerie: profile changes inigo: added
+ -profile and progress=1 to mimic kdenlive_renderer modules: profile changes.
+ Since nearly every file was touched, remove superfluous headers and prepare
+ for coming mlt_repository change.
+
+2008-01-20 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+ (kdenlive-28) a/v sync on non-whole frame rate.
+
+2008-01-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: do not free
+ AVPacket if av_read_frame fails.
+
+2008-01-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/oldfilm/Makefile, src/modules/oldfilm/configure,
+ src/modules/oldfilm/factory.c, src/modules/oldfilm/filter_dust.c,
+ src/modules/oldfilm/filter_dust.h, src/modules/oldfilm/filter_grain.c,
+ src/modules/oldfilm/filter_grain.h, src/modules/oldfilm/filter_lines.c,
+ src/modules/oldfilm/filter_lines.h, src/modules/oldfilm/filter_oldfilm.c,
+ src/modules/oldfilm/filter_oldfilm.h: src/modules/oldfilm/*: add oldfilm
+ module contributed by Marco Gittler
+
+ * docs/services.txt: minor typo fix
+
+ * src/framework/mlt_playlist.c: mlt_playlist.c: fix some blank-handling bugs
+ in mlt_playlist_insert_at()
+
+2007-12-18 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict: fezzik.dict: prioritize avformat over vorbis
+ module for .ogg, at least until better track type detection is in place.
+
+2007-12-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: make
+ compilation fix on url_fclose version sensitive to support older ffmpeg
+
+2007-12-08 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * .../motion_est/filter_autotrack_rectangle.c: Autotrack rectangle can now be
+ defined using geometry="x,y:wxh" instead of having to pass it in the filter
+ name
+
+2007-12-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure, src/modules/sox/configure: sox/configure:
+ remove libsamplerate from linking by default
+
+2007-12-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+ src/modules/core/filter_data_show.c, src/modules/dv/producer_libdv.c,
+ src/modules/inigo/producer_inigo.c, src/modules/vorbis/producer_vorbis.c,
+ src/modules/westley/producer_westley.c: mlt_consumer.c, mlt_frame.c,
+ mlt_multitrack.c, mlt_playlist.c, mlt_producer.c, producer_avformat.c,
+ filter_data_show.c, producer_libdv.c, producer_inigo.c, producer_vorbis.c,
+ producer_westley.c: remove statefulness of frame rate through framework and
+ modules, and allow consumer properties to override profile settings.
+
+ * src/modules/sdl/producer_sdl_image.c: producer_sdl_image.c: fix compilation
+ warning with respect to const pointer
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: fix pointer
+ passed to url_fclose()
+
+ * src/modules/kino/riff.h: kino/riff.h: fix compiler warnings on missing
+ const for char*
+
+2007-11-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/Makefile, src/modules/sox/configure,
+ src/modules/sox/filter_sox.c: filter_sox.c, src/modules/sox/Makefile,
+ src/modules/sox/configure: add support for sox v14.0.0.
+
+2007-10-19 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_server.c, src/miracle/miracle_unit.c,
+ src/modules/avformat/factory.c, src/modules/gtk2/pixops.c,
+ src/modules/gtk2/producer_pango.c, src/modules/jackrack/jack_rack.c,
+ src/modules/jackrack/plugin_settings.c, src/modules/kdenlive/filter_wave.c,
+ src/modules/plus/transition_affine.c, src/modules/vmfx/filter_chroma.c,
+ src/modules/vorbis/producer_vorbis.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: avformat/factory.c,
+ jackrack/jack_rack.c, jackrack/plugin_settings.c, vmfx/filter_chroma.c,
+ plus/transition_affine.c, westley/producer_westley.c,
+ westley/consumer_westley.c, kdenlive/filter_wave.c, vorbis/producer_vorbis.c,
+ gtk2/producer_pango.c, gtk2/pixops.c, miracle_server.c, miracle_unit.c:
+ cleanup a whole bunch of compiler warnings
+
+ * src/modules/jackrack/configure: jackrack/configure: add detection for
+ ladspa and disable if not detected
+
+ * src/modules/core/filter_luma.c: filter_luma.c: bugfix testing b_frame's
+ dimensions
+
+ * src/modules/core/filter_resize.c: filter_resize.c: bugfix overriding
+ top_field_first property
+
+ * src/modules/motion_est/filter_motion_est.c,
+ src/modules/motion_est/filter_vismv.c: filter_vismv.c: bugfix pointer to
+ array of motion vectors
+
+ * src/modules/avformat/configure: avformat/configure: fix detect shared
+ install of libavformat due to link to versioned .so.
+
+2007-10-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_factory.c, src/framework/mlt_profile.c: mlt_profile.c,
+ mlt_factory.c: bugfix loading profile by file specification and remove a
+ small memory leak
+
+ * setenv, src/framework/mlt_profile.c: mlt_profle.c: add support for
+ MLT_PROFILES_DIR environment variable
+
+ * src/modules/sdl/consumer_sdl.c: consumer_sdl.c: fix specifying window size
+ on constructor arg
+
+ * src/modules/effectv/utils.c, src/modules/effectv/utils.h: effectv/utils.*:
+ fix compilation on OS X
+
+2007-08-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_still.c: consumer_sdl_still.c: bugfix segfault
+
+2007-08-03 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_still.c: consumer_sdl_still.c: bugfix
+ initialisation of window dimensions due to recent profiles addition
+
+2007-07-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, docs/policies.txt: configure: fix broken variables in pkg-config
+ files policies.txt: add bug reporting procedure
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: align some
+ defaults with ffmpeg for more reliable output
+
+2007-07-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_profile.c: mlt_profile.c:
+ bugfix string allocation length mlt_consumer.c: bugfix removal of
+ property-changed listener
+
+2007-07-20 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * profiles/Makefile: profiles/Makefile: cleanup profiles dir on (un)install
+
+ * profiles/atsc_1080i_60, profiles/atsc_720p_30, profiles/atsc_wide_1080i,
+ profiles/atsc_wide_720p: profiles/atsc_*: rename and change descriptions
+
+ * profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_720_25p,
+ profiles/hdv_720_30p:
+
+ * profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_1080_ntsc,
+ profiles/hdv_1080_pal, profiles/hdv_720_30p, profiles/hdv_720_60i:
+
+ * profiles/hdv_720_25p, profiles/hdv_720_50p:
+
+ * profiles/hdv_720_50p, profiles/hdv_720_60i, profiles/hdv_720_ntsc,
+ profiles/hdv_720_pal:
+
+ * src/framework/mlt_profile.c: mlt_profile.c: revise substrings for legacy
+ setting of MLT_NORMALISATION
+
+ * profiles/atsc_wide_1080i, profiles/atsc_wide_720p, profiles/cif_ntsc,
+ profiles/cif_pal, profiles/cvd_ntsc, profiles/cvd_pal, profiles/dv_ntsc,
+ profiles/dv_ntsc_wide, profiles/dv_pal, profiles/dv_pal_wide,
+ profiles/hdv_1080_ntsc, profiles/hdv_1080_pal, profiles/hdv_720_ntsc,
+ profiles/hdv_720_pal, profiles/qcif_ntsc, profiles/qcif_pal,
+ profiles/quarter_ntsc, profiles/quarter_ntsc_wide, profiles/quarter_pal,
+ profiles/quarter_pal_wide, profiles/square_ntsc, profiles/square_ntsc_wide,
+ profiles/square_pal, profiles/square_pal_wide, profiles/svcd_ntsc,
+ profiles/svcd_ntsc_wide, profiles/svcd_pal, profiles/svcd_pal_wide,
+ profiles/vcd_ntsc, profiles/vcd_pal, src/framework/mlt_factory.c,
+ src/framework/mlt_factory.h, src/framework/mlt_profile.c,
+ src/framework/mlt_profile.h: profiles/*: name->description
+ mlt_factory.{h,cc}: added mlt_environment_set() mlt_profile.{h,cc}: fix
+ setting legacy MLT_NORMALISATION, set MLT_PROFILE, and change "name" to
+ "description" for clarity
+
+2007-07-15 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure:
+ avformat/configure: add --avformat-svn-extra avformat/Makefile: rebuild
+ module when local ffmpeg changes
+
+ * profiles/Makefile, profiles/square_pal_wide: profiles/Makefile: do not
+ install Makefile profiles/square_pal_wide: fix display aspect
+
+ * ChangeLog, Makefile, configure, profiles/Makefile,
+ profiles/atsc_wide_1080i, profiles/atsc_wide_720p, profiles/cif_ntsc,
+ profiles/cif_pal, profiles/cvd_ntsc, profiles/cvd_pal, profiles/dv_ntsc,
+ profiles/dv_ntsc_wide, profiles/dv_pal, profiles/dv_pal_wide,
+ profiles/hdv_1080_ntsc, profiles/hdv_1080_pal, profiles/hdv_720_ntsc,
+ profiles/hdv_720_pal, profiles/qcif_ntsc, profiles/qcif_pal,
+ profiles/quarter_ntsc, profiles/quarter_ntsc_wide, profiles/quarter_pal,
+ profiles/quarter_pal_wide, profiles/square_ntsc, profiles/square_ntsc_wide,
+ profiles/square_pal, profiles/square_pal_wide, profiles/svcd_ntsc,
+ profiles/svcd_ntsc_wide, profiles/svcd_pal, profiles/svcd_pal_wide,
+ profiles/vcd_ntsc, profiles/vcd_pal, src/framework/Makefile,
+ src/framework/mlt.h, src/framework/mlt_consumer.c,
+ src/framework/mlt_factory.c, src/framework/mlt_frame.c,
+ src/framework/mlt_geometry.c, src/framework/mlt_producer.c,
+ src/framework/mlt_profile.c, src/framework/mlt_profile.h,
+ src/framework/mlt_types.h, src/modules/dv/consumer_libdv.c,
+ src/modules/sdl/consumer_sdl.c: Added new profiles system: mlt_profile,
+ MLT_PROFILE, and profiles documents.
+
+2007-07-14 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: consumer_avformat.c: save disabled,
+ experimental flushing code
+
+2007-07-07 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/configure: Fix build based on patch from Ryan Hodge
+
+2007-07-01 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict, src/modules/qimage/Makefile,
+ src/modules/qimage/configure, src/modules/qimage/producer_qimage.c,
+ src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+ Add support for psd, xcf and exr images (KDE libraries needed for these
+ formats). Make pcx and tiff images load correctly
+
+ * src/modules/gtk2/producer_pixbuf.c: Fix for rgba images (based on the code
+ from qimage_producer)
+
+ * src/modules/kdenlive/producer_framebuffer.c: Fix get image for formats
+ different from yuv422
+
+2007-07-01 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: improve
+ frame accuracy
+
+2007-06-30 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/producer_framebuffer.c: Better fix for aspect_ratio
+ problem in framebuffer producer
+
+ * src/modules/kdenlive/producer_framebuffer.c: Fix aspect ratio for
+ slowmotion / freeze effect
+
+ * src/modules/kdenlive/configure: Fix typo which prevented wave filter to be
+ available
+
+2007-06-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/circle.png: demo/circle.png: fix broken image
+
+ * demo/watermark1.png: watermark1.png: fix broken image
+
+ * demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark,
+ demo/mlt_voiceover: demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark,
+ demo/mlt_voiceover: fix broken demos due to recent hidden track handling
+ change in mlt_transition.c
+
+2007-06-28 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+ initial buffer size to prevent high quantization at beginning
+
+2007-06-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix audio
+ sync with some codecs and revert unnecessary precautions that introduce
+ inefficiency
+
+2007-06-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile: added dist make targets
+
+ * Makefile, src/albino/Makefile, src/framework/Makefile,
+ src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+ src/modules/Makefile, src/valerie/Makefile: added uninstall make targets
+
+2007-06-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/effectv/Makefile, src/modules/effectv/configure,
+ src/modules/effectv/factory.c, src/modules/effectv/filter_burn.c,
+ src/modules/effectv/filter_burn.h, src/modules/effectv/image.c,
+ src/modules/effectv/utils.c, src/modules/effectv/utils.h: added effectv
+ module with BurningTV filter provided by Stephane Fillod
+
+ * src/framework/mlt_frame.c: mlt_frame.c: let image conversions accept NULL
+ for the alpha parameter
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+ segfault when paused after seeking but no picture available to duplicate
+
+ * docs/westley.txt, src/modules/fezzik.dict: fezzik.dict: prioritize avformat
+ higher than libdv for better quality
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: make better
+ test for existence for avcodec_decode_audio2
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: fix setting
+ int property as double
+
+ * src/modules/avformat/producer_avformat.c: producer_avformat.c: - remove
+ seeking immediately after opening file improves compatibility (in particular,
+ ogg theora) - use non-deprecated avcodec_decode_audio2 if available - changes
+ to adhere to warnings on ffmpeg decode api docs ought to improve stability
+ and compatibility
+
+ * src/modules/avformat/consumer_avformat.c: added support for ilme=1 and
+ ildct=1 properties to consumer_avformat
+
+2007-06-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure: --avformat-swscale with --avformat-svn is
+ only permitted with --enable-gpl
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure: change
+ --avformat-svn configure option to do a static build of ffmpeg libs only and
+ statically link to mlt module. Also, make --avformat-svn aware of
+ --avformat-swscale and --enable-gpl
+
+2007-06-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_rescale.c: bugfix core/filter_rescale segfault on
+ scaling alpha that was already to correct scale (e.g. mlt_bouncy_ball)
+
+2007-06-01 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_rescale.c: bugfix segfault in core/filter_rescale
+ scaling alpha already scaled in gtk2/filter_rescale
+
+ * src/framework/mlt_tractor.c: bugfix tractor not propogating resize_alpha
+ frame property
+
+ * src/framework/mlt_transition.c: bugfix transition processing hidden track
+
+2007-05-31 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/producer_framebuffer.c: Fix framebuffer crash & clip
+ duration error
+
+2007-05-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/dv/consumer_libdv.c: per jb's suggestion, enable
+ terminate_on_pause by default
+
+ * demo/README, demo/mlt_attributes, demo/mlt_intro, demo/mlt_jcut,
+ demo/mlt_lcut, docs/inigo.txt: fix some demos broken by old changes
+
+2007-05-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_data_show.c: fix dynamic attribute value parsing
+ and memory management in data_show
+
+2007-05-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_factory.c, src/framework/mlt_producer.c,
+ src/modules/fezzik.ini: the framework may not depend upon specific
+ modules--data_feed/show in this case
+
+ * src/modules/core/filter_rescale.c: Only scale the alpha when also scaling
+ the image.
+
+2007-04-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kdenlive/filter_wave.c: compilation fix
+
+ * src/modules/avformat/configure: fix compilation without swscale
+
+ * ChangeLog, docs/policies.txt, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_boxblur.c, src/modules/core/filter_boxblur.h,
+ src/modules/core/filter_wave.c, src/modules/core/filter_wave.h,
+ src/modules/core/producer_framebuffer.c,
+ src/modules/core/producer_framebuffer.h, src/modules/core/transition_luma.c,
+ src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h,
+ src/modules/jackrack/jack_rack.c, src/modules/jackrack/jack_rack.h,
+ src/modules/jackrack/lock_free_fifo.c, src/modules/jackrack/lock_free_fifo.h,
+ src/modules/jackrack/plugin.c, src/modules/jackrack/plugin.h,
+ src/modules/jackrack/plugin_desc.c, src/modules/jackrack/plugin_desc.h,
+ src/modules/jackrack/plugin_mgr.c, src/modules/jackrack/plugin_mgr.h,
+ src/modules/jackrack/plugin_settings.c,
+ src/modules/jackrack/plugin_settings.h, src/modules/jackrack/process.c,
+ src/modules/jackrack/process.h, src/modules/kdenlive/Makefile,
+ src/modules/kdenlive/configure, src/modules/kdenlive/factory.c,
+ src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.h,
+ src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/filter_wave.h,
+ src/modules/kdenlive/producer_framebuffer.c,
+ src/modules/kdenlive/producer_framebuffer.h,
+ src/modules/normalize/filter_volume.c, src/modules/xine/filter_deinterlace.c:
+ Cleanup copyrights and attributions, and move Jean-Baptiste's services to a
+ new kdenlive module.
+
+2007-03-31 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/producer_framebuffer.c: Fixed crash in slowmotion producer
+
+
+2007-03-31 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, src/modules/sox/filter_sox.c: add sox 13.0.0 support
+
+2007-03-31 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/producer_framebuffer.c: Fix slowmotion producer (no more
+ variable speed, but at least it works now).
+
+2007-03-30 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, src/modules/core/filter_boxblur.c,
+ src/modules/core/filter_boxblur.h, src/modules/core/filter_wave.c,
+ src/modules/core/filter_wave.h: Update ChangeLog and fix license for blur and
+ wave filters
+
+2007-03-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, src/modules/vmfx/configure, src/modules/vmfx/factory.c: Change
+ registration of vmfx/mono to threshold to disambiguate with core/mono.
+
+ * ChangeLog, GPL, README, configure, docs/install.txt, docs/policies.txt,
+ docs/services.txt, docs/testing-20040110.txt, src/albino/albino.c,
+ src/framework/mlt.h, src/framework/mlt_consumer.c,
+ src/framework/mlt_consumer.h, src/framework/mlt_deque.c,
+ src/framework/mlt_deque.h, src/framework/mlt_events.c,
+ src/framework/mlt_events.h, src/framework/mlt_factory.c,
+ src/framework/mlt_factory.h, src/framework/mlt_field.c,
+ src/framework/mlt_field.h, src/framework/mlt_filter.c,
+ src/framework/mlt_filter.h, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/framework/mlt_geometry.c,
+ src/framework/mlt_geometry.h, src/framework/mlt_multitrack.c,
+ src/framework/mlt_multitrack.h, src/framework/mlt_parser.c,
+ src/framework/mlt_parser.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_pool.c,
+ src/framework/mlt_pool.h, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/framework/mlt_property.c,
+ src/framework/mlt_property.h, src/framework/mlt_repository.c,
+ src/framework/mlt_repository.h, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+ src/framework/mlt_tractor.h, src/framework/mlt_transition.c,
+ src/framework/mlt_transition.h, src/framework/mlt_types.h,
+ src/humperdink/client.c, src/humperdink/client.h, src/humperdink/io.c,
+ src/humperdink/io.h, src/humperdink/remote.c, src/inigo/inigo.c,
+ src/inigo/io.c, src/inigo/io.h, src/miracle/miracle.c,
+ src/miracle/miracle_local.h, src/miracle/miracle_server.c,
+ src/miracle/miracle_server.h, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/consumer_avformat.h, src/modules/avformat/factory.c,
+ src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avcolour_space.h,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/filter_avdeinterlace.h,
+ src/modules/avformat/filter_avresample.c,
+ src/modules/avformat/filter_avresample.h,
+ src/modules/avformat/producer_avformat.c,
+ src/modules/avformat/producer_avformat.h, src/modules/core/consumer_null.c,
+ src/modules/core/consumer_null.h, src/modules/core/factory.c,
+ src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.h,
+ src/modules/core/filter_channelcopy.c, src/modules/core/filter_channelcopy.h,
+ src/modules/core/filter_data.h, src/modules/core/filter_data_feed.c,
+ src/modules/core/filter_data_show.c, src/modules/core/filter_gamma.c,
+ src/modules/core/filter_gamma.h, src/modules/core/filter_greyscale.c,
+ src/modules/core/filter_greyscale.h, src/modules/core/filter_luma.c,
+ src/modules/core/filter_luma.h, src/modules/core/filter_mirror.c,
+ src/modules/core/filter_mirror.h, src/modules/core/filter_mono.c,
+ src/modules/core/filter_mono.h, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_obscure.h, src/modules/core/filter_region.c,
+ src/modules/core/filter_region.h, src/modules/core/filter_rescale.c,
+ src/modules/core/filter_rescale.h, src/modules/core/filter_resize.c,
+ src/modules/core/filter_resize.h, src/modules/core/filter_transition.c,
+ src/modules/core/filter_transition.h, src/modules/core/filter_watermark.c,
+ src/modules/core/filter_watermark.h, src/modules/core/producer_colour.c,
+ src/modules/core/producer_colour.h, src/modules/core/producer_noise.c,
+ src/modules/core/producer_noise.h, src/modules/core/producer_ppm.c,
+ src/modules/core/producer_ppm.h, src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h, src/modules/core/transition_luma.c,
+ src/modules/core/transition_luma.h, src/modules/core/transition_mix.c,
+ src/modules/core/transition_mix.h, src/modules/core/transition_region.c,
+ src/modules/core/transition_region.h, src/modules/dv/consumer_libdv.c,
+ src/modules/dv/consumer_libdv.h, src/modules/dv/factory.c,
+ src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.h,
+ src/modules/fezzik/factory.c, src/modules/fezzik/producer_fezzik.c,
+ src/modules/fezzik/producer_fezzik.h, src/modules/fezzik/producer_hold.c,
+ src/modules/fezzik/producer_hold.h, src/modules/gtk2/consumer_gtk2.c,
+ src/modules/gtk2/consumer_gtk2.h, src/modules/gtk2/factory.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/gtk2/filter_rescale.h,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.h,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+ src/modules/gtk2/scale_line_22_yuv_mmx.S, src/modules/inigo/factory.c,
+ src/modules/inigo/producer_inigo.c, src/modules/inigo/producer_inigo.h,
+ src/modules/lumas/luma.c, src/modules/plus/factory.c,
+ src/modules/plus/filter_affine.c, src/modules/plus/filter_affine.h,
+ src/modules/plus/filter_charcoal.c, src/modules/plus/filter_charcoal.h,
+ src/modules/plus/filter_invert.c, src/modules/plus/filter_invert.h,
+ src/modules/plus/filter_sepia.c, src/modules/plus/filter_sepia.h,
+ src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.h,
+ src/modules/qimage/producer_qimage.c, src/modules/qimage/qimage_wrapper.cpp,
+ src/modules/qimage/qimage_wrapper.h, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl.h, src/modules/sdl/consumer_sdl_osx_hack.h,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c,
+ src/modules/sdl/factory.c, src/modules/sdl/producer_sdl_image.c,
+ src/modules/sdl/producer_sdl_image.h, src/modules/sox/factory.c,
+ src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.h,
+ src/modules/valerie/consumer_valerie.c,
+ src/modules/valerie/consumer_valerie.h, src/modules/valerie/factory.c,
+ src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.c,
+ src/modules/vorbis/producer_vorbis.h, src/modules/westley/consumer_westley.c,
+ src/modules/westley/consumer_westley.h, src/modules/westley/factory.c,
+ src/modules/westley/producer_westley.c,
+ src/modules/westley/producer_westley.h, src/valerie/valerie.h: Cleanup
+ license declarations and remove dv1394d references.
+
+2007-03-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, src/modules/avformat/Makefile, src/modules/avformat/configure:
+ fixup some swscale integration
+
+2007-03-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, docs/TODO, docs/policies.txt: added docs/policies.txt
+
+2007-03-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/producer_avformat.c: add support for ffmpeg libswscale
+
+ * demo/README, demo/consumers.ini: change default dv1394 device file
+
+ * configure: remove bashisms
+
+2007-03-02 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c: Allow user to choose video driver
+ and output display
+
+2007-02-19 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_boxblur.c, src/modules/core/filter_boxblur.h,
+ src/modules/core/filter_wave.c, src/modules/core/filter_wave.h: Fix typo,
+ credits and make functions static, (patch from stephane fillod - thanks)
+
+2007-02-18 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_boxblur.c,
+ src/modules/core/filter_boxblur.h, src/modules/core/filter_wave.c,
+ src/modules/core/filter_wave.h: Add blur and wave filters from Leny Grisel
+
+2007-02-01 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c: Allow user to set alsa device
+
+2007-01-23 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_data_show.c: Allow display of metadata and timecode
+
+
+2007-01-22 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: Write metadata if there is any
+
+2007-01-19 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c: Fix my terribly broken YUV to RGB conversion
+
+2007-01-13 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c: Allow changing volume in
+ sdl_preview consumer
+
+2007-01-02 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: Change default value for
+ libavformat's qscale, preventing some crashes
+
+2006-12-31 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c,
+ src/modules/vorbis/producer_vorbis.c: Read metadata from avformat and vorbis
+ producers, using basic structure like:
+ meta.attr.metadata_name.markup=metadata_value
+
+ * src/modules/vorbis/producer_vorbis.c: Vorbis should set correct values in
+ frame for audio channels and frequency.
+
+2006-12-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, configure, src/framework/mlt_consumer.h,
+ src/framework/mlt_filter.h, src/framework/mlt_frame.h,
+ src/framework/mlt_geometry.h, src/framework/mlt_multitrack.h,
+ src/framework/mlt_producer.h, src/framework/mlt_service.h,
+ src/framework/mlt_transition.h: Applied patch from Stephane Fillod to make
+ configure run with bash since it uses bash-specific features. Also, patches
+ headers to comments for pedantic compilation.
+
+2006-11-20 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/producer_framebuffer.c: remove debug msg
+
+2006-11-18 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/producer_framebuffer.c,
+ src/modules/core/producer_framebuffer.h: Fix header + add freeze feature
+
+ * src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/producer_framebuffer.c,
+ src/modules/core/producer_framebuffer.h: New framebuffer producer. Provides
+ slowmotion, reverse playing and stroboscope effect
+
+2006-11-05 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict: Kdenlive project files are now westley compatible
+
+ * src/modules/core/transition_luma.c: Luma get_image produces yuv only, so
+ announce it. Fix problem when requesting rgb image of a luma transition.
+
+2006-10-26 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_rescale.c: Fix rescaling of rgb images when not
+ using gtk2
+
+2006-10-16 j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_obscure.c: Position for the effect was not
+ calculated right if the clip was in the middle of a playlist
+
+2006-10-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: + General improved media support
+
+2006-10-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: + Correction to previous patch -
+ fixes pause behaviour with rawvideo
+
+ * src/modules/avformat/producer_avformat.c: + Corrections for uncompressed
+ video sources
+
+2006-09-28 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog: Following Dan's example. Applied an amd64 compilation patch to
+ motion_est module and patch to correctly initialize audio frequency and
+ channels.
+
+ * src/modules/avformat/producer_avformat.c, src/modules/dv/producer_libdv.c:
+ Patch supplied by Jean-Baptiste.
+
+
+
+ * src/modules/motion_est/filter_motion_est.c,
+ src/modules/motion_est/sad_sse.h: Zypher's amd64 patch.
+ http://sources.gentoo.org/viewcvs.py/gentoo-x86/media-libs/mlt/files/
+
+2006-09-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog, src/modules/sdl/Makefile: fix SDL compilation on some systems
+ using modular x.org
+
+2006-08-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vmfx/filter_mono.h: + Header file for mono filter
+
+ * src/modules/vmfx/Makefile, src/modules/vmfx/configure,
+ src/modules/vmfx/factory.c, src/modules/vmfx/filter_mono.c: + A mono filter
+ for mask generation (not v. useful)
+
+ * src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c: +
+ Correction to uneven chroma samples
+
+ * src/modules/qimage/qimage_wrapper.cpp: + Image caching for the qimage
+ producer
+
+ * src/modules/gtk2/producer_pixbuf.c: + Image caching for the gtk2 pixbuf
+ producer
+
+2006-08-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * ChangeLog: *** empty log message ***
+
+ * src/modules/westley/producer_westley.c: enhance producer_westley to parse
+ Kino 0.9.1 SMIL (clock) time values.
+
+ * ChangeLog: *** empty log message ***
+
+ * src/modules/avformat/configure: convert --avformat-cvs to svn and rename
+ option as --avformat-svn (--avformat-cvs is an undocumented alias).
+
+2006-05-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure: bump version
+
+2006-05-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/producer_qimage.c: apply patch from Jean-Baptiste
+ <jb@ader.ch> to add rgb24a support to producer_qimage
+
+2006-05-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: apply patch from Jean Baptiste
+ <jb@ader.ch> to fix fill-type rescaling when aspect ratio is equal to
+ normalised ratio
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/modules/gtk2/producer_pixbuf.c: apply patch from Jean Baptiste to add
+ rgb24a support to producer_pixbuf
+
+2006-05-20 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/configure: let QTDIR also define location of qt include
+ dir
+
+ * src/modules/kino/filehandler.cc: fix compilation on latest version of
+ libquicktime (0.9.8)
+
+2006-05-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/filter_avcolour_space.c: + Big endian patch courtesy
+ of Goncalo Carvalho (glslang at gmail dot com) - specifically, corrects
+ colour space conversions on the Intel Mac
+
+2006-04-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_resize.c: + Field order control reworked
+ (meta.top_field_first has priority over source)
+
+2006-04-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_resize.c: + Top field first correction (original
+ approach would not have worked [mea culpa], and this is only a partial
+ solution since the consumers have no say in field order)
+
+ * src/modules/qimage/qimage_wrapper.cpp: + Fix for byte order as spotted by
+ Goncalo Carvhalo (many thanks :-))
+
+ * src/modules/core/filter_resize.c: + Meta override for field order
+ misreporting/errors in encoders
+
+2006-03-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/qimage/Makefile, src/modules/qimage/configure,
+ src/modules/qimage/qimage_wrapper.cpp: + And a fix for the PPC darwin
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h: + Sigh - big endian
+ issues on ppc based macs
+
+ * src/modules/fezzik.dict, src/modules/qimage/Makefile,
+ src/modules/qimage/configure, src/modules/qimage/factory.c,
+ src/modules/qimage/producer_qimage.c, src/modules/qimage/producer_qimage.h,
+ src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h: +
+ QImage module added - default is still GTK2 when available
+
+ * src/modules/gtk2/producer_pixbuf.c: + Bug Fix: Removes a memory leak on
+ last alpha channel
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h: + Preparation for a
+ QT image loader (to allow optional and functionally equivalent qt or gtk2
+ usage for image loading)
+
+2006-03-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pixbuf.c: + Usage of mlt_properties_dir_list
+
+ * src/framework/mlt_properties.c, src/framework/mlt_properties.h: + Adds a
+ utility function for listing files in a directory (aids with cross platform
+ support)
+
+2006-03-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/framework/mlt_manager.h, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_mono.c, src/modules/core/filter_mono.h: added mono
+ audio filter
+
+ * src/modules/kino/Makefile: libquicktime prefers pkg-config now and latest
+ lqt-config is broken with respect to --cflags
+
+ * configure: log configuration history to config.log
+
+2006-02-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vmfx/filter_shape.c: + Activates the mixdown in the combine to
+ allow audio sync'd with wipe (smooth ramping not implemented yet)
+
+ * src/modules/core/transition_mix.c: + Alternative mixing mechanism
+ introduced (specify a property of combine=1 on the mix transition to
+ activate)
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h: + Alternative between
+ track mixing mechanism (using a low pass filter)
+
+2006-02-15 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/dvcp.txt, docs/inigo.txt: minor fixes
+
+ * src/miracle/miracle_commands.c: add proper response to uadd command
+
+2006-01-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/Makefile: fix compilation error
+
+ * src/modules/dv/producer_libdv.c: Make libdv producer return some image even
+ if unable to handle specific image type request.
+
+ * Makefile: dist-clean target is more familiar - alias it
+
+ * src/modules/feeds/NTSC/data_fx.properties,
+ src/modules/feeds/NTSC/obscure.properties: fix comment/docu typo
+
+2005-12-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * debian/control, debian/rules, src/miracle/Makefile: + Fix for libmiracle
+ and alternative deb packaging
+
+ * src/framework/Makefile, src/miracle/Makefile,
+ src/modules/avformat/configure, src/valerie/Makefile: + Fix for Darwin and
+ soname logic
+
+ * debian/changelog, debian/control, debian/copyright, debian/rules: +
+ Functional debian build rules
+
+ * Makefile, configure, src/albino/Makefile, src/framework/Makefile,
+ src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+ src/modules/Makefile, src/modules/avformat/Makefile,
+ src/modules/core/Makefile, src/modules/dv/Makefile,
+ src/modules/feeds/Makefile, src/modules/fezzik/Makefile,
+ src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+ src/modules/jackrack/Makefile, src/modules/kino/Makefile,
+ src/modules/lumas/Makefile, src/modules/motion_est/Makefile,
+ src/modules/normalize/Makefile, src/modules/plus/Makefile,
+ src/modules/resample/Makefile, src/modules/sdl/Makefile,
+ src/modules/sox/Makefile, src/modules/valerie/Makefile,
+ src/modules/vmfx/Makefile, src/modules/vorbis/Makefile,
+ src/modules/westley/Makefile, src/modules/xine/Makefile,
+ src/valerie/Makefile: + Final updates for 0.2.1 - distclean corrected, soname
+ usage in linking, version bump
+
+2005-11-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/configure, src/miracle/configure, src/valerie/configure: +
+ More fixes for lib64
+
+ * src/modules/avformat/Makefile: + Uses libdir in private build of ffmpeg too
+
+
+ * src/modules/avformat/configure: + 64 bit fix for ffmpeg built externally
+ (should switch to pkg-config here)
+
+ * configure: - Removed a diagnostic
+
+ * Makefile, configure, src/framework/Makefile, src/miracle/Makefile,
+ src/valerie/Makefile: + Added a --libdir switch to the configure and build
+
+2005-11-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: + Correction to alpha mask
+ generation
+
+2005-11-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, src/albino/Makefile, src/framework/Makefile,
+ src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+ src/modules/Makefile, src/modules/avformat/Makefile,
+ src/modules/core/Makefile, src/modules/dv/Makefile,
+ src/modules/feeds/Makefile, src/modules/fezzik/Makefile,
+ src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+ src/modules/jackrack/Makefile, src/modules/kino/Makefile,
+ src/modules/lumas/Makefile, src/modules/motion_est/Makefile,
+ src/modules/normalize/Makefile, src/modules/plus/Makefile,
+ src/modules/resample/Makefile, src/modules/sdl/Makefile,
+ src/modules/sox/Makefile, src/modules/valerie/Makefile,
+ src/modules/vmfx/Makefile, src/modules/vorbis/Makefile,
+ src/modules/westley/Makefile, src/modules/xine/Makefile,
+ src/valerie/Makefile: + DESTDIR patch from Anthony Green (green at redhat dot
+ com) - many thanks :-)
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: Allows aac output, corrects ntsc
+ sample collection, and picks up known info streams
+
+2005-10-28 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/filter_crop_detect.c: Correct bug introduced by
+ revision 1.3
+
+ * src/modules/motion_est/filter_motion_est.c: x86 doesn't play well with ppc
+
+ * src/modules/motion_est/Makefile: Fix shared lib flags in Makefile for
+ Darwin
+
+2005-10-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/configure: + Enabled Zach's new slowmotion producer
+
+ * src/modules/core/consumer_null.c,
+ .../motion_est/filter_autotrack_rectangle.c, src/modules/sdl/consumer_sdl.c:
+ src/modules/core/consumer_null.c src/modules/sdl/consumer_sdl.c + Terminate
+ on pause functionality src/modules/motion_est/filter_autotrack_rectangle.c +
+ Ensures that tracked area remains valid (out of bounds was causing core
+ dumps) ? Currently, width/height is preserved on boundaries, but maybe it
+ should shrink/grow?
+
+2005-10-24 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/README: Added a producer slowmotion example.
+
+ * src/modules/motion_est/Makefile, src/modules/motion_est/factory.c,
+ src/modules/motion_est/filter_motion_est.c,
+ src/modules/motion_est/filter_motion_est.h,
+ src/modules/motion_est/producer_slowmotion.c: Import the proof of concept
+ slow motion producer. It provides basic slow motion through frame repeats and
+ a more advanced interpolation.
+
+2005-10-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vmfx/filter_shape.c: + Correction for non-zero in point on the
+ associated cut
+
+2005-10-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pango.c: + Moved ~ to LF hack to pango processing
+
+
+ * src/modules/sdl/consumer_sdl_still.c: + Rounding errors corrected for last
+ gasp scaling
+
+2005-10-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: + Deadlock resolution
+
+2005-10-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+ src/modules/core/filter_luma.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c: + Added an option to override alignment
+ and transparent borders for compositing
+
+2005-10-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vmfx/filter_shape.c: + Corrections, optimisations and a hack
+ for loading lumas from the mlt luma collection
+
+2005-10-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/configure, src/modules/sdl/factory.c: + Correction for
+ uninstalled sdl image lib
+
+ * configure: + OS/X Tiger patch
+
+ * src/framework/mlt_events.h, src/framework/mlt_types.h: gcc/g++ 4.x fix
+
+ * src/humperdink/client.c, src/humperdink/io.c, src/humperdink/io.h,
+ src/humperdink/remote.c, src/inigo/io.c: Remove OS/X warning re: get_string
+
+ * src/framework/mlt.h, src/inigo/inigo.c: + Whoops - removed dependency on
+ sdl in the framework for darwin
+
+ * src/modules/sdl/producer_sdl_image.c: + Surface conversion
+
+ * src/modules/sdl/producer_sdl_image.h: + Added producer_sdl_image as an
+ alternative image and image sequence producer
+
+ * src/modules/fezzik.dict, src/modules/sdl/Makefile,
+ src/modules/sdl/configure, src/modules/sdl/factory.c,
+ src/modules/sdl/producer_sdl_image.c: + Added producer_sdl_image as an
+ alternative image and image sequence producer
+
+2005-10-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: + Clean ups and corrections
+
+2005-09-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/filter_avcolour_space.c: + Extracts alpha from rgb24a
+ images
+
+2005-09-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+ src/modules/core/filter_rescale.c, src/modules/core/filter_resize.c,
+ src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c,
+ src/modules/core/transition_composite.c,
+ src/modules/feeds/PAL/etv.properties: src/framework/mlt_frame.c + Corrections
+ for resizing images and alpha (uneven widths) src/framework/mlt_tractor.c +
+ Added an output aspect ratio (being the aspect ratio of the background)
+ src/modules/core/filter_rescale.c + Force a rescale of the alpha in parallel
+ with image src/modules/core/filter_resize.c + Rounding errors corrections
+ src/modules/core/filter_watermark.c + Propogation of output aspect ratio in
+ reverse case src/modules/core/producer_colour.c + Reassign aspect ratio
+ after get_image src/modules/core/transition_composite.c + More uneven width
+ corrections + Use of output aspect ratio when available
+ src/modules/feeds/PAL/etv.properties + Temporary work around to keep
+ composites correct
+
+2005-09-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: + Correction and a minor
+ optimisation
+
+ * src/modules/gtk2/producer_pixbuf.c: + Changed incorrect global variable to
+ static
+
+ * src/modules/avformat/consumer_avformat.c:
+ src/modules/avformat/consumer_avformat.c + User specified pixel format
+ property (pix_fmt) + Corrections to aspect ratio + Alpha channel added to
+ RGBA32 conversions - Removed an historical/erroneous attempt to hack aspect
+ ratio
+
+2005-09-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c: + Indicator for missing media
+ replacement in case pango doesn't exist
+
+ * src/modules/plus/filter_charcoal.c: + Bounds checking on chroma samples
+
+ * src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/producer_avformat.c: filter_avcolour_space.c +
+ Correction for uneven width filter_avdeinterlace.c + Correction for cases
+ were the interlace state of frame is only known after rendering
+ producer_avformat.c + Corrections for uneven width + Corrections for state
+ propogation of top field first and interlaced state
+
+ * src/modules/xine/filter_deinterlace.c: + Correction for cases where the
+ interlaced state is determined after the image is rendered
+
+2005-09-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/framework/mlt_producer.c, src/modules/avformat/configure,
+ src/modules/avformat/producer_avformat.c, src/modules/core/filter_mirror.c,
+ src/modules/core/producer_colour.c, src/modules/core/transition_composite.c,
+ src/modules/plus/filter_sepia.c, src/modules/plus/transition_affine.c,
+ src/modules/sdl/consumer_sdl.c: src/framework/mlt_frame.c + Removed
+ unecessary even pixel position and width dependency + Rewrote resize methods
+ to accomodate uneven widths src/framework/mlt_frame.h + Correct RGB2YUV -
+ now 2^10 based and range checks removed (not needed)
+ src/framework/mlt_producer.c + Check for unspecified eof property
+ src/modules/avformat/producer_avformat.c + Provide forced aspect ratio
+ property src/modules/core/filter_mirror.c + Correction for uneven width
+ src/modules/core/producer_colour.c + Corrections for aspect ratio (default to
+ 0) and allow override + Corrections for uneven width
+ src/modules/core/transition_composite.c + Corrections for uneven pixel
+ position and width + Removed deprecated operator code
+ src/modules/plus/filter_sepia.c + Corrections for uneven width
+ src/modules/plus/transition_affine.c + Corrections for uneven width
+ src/modules/sdl/consumer_sdl.c + Corrections for uneven width
+
+2005-09-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/framework/mlt_tractor.c,
+ src/framework/mlt_types.h, src/modules/avformat/filter_avcolour_space.c,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_luma.c, src/modules/core/transition_composite.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+ src/framework/mlt_consumer.c + Added capabilities to allow the application to
+ handle images via the consumer-frame-show event + Added cabilities to allow
+ the application to control the image format src/framework/mlt_frame.c + Long
+ standing discrepancy resolved - image format is now stored on the frame
+ object src/framework/mlt_tractor.c src/framework/mlt_types.h + Added
+ mlt_image_opengl which is supposed to provide an rgb image swapped around for
+ the platform src/framework/mlt_frame.h + Added a basic YUV2RGB macro
+ src/modules/avformat/filter_avcolour_space.c + Added a converter for the
+ opengl swapped RGB image + Corrected support for rgb24a requests
+ src/modules/core/configure src/modules/core/factory.c + Added an alias for
+ color (since it seems to trouble so many people)
+ src/modules/core/filter_luma.c + Added the format property to the generated
+ frame src/modules/core/transition_composite.c + Added the format property to
+ the generated frame src/modules/gtk2/producer_pixbuf.c + Swapped some
+ properties to hidden from the serialiser src/modules/sdl/consumer_sdl.c +
+ Support for application provided previews and colour space conversion
+ src/modules/sdl/consumer_sdl_preview.c + Partial switch to
+ mlt_properties_pass_list + Application provided preview support added
+ src/modules/sdl/consumer_sdl_still.c + Application provided preview support
+ added
+
+2005-09-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_invert.c: + Small mod to allow better use of invert
+ as a gui item selector (alpha property)
+
+2005-09-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_still.c:
+ consumer_sdl.c consumer_sdl_still.c + Corrections to silly mistake regarding
+ initialisation from previous checkin
+
+ * src/modules/vmfx/Makefile, src/modules/vmfx/configure,
+ src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c,
+ src/modules/vmfx/filter_chroma.h, src/modules/vmfx/filter_chroma_hold.c,
+ src/modules/vmfx/filter_chroma_hold.h, src/modules/vmfx/filter_shape.c,
+ src/modules/vmfx/filter_shape.h, src/modules/vmfx/producer_pgm.c,
+ src/modules/vmfx/producer_pgm.h: + Changed license of plugins to LGPL + Added
+ a chroma hold filter + Small optimisation/correction to chroma filter
+
+2005-08-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c: + Keyboard handling events on Darwin
+
+ * src/modules/lumas/Makefile, src/modules/sdl/consumer_sdl.c: lumas/Makefile
+ + Correction for non-gui app build on darwin lumas/luma.c + Handle sdl
+ events sdl/consumer_sdl.c + Audio on Darwin
+
+ * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: src/modules/sdl/consumer_sdl.c
+ src/modules/sdl/consumer_sdl_preview.c src/modules/sdl/consumer_sdl_still.c +
+ Corrections to preview mode switching
+
+ * src/modules/sdl/consumer_sdl_preview.c:
+ src/modules/sdl/consumer_sdl_preview.c + Temporary rollback for linux
+
+ * configure, src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: configure + Correction to ldflags for
+ Darwin src/modules/avformat/Makefile src/modules/avformat/configure +
+ Correction for avformat on Darwin src/modules/sdl/consumer_sdl.c
+ src/modules/sdl/consumer_sdl_preview.c src/modules/sdl/consumer_sdl_still.c +
+ Forgot to create the surface on the start (doh)
+
+ * configure, src/framework/mlt.h, src/inigo/inigo.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: configure + Darwin sdl linking and
+ cflags on all use of mlt (annoying, but looks unavoidable)
+ src/framework/mlt.h + Include sdl header on Darwin src/inigo/inigo.c +
+ Correction for Darwin key reading from terminal
+ src/modules/sdl/consumer_sdl.c src/modules/sdl/consumer_sdl_preview.c
+ src/modules/sdl/consumer_sdl_still.c + Moved initialisation of sdl components
+ to the start/stop methods (Darwin requirement)
+
+ * src/modules/motion_est/configure: + Correction to the disabled case (should
+ be disable-motion_est and plugins should not be registered)
+
+2005-08-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/vmfx/Makefile, src/modules/vmfx/configure,
+ src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c,
+ src/modules/vmfx/filter_chroma.h: + Added rudimentary chroma to alpha filter
+ (optimised on green by default)
+
+2005-08-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.h: src/framework/mlt_properties.c
+ src/framework/mlt_properties.h + Added get and set for int64_t
+ src/framework/mlt_property.h + Corrected int64_t
+
+2005-08-26 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/README, .../motion_est/filter_autotrack_rectangle.c:
+ Add the obscure=1 option to filter_autotrack_rectangle and update the README
+ with an example.
+
+2005-08-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict, src/modules/vmfx/Makefile,
+ src/modules/vmfx/configure, src/modules/vmfx/factory.c,
+ src/modules/vmfx/filter_shape.c, src/modules/vmfx/filter_shape.h,
+ src/modules/vmfx/producer_pgm.c, src/modules/vmfx/producer_pgm.h: + Added
+ VMFX module + New filter (shape) which provides alpha manipulations and an
+ alternative wipe mechanism + New producer (pgm) which provides basic
+ functionality for portable grey maps
+
+ * src/modules/core/transition_composite.c: + SMP fix - geometry modifications
+ need explicit locking
+
+2005-08-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.h: + Replaced this with self in new pass
+ functions for C++ compilation
+
+2005-08-21 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.c, src/framework/mlt_property.h: Introduce some
+ more civilized ways to copy properties. See code comments for usage.
+
+2005-08-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_tractor.c: + Attempt to ensure that the aspect ratio of
+ the background is the reported ar of the output frame
+
+ * src/modules/core/transition_composite.c: + Yet another aspect ratio
+ correction for the filter transition (not 100% correct yet...) + Correction
+ for aspect_ratio == 0 case (should honour consumer)
+
+ * src/modules/avformat/consumer_avformat.c: + Correction for aspect ratio
+
+ * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c:
+ producer_pango.c producer_pixbuf.c + More efficient use of pixbuf objects and
+ sequences/mlt pango lists
+
+2005-08-15 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/consumer_westley.c: Fix build errors caused by the
+ (hypothetical) conversion of mlt_position from an int to a float, preserving
+ original behavior.
+
+ * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/framework/mlt_property.c, src/framework/mlt_types.h: Fix build errors
+ caused by the (hypothetical) conversion of mlt_position from an int to a
+ float, preserving original behavior.
+
+ * src/inigo/inigo.c, src/modules/core/filter_luma.c,
+ src/modules/motion_est/filter_crop_detect.c, src/modules/sdl/consumer_sdl.c:
+ Fix build errors caused by the (hypothetical) conversion of mlt_position from
+ an int to a float, preserving original behavior.
+
+2005-08-07 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/filter_vismv.c: Misc changes. May remove this file
+ completely soon.
+
+ * src/modules/motion_est/filter_motion_est.c: This is a significant rewrite.
+ -Cleared up as many conceptualy sticky points as possible. -Removed chroma
+ comparison code pending a better rewrite. -Added show_residual=1 and
+ show_reconstruction=1 debug modes. See README. -Renamed many variables and
+ functions. -Revamped geometry handling. -Lots more I'm forgeting.
+
+ * src/modules/motion_est/README: Added some more examples.
+
+ * src/inigo/inigo.c: Prevent a frame from being skipped when inigo is first
+ paused.
+
+ * src/modules/motion_est/filter_crop_detect.c: Corrected geometry handling.
+ Removed redundant arrow drawing code. Modified thresholding.
+
+2005-08-04 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure: ffmpeg split
+ of the libavutil library.
+
+2005-07-30 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/README, src/modules/motion_est/filter_motion_est.c:
+ Added a README file with lots of juicy info. Added a denoise motion vectors
+ function, enabled by default; the results seem very good. Removed some unused
+ development code.
+
+2005-07-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/Makefile: + Allow header dependency checks
+
+ * src/modules/avformat/configure: + Added an additional help message (for
+ ffmpeg suffix)
+
+ * Makefile: + Force dependency checks on header files
+
+2005-07-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_types.h: Do not break ABI to workaround a problem in
+ swig.
+
+2005-07-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/producer_kino.c: + Stores the resource correctly (to allow
+ serialisation via westley)
+
+2005-07-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_types.h: Add names to enums to make newer versions of
+ swig (noticed on 1.3.24) happy.
+
+2005-07-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_watermark.c: + Correction to long outstanding
+ oddity regarding composite.out - not needed in many cases now
+
+2005-07-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/riff.cc: + Minor correction for entry length being less
+ than the data length
+
+ * src/modules/kino/avi.cc, src/modules/kino/avi.h, src/modules/kino/riff.cc,
+ src/modules/kino/riff.h: + fixes for opendml dv avi
+
+2005-07-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: - Removed 'resize' property logic and
+ width/height confusion
+
+ * src/modules/core/filter_resize.c: + Correction for rounding errors
+
+2005-07-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/dv/producer_libdv.c: - Removed unused aspect ratio property
+
+ * src/modules/avformat/producer_avformat.c: + Hide internal properties via
+ the _ convention
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_service.c: - Remove
+ warnings
+
+2005-07-21 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/filter_motion_est.c: autotrack_rectangle and
+ motion_est now convert pixel units to macroblock (whole) units the same way.
+
+ * .../motion_est/filter_autotrack_rectangle.c: Fixed several accuracy issues.
+ Cleaned up code. Corrected pause behavior.
+
+2005-07-20 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * .../motion_est/filter_autotrack_rectangle.c: use shared arrow drawing code.
+ improve tracking accuracy.
+
+2005-07-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_filter.c, src/framework/mlt_service.c: mlt_filter.c
+ mlt_service.c + Filter disable property
+
+2005-07-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pango.c: producer_pango.c + Correction of
+ oversight - allow serialisation of mpl usage
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: consumer_avformat.c
+ producer_avformat.c + Sync with current ffmpeg CVS - PLEASE UPDATE FFMPEG
+ FIRST
+
+2005-07-18 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pango.c: + Mutex protection (temporary work
+ around of SMP systems) + Corrected aspect ratio (should be 1, not 0)
+
+ * src/modules/core/producer_colour.c: + Accepts modifiable colour property
+ (via resource) + Hides non-public properties
+
+ * src/modules/fezzik.dict: + Added convenience lookup for MLT Pango List
+ files
+
+ * src/modules/core/filter_mirror.c: + Alpha handling in silly filter :-)
+
+ * src/modules/core/transition_composite.c: + Inherits deinterlace method from
+ the consumer + Sanity check on scaled size for compositing
+
+ * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c:
+ producer_pango.c + Added cloning + Added the very silly .mpl (MLT Pango List)
+ format [details to follow] + Corrected invalid content producer_pixbuf.c +
+ Corrected invalid content
+
+ * src/modules/gtk2/producer_pixbuf.c: + Bug fixes to test card handling +
+ Alpha channel cloning + Minor tidy up
+
+2005-07-16 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_service.c,
+ src/framework/mlt_tractor.c, src/modules/core/filter_resize.c,
+ src/modules/core/filter_transition.c,
+ src/modules/core/transition_composite.c, src/modules/dv/producer_libdv.c:
+ rc/framework/mlt_frame.c + image_count added to assist the 'transition
+ filter' in knowing when to act... src/framework/mlt_playlist.c + Complete
+ rework of fx cuts - now only the fx are output on a frame
+ src/framework/mlt_producer.c + Aspect ratio of cuts inherited from parent
+ src/framework/mlt_service.c + Get frame reworked and cleaned up
+ src/framework/mlt_tractor.c - Removed erroneous width/height pass down prior
+ to image fetching + Corrected types on other properties for pass down +
+ Complete rework of fx cuts - they're now received as producer-less frames
+ from a track + Added image_count logic for transition filter assistance
+ src/modules/core/filter_resize.c + Added state retention of aspect ratio (may
+ withdraw this later - it assumes producer knows a/r on frame creation/prior
+ to image fetch) src/modules/core/filter_transition.c + Checks that two
+ images are available before processing + Checks test image/audio cases
+ src/modules/core/transition_composite.c + Major correction in aspect ratio
+ handling (the b frame image is 'distorted' to the consumers aspect ratio) +
+ Minor clean up of silly and/or/xor - now have 'operator=[and/or/xor]' (more
+ clean up to follow) src/modules/dv/producer_libdv.c + Frame stored width and
+ height are no longer assumed to be 'safe' here (investigating)
+
+2005-07-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_repository.c: mlt_repository.c + VERY temporary hack to
+ avoid global symbol clashes (RTLD_GLOBAL needed by kino/libquicktime only so
+ far)
+
+2005-07-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/filehandler.cc: filehandler.cc + FOURCC for DVCPRO
+ quicktime
+
+ * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c + Oops - the frame
+ position is relative to the in point (the internal position is absolute)
+
+ * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c + Fix for non-zero
+ in point
+
+2005-07-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: consumer_sdl_preview.c
+ consumer_sdl_still.c + Fixes a deadlock condition
+
+ * src/modules/kino/filehandler.cc: src/modules/kino/filehandler.cc + Added
+ missing fourccs to allow compilation
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/framework/mlt_repository.c, src/modules/kino/filehandler.cc,
+ src/modules/kino/filehandler.h: framework/mlt_frame.c framework/mlt_frame.h +
+ Added sample calculator (samples to current frame)
+ framework/mlt_repository.c + Symbols exported from plugins
+ modules/kino/filehandler.cc modules/kino/filehandler.h + Audio handling of dv
+ mov
+
+2005-07-09 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/configure: Mention that motion est is disabled by
+ default during ./configure.
+
+ * configure, src/modules/motion_est/configure: Prevent motion estimation
+ components from building unless requested.
+
+2005-07-08 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/motion_est/Makefile, src/modules/motion_est/configure: removed
+ a debugging target.
+
+ * src/modules/motion_est/Makefile, src/modules/motion_est/arrow_code.c,
+ src/modules/motion_est/arrow_code.h, src/modules/motion_est/configure,
+ src/modules/motion_est/factory.c,
+ .../motion_est/filter_autotrack_rectangle.c,
+ src/modules/motion_est/filter_crop_detect.c,
+ src/modules/motion_est/filter_motion_est.c,
+ src/modules/motion_est/filter_motion_est.h,
+ src/modules/motion_est/filter_vismv.c, src/modules/motion_est/sad_sse.h:
+ Initial import of the motion estimation filter.
+
+2005-07-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c:
+ src/modules/avformat/consumer_avformat.c + Correction for mpeg encoding -
+ Removal of erroneous frame rate checks
+
+2005-07-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c:
+ src/modules/westley/producer_westley.c - Rollback on erroneous checkin
+ (functionality covered correctly in playlist)
+
+ * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/modules/fezzik.dict, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/westley/producer_westley.c: src/framework/mlt_frame.c +
+ Correction for aspect ratio of synthesized test card
+ src/framework/mlt_playlist.c + Special case for handling fx cuts
+ src/modules/fezzik.dict + Convenience jfx and jef extensions for jahshaka
+ src/modules/core/transition_composite.c + Ensure that scaling and correct
+ image extraction is handled src/modules/core/transition_luma.c + Ensure that
+ scaling and correct image extraction is handled
+ src/modules/gtk2/producer_pixbuf.c + Allow user overrides for progressive and
+ aspect_ration src/modules/westley/producer_westley.c + Special case for fx
+ cuts
+
+2005-06-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: src/modules/sdl/consumer_sdl.c + (Re)Added
+ audio volume control
+
+ * src/framework/mlt_tractor.c: src/framework/mlt_tractor.c + Added support
+ for pango usage on audio only fx cuts (sigh...)
+
+ * src/framework/mlt_tractor.c: src/framework/mlt_tractor.c + Slight
+ modification to allow pango use in fx cuts
+
+2005-06-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_transition.c, src/modules/core/filter_transition.h:
+ src/modules/core/filter_transition.c src/modules/core/filter_transition.h +
+ Initial release
+
+ * src/framework/mlt_deque.c, src/framework/mlt_deque.h,
+ src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h: src/framework/mlt_deque.c
+ src/framework/mlt_deque.h + Added support for doubles
+ src/framework/mlt_frame.c + Switched order of source/dest audio mix
+ extraction (for transition as filter usage) src/framework/mlt_tractor.c -
+ Removed warning introduced from previous checkin (missing ctype.h) +
+ Temporary work around to allow frames to carry multiple frames (for
+ transition as filter usage) src/modules/core/Makefile
+ src/modules/core/configure src/modules/core/factory.c + Support for new
+ transition filter :-) src/modules/core/transition_composite.c
+ src/modules/core/transition_composite.h - Removed frame properties dependence
+ for process/get_image state communication + Extended alpha blending modes to
+ 'and' and 'xor' logic (may change property triggering soon) + Provided
+ support for transition as filter usage + Cleaned up public copy region
+ functionality
+
+ * src/modules/core/transition_composite.c: + Cleaned up compositing and alpha
+ usage (all frames always have an alpha mask) + Provided an alternative
+ rendering mechanism ('or' which takes a and b alpha into account) + Provided
+ a and b alpha mask overides ('alpha_a' and 'alpha_b')
+
+2005-06-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_geometry.c, src/framework/mlt_tractor.c,
+ src/modules/core/transition_composite.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl_still.c: src/framework/mlt_geometry.c
+ src/modules/core/transition_composite.c src/modules/sdl/consumer_sdl.c
+ src/modules/sdl/consumer_sdl_still.c + replaced floats with doubles (attempt
+ to avoid rounding errors?) src/framework/mlt_tractor.c + corrections for
+ fx_cuts (allows animated fx)
+
+2005-06-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict: + BGa's request for additional westley extensions
+
+2005-06-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.h, src/framework/mlt_tractor.c,
+ src/modules/core/filter_watermark.c, src/modules/core/producer_noise.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+ src/framework/mlt_consumer.c + Attempt to make all frames have the correct
+ aspect_ratio (works in many but not all cases) src/framework/mlt_frame.h +
+ Provide macro access to the video and image RPN queues
+ src/framework/mlt_tractor.c + Provides orphaned filters
+ src/modules/core/producer_noise.c - remove specification of aspect ratio
+ src/modules/core/filter_watermark.c src/modules/core/transition_composite.c
+ src/modules/core/transition_luma.c src/modules/plus/filter_affine.c
+ src/modules/plus/transition_affine.c + Corrections for frames with an aspect
+ ratio = 0 (supplement to mlt_consumer mod)
+
+2005-06-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_producer.c, src/inigo/inigo.c,
+ src/modules/avformat/consumer_avformat.c, src/modules/core/filter_resize.c,
+ src/modules/core/producer_colour.c, src/modules/core/producer_noise.c,
+ src/modules/dv/consumer_libdv.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/kino/Makefile,
+ src/modules/kino/avi.cc, src/modules/kino/avi.h, src/modules/kino/configure,
+ src/modules/kino/filehandler.cc, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+ src/framework/mlt_consumer.c src/framework/mlt_consumer.h + Added a general
+ profile handling for size, aspect ratio and display ratio
+ src/framework/mlt_producer.c + Correction to aspect ratio properties
+ src/inigo/inigo.c + Minimalist support for sdl_preview (still not very good)
+ src/modules/avformat/consumer_avformat.c + Takes consumer profile into
+ account src/modules/core/filter_resize.c + Corrections for synthesised
+ producers and aspect ratio (inherits from consumer)
+ src/modules/core/producer_colour.c src/modules/core/producer_noise.c
+ src/modules/gtk2/producer_pango.c + Ensures that resize picks up consumer
+ aspect ratio src/modules/dv/consumer_libdv.c + Honour wide screen output
+ src/modules/gtk2/producer_pixbuf.c + Correction for 1:1 aspect ratio
+ src/modules/kino/Makefile src/modules/kino/avi.cc src/modules/kino/avi.h
+ src/modules/kino/configure src/modules/kino/filehandler.cc + Attempt to allow
+ mov dv files to provide audio src/modules/sdl/consumer_sdl.c
+ src/modules/sdl/consumer_sdl_preview.c src/modules/sdl/consumer_sdl_still.c +
+ Takes consumer profile into account
+
+2005-06-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/configure: Quick temporary fix for mlt config in non-standard
+ paths (relates to mlt++)
+
+2005-06-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_tractor.c,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/xine/filter_deinterlace.c: Consumer deinterlace_method property
+ added
+
+ * src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/core/filter_resize.c, src/modules/xine/filter_deinterlace.c:
+ Sanity checks for normalising filters
+
+2005-06-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik.dict: libdv/avformat switching
+
+2005-06-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/filter_avcolour_space.c: Sanity checks
+
+ * src/modules/gtk2/producer_pixbuf.c: Fallback to testcard
+
+2005-05-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: NTSC fix
+
+ * src/modules/fezzik.dict: Added bmp support
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+ src/framework/mlt_producer.c: Frame rate properites and factory
+ initialisation
+
+2005-05-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: audio out fix
+
+2005-05-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/filehandler.cc, src/modules/kino/filehandler.h: DVCPRO fix
+
+
+2005-05-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c: jpeg and mjpeg fixes
+
+2005-05-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/jackrack/filter_ladspa.c: bugfix segfault on closre when filter
+ never invoked
+
+2005-05-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c: Build modification to ffmpeg/avformat
+
+2005-05-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/dv/configure, src/modules/gtk2/configure,
+ src/modules/jackrack/configure, src/modules/kino/configure,
+ src/modules/resample/configure, src/modules/sdl/configure,
+ src/modules/sox/configure, src/modules/vorbis/configure,
+ src/modules/westley/configure, src/modules/xine/configure: Bourne shell
+ compliance
+
+ * configure: Bourne shell compliance
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure: Corrections
+ to --avformat-cvs option
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c,
+ src/modules/avformat/producer_avformat.c: FFMPEG revisions to match current
+ CVS (part 1)
+
+2005-05-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/Makefile: fix compilation
+
+2005-04-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/modules/configure, src/modules/jackrack/Makefile,
+ src/modules/jackrack/configure, src/modules/jackrack/control_message.h,
+ src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.c,
+ src/modules/jackrack/filter_ladspa.c, src/modules/jackrack/filter_ladspa.h,
+ src/modules/jackrack/jack_rack.c, src/modules/jackrack/jack_rack.h,
+ src/modules/jackrack/plugin.c, src/modules/jackrack/plugin.h,
+ src/modules/jackrack/plugin_desc.c, src/modules/jackrack/plugin_mgr.c,
+ src/modules/jackrack/plugin_mgr.h, src/modules/jackrack/process.c,
+ src/modules/jackrack/process.h, src/modules/jackrack/ui.c,
+ src/modules/jackrack/ui.h: cleanup and reduce code in jackrack support code
+ and add new jack-less filter_ladspa.
+
+2005-04-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/dv/producer_libdv.c: Fix for file identification and dv
+
+2005-04-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/kino/avi.h: Minor correction
+
+ * src/modules/kino/Makefile, src/modules/kino/avi.cc, src/modules/kino/avi.h,
+ src/modules/kino/configure, src/modules/kino/endian_types.h,
+ src/modules/kino/error.cc, src/modules/kino/error.h,
+ src/modules/kino/factory.c, src/modules/kino/filehandler.cc,
+ src/modules/kino/filehandler.h, src/modules/kino/kino_wrapper.cc,
+ src/modules/kino/kino_wrapper.h, src/modules/kino/producer_kino.c,
+ src/modules/kino/producer_kino.h, src/modules/kino/riff.cc,
+ src/modules/kino/riff.h: Initial version
+
+ * src/modules/dv/producer_libdv.c, src/modules/fezzik.dict: Preparation for
+ kino support
+
+2005-04-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/dv/Makefile: corrected pkg-config libdv usage
+
+2005-04-14 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl_still.c: Build
+ fixes.
+
+ * src/modules/sdl/consumer_sdl.c: An unfinished attempt at porting the SDL
+ consumer to OS X. What remains is a bug in libSDL where the SDL screen object
+ becomes a NULL pointer when it shouldn't. This also affects 'ffplay' and the
+ SDL test program 'threadwin -threaded' I think.
+
+ * src/modules/sdl/consumer_sdl_osx_hack.h: A hack to inform Cocoa that is
+ should be multithreaded by spinning of a dummy thread.
+
+ * configure, src/albino/albino.c, src/inigo/inigo.c, src/miracle/miracle.c:
+ OS X uses -DDARWIN in
+ /System/Library/Frameworks/CoreFoundation.framework/Headers/CFBase.h; This in
+ combination with #include <Foundation/Foundation.h> caused compilation errors
+ while porting consumer_sdl to OS X.
+
+2005-04-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * COPYING: License update
+
+ * src/modules/sox/Makefile, src/modules/sox/configure: Disable sox when
+ unavailable
+
+ * src/modules/jackrack/configure: Disable jackrack when unavailable
+
+ * src/modules/dv/configure, src/modules/vorbis/configure: Disable libdv when
+ unavailable
+
+ * src/modules/resample/configure: Disable libsamplerate when unavailable
+
+ * src/modules/sdl/configure: Disable sdl when unavailable
+
+ * src/modules/vorbis/configure: Disable vorbis when unavailable
+
+ * configure: Automatic disabling off mmx on a OS/X; mmx detection on Linux;
+ other platforms probably broken
+
+ * src/modules/xine/configure: Disable xine when mmx not available
+
+ * src/modules/westley/configure: Conditional compilation of westley/libxml2
+ components
+
+ * src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+ src/modules/gtk2/factory.c: Conditional compilation of gtk2 components
+
+2005-04-12 dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_geometry.c: Minor but confusing comment fix.
+
+2005-04-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, setenv, src/albino/Makefile, src/albino/albino.c,
+ src/framework/Makefile, src/humperdink/Makefile, src/humperdink/io.c,
+ src/inigo/Makefile, src/inigo/inigo.c, src/inigo/io.c, src/miracle/Makefile,
+ src/miracle/miracle.c, src/modules/avformat/Makefile,
+ src/modules/avformat/configure, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/dv/Makefile,
+ src/modules/dv/configure, src/modules/fezzik/Makefile,
+ src/modules/fezzik/configure, src/modules/gtk2/Makefile,
+ src/modules/gtk2/configure, src/modules/inigo/Makefile,
+ src/modules/inigo/configure, src/modules/jackrack/Makefile,
+ src/modules/jackrack/configure, src/modules/normalize/Makefile,
+ src/modules/normalize/configure, src/modules/plus/Makefile,
+ src/modules/plus/configure, src/modules/resample/Makefile,
+ src/modules/resample/configure, src/modules/sdl/Makefile,
+ src/modules/sdl/configure, src/modules/sox/Makefile,
+ src/modules/sox/configure, src/modules/valerie/Makefile,
+ src/modules/valerie/configure, src/modules/vorbis/Makefile,
+ src/modules/vorbis/configure, src/modules/westley/Makefile,
+ src/modules/westley/configure, src/modules/xine/Makefile,
+ src/modules/xine/configure, src/tests/Makefile, src/valerie/Makefile,
+ src/valerie/valerie_socket.c: OS/X Patch from Torsten Spindler
+
+ * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_repository.c, src/framework/mlt_repository.h: More const
+ usage
+
+2005-04-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/modules/gtk2/Makefile,
+ src/modules/resample/filter_resample.c: Auto deinterlace on pause, fix for
+ audio resampling/test audio and MMX checks in gtk2
+
+2005-04-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c, src/modules/avformat/filter_avresample.c,
+ src/modules/gtk2/Makefile, src/modules/jackrack/filter_jackrack.c,
+ src/modules/sox/filter_sox.c: avformat-cvs build fix and audio filter
+ correction
+
+2005-04-05 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/albino.c, src/miracle/miracle.c: make miracle and albino local
+ use fifo instead of rr rt schedule
+
+ * src/albino/albino.c, src/framework/mlt_consumer.c, src/inigo/inigo.c,
+ src/miracle/miracle.c, src/miracle/miracle_server.c,
+ src/modules/avformat/consumer_avformat.c, src/modules/core/consumer_null.c,
+ src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+ src/modules/fezzik/producer_hold.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c, src/modules/xine/filter_deinterlace.c:
+ realtime scheduling updates; suppress libdv errors; add frame property
+ deinterlace_method; default producer_hold to use onefield; add begin property
+ to producer_pixbuf
+
+2005-03-16 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_producer.c: Frame rendering
+ event
+
+2005-03-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/dvcp.txt, src/miracle/miracle_local.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+ src/miracle/miracle_unit_commands.h, src/modules/avformat/factory.c,
+ src/valerie/valerie.c, src/valerie/valerie.h: Threading considerations and
+ DVCP WIPE introduced
+
+2005-03-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_producer.c,
+ src/modules/core/transition_composite.c,
+ src/modules/plus/transition_affine.c: Minor corrections and more affine
+ experiments
+
+2005-02-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_unit.c, src/modules/avformat/consumer_avformat.c: Minor
+ mods to playout via avformat and miracle unit generation on an xfer
+
+ * src/modules/westley/producer_westley.c: Reinstatement of entity handling
+ and removal of libxml2 warning for non-existent file
+
+2005-02-18 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/core/producer_colour.c,
+ src/modules/core/transition_composite.c,
+ src/modules/plus/transition_affine.c: Minor corrections with alpha and
+ affines
+
+2005-02-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_unit.c: Smoother unit load
+
+2005-02-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_producer.c, src/framework/mlt_tractor.c,
+ src/modules/core/producer_colour.c, src/modules/core/transition_composite.c,
+ src/modules/feeds/PAL/etv.properties, src/modules/gtk2/producer_pango.c,
+ src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c,
+ src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Alphas and global feeds revisted
+
+2005-02-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: Speed switch corrections
+
+2005-02-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c: Optional 8 or 16 bit pgm or png lumas;
+ fixes for non-existence
+
+ * src/modules/lumas/configure, src/modules/lumas/create_lumas: Optional 8 or
+ 16 bit pgm or png
+
+2005-02-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+ more affine silliness
+
+2005-02-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/transition_affine.c: affine silliness
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: SMP/HT fixes
+
+2005-02-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/feeds/PAL/border.properties: fill for borders
+
+ * src/modules/gtk2/Makefile: conditional mmx compilation
+
+ * src/modules/core/transition_composite.c: int handling on the frame image
+ stack
+
+ * src/framework/mlt_deque.c, src/framework/mlt_deque.h,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h: 64 bit fix and deque
+ int holding
+
+2005-01-31 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c: Refresh count instead of flag
+
+ * src/modules/sdl/consumer_sdl_preview.c: Mutex locking for refresh handling
+
+ * src/modules/core/filter_rescale.c: Warning removal
+
+ * src/modules/resample/filter_resample.c: Workaround for test card audio (may
+ need to review)
+
+ * src/modules/inigo/producer_inigo.c: Empty track definition fix
+
+ * src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: Consumer reworked
+
+ * src/modules/plus/transition_affine.c: Pointless improvement on a bad filter
+ :-)
+
+ * src/modules/gtk2/producer_pango.c: Memory leak fix
+
+ * src/modules/westley/consumer_westley.c: titles and global feeds
+
+ * src/modules/feeds/PAL/border.properties,
+ src/modules/feeds/PAL/data_fx.properties: Minor corrections
+
+ * src/modules/core/filter_data_show.c: Global/local data show distinction
+
+ * src/modules/core/Makefile: Removed superflous mmx compilation
+
+ * src/framework/mlt_tractor.c: Global data feed handling
+
+ * src/framework/mlt_filter.c, src/framework/mlt_service.c: Wild card filter
+ tracks
+
+ * src/framework/mlt_events.c: Memory leak fix
+
+ * src/framework/mlt_consumer.c: Small correction to deinterlacing
+
+2005-01-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/feeds/PAL/border.properties,
+ src/modules/feeds/PAL/example.properties: Test case feeds added
+
+ * src/modules/avformat/filter_avresample.c,
+ src/modules/avformat/producer_avformat.c,
+ src/modules/core/filter_channelcopy.c, src/modules/core/filter_watermark.c,
+ src/modules/core/producer_noise.c, src/modules/core/producer_ppm.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/modules/core/transition_mix.c, src/modules/core/transition_region.c,
+ src/modules/dv/producer_libdv.c, src/modules/feeds/PAL/etv.properties,
+ src/modules/jackrack/filter_jackrack.c,
+ src/modules/normalize/filter_volume.c, src/modules/plus/transition_affine.c,
+ src/modules/resample/filter_resample.c, src/modules/sox/filter_sox.c,
+ src/modules/vorbis/producer_vorbis.c: Remaining audio handling switched to
+ stacks; Minor corrections to compositing and mixing; localisation for pango
+
+ * src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Localised data storage and utf-8
+ properties
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/framework/mlt_producer.c,
+ src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+ src/framework/mlt_transition.h: Transitions reworked (always_active
+ capabilities); remaining audio handling switched to stacks
+
+ * demo/mlt_news: Correction for audio mix
+
+2005-01-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c: consumer close fix
+
+ * src/modules/feeds/PAL/etv.properties, src/modules/gtk2/producer_pango.c:
+ iconv fixes
+
+2005-01-16 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_slideshow_black, docs/services.txt,
+ src/modules/core/transition_composite.c,
+ src/modules/feeds/PAL/etv.properties: Minor modifications to compositing
+ options and etv fx
+
+ * src/modules/gtk2/producer_pango.c: Added a weight property
+
+2005-01-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_attributes: Correction for ETV specific filters
+
+ * src/modules/feeds/PAL/etv.properties: Seperation for ETV specific filters
+
+ * docs/testing.txt: Test case clean up
+
+ * demo/demo, demo/mlt_watermark, src/framework/mlt_producer.c,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.c, src/framework/mlt_property.h,
+ src/framework/mlt_tractor.c, src/modules/core/filter_data_show.c,
+ src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c,
+ src/modules/feeds/PAL/data_fx.properties,
+ src/modules/feeds/PAL/obscure.properties, src/modules/fezzik.ini,
+ src/modules/gtk2/producer_pango.c: Sundry minor fixes and optimisations
+
+2005-01-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_geometry.c: Corrections to geometry next key and
+ serialise
+
+2005-01-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_geometry.c, src/framework/mlt_geometry.h: Next/Prev key
+ extraction
+
+ * src/modules/feeds/PAL/data_fx.properties,
+ src/modules/feeds/PAL/obscure.properties: Smaller mask width/height
+
+ * src/miracle/miracle_server.c, src/miracle/miracle_server.h: Fetch unit from
+ miracle server
+
+2005-01-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c: Correction to clip_start at end of playlist
+
+2004-12-31 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/demo.ini, src/framework/mlt_producer.c,
+ src/framework/mlt_properties.c, src/framework/mlt_property.c,
+ src/framework/mlt_transition.c: Corrections after valgrinding
+
+ * demo/demo.ini, demo/mlt_attributes, demo/mlt_news, demo/mlt_slideshow,
+ demo/mlt_slideshow_black, demo/mlt_squeeze, demo/mlt_ticker,
+ demo/mlt_watermark: Corrections and minor fixes to use new geometry spec;
+ couple of new test cases
+
+ * src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c,
+ src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+ src/modules/inigo/producer_inigo.c: Sundry minor updates
+
+ * src/modules/feeds/NTSC/obscure.properties,
+ src/modules/feeds/PAL/data_fx.properties: Feeds updates
+
+ * src/framework/mlt_producer.c: Extension to mini fezzik for obscures on cuts
+
+
+ * src/framework/mlt_tractor.c: Option to hold feed processing on a track
+
+ * src/framework/mlt_playlist.c: Fix for join length correction
+
+ * src/framework/mlt_frame.c: Resize fix for chroma offsets
+
+ * src/framework/mlt_geometry.c, src/framework/mlt_geometry.h: Improved
+ geometry
+
+2004-12-28 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/jackrack/filter_jackrack.c: bootstrap earlier with fixed number
+ of channels, better initial synchronisation phase, reduced internal buffer
+ size
+
+ * src/modules/jackrack/filter_jackrack.c: even better close handling?
+
+ * src/modules/jackrack/filter_jackrack.c: fixup includes
+
+ * src/modules/jackrack/filter_jackrack.c: ensure disconnected from jack
+ before releasing any resources
+
+2004-12-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/modules/jackrack/filter_jackrack.c: add
+ filter/jackrack to services.txt and apply a performance tweak to
+ filter_jackrack
+
+ * src/modules/jackrack/Makefile, src/modules/jackrack/configure,
+ src/modules/jackrack/control_message.h, src/modules/jackrack/factory.c,
+ src/modules/jackrack/filter_jackrack.c,
+ src/modules/jackrack/filter_jackrack.h, src/modules/jackrack/jack_rack.c,
+ src/modules/jackrack/jack_rack.h, src/modules/jackrack/lock_free_fifo.c,
+ src/modules/jackrack/lock_free_fifo.h, src/modules/jackrack/plugin.c,
+ src/modules/jackrack/plugin.h, src/modules/jackrack/plugin_desc.c,
+ src/modules/jackrack/plugin_desc.h, src/modules/jackrack/plugin_mgr.c,
+ src/modules/jackrack/plugin_mgr.h, src/modules/jackrack/plugin_settings.c,
+ src/modules/jackrack/plugin_settings.h, src/modules/jackrack/process.c,
+ src/modules/jackrack/process.h, src/modules/jackrack/ui.c,
+ src/modules/jackrack/ui.h: added jackrack filter
+
+ * demo/consumers.ini, docs/services.txt, setenv, setenv_mc,
+ src/modules/dv/producer_libdv.c, src/modules/fezzik.dict,
+ src/modules/fezzik.ini: fix aspect ratios in producer_libdv tweak fezzik
+ priorities minor fixes to setenv and demo/consumers.ini
+
+2004-12-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_bouncy_ball, demo/mlt_my_name_is, demo/mlt_title_over_gfx,
+ src/framework/mlt_tractor.c, src/modules/core/filter_rescale.c,
+ src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c, src/modules/data_fx.properties,
+ src/modules/feeds/PAL/data_fx.properties, src/modules/plus/filter_affine.c,
+ src/modules/plus/transition_affine.c: Composite distort, fill and titles
+ rework
+
+ * src/modules/core/transition_composite.c, src/modules/feeds/Makefile: Feeds
+ pseudo module added
+
+ * src/modules/feeds/Makefile, src/modules/feeds/NTSC/data_fx.properties,
+ src/modules/feeds/PAL/data_fx.properties,
+ src/modules/feeds/PAL/obscure.properties: Feeds pseudo module added
+
+ * docs/services.txt, src/framework/mlt_frame.c, src/framework/mlt_geometry.c,
+ src/modules/core/filter_data_show.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/data_fx.properties,
+ src/modules/inigo/producer_inigo.c, src/modules/lumas/create_lumas,
+ src/modules/lumas/luma.c: Luma and composite fixes
+
+2004-12-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: Luma generation and use
+
+ * src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/lumas/Makefile,
+ src/modules/lumas/create_lumas, src/modules/lumas/luma.c: Luma generation and
+ use
+
+ * demo/mlt_bouncy_ball, demo/mlt_push, demo/mlt_ticker,
+ src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_geometry.c,
+ src/framework/mlt_geometry.h, src/framework/mlt_types.h,
+ src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c,
+ src/modules/data_fx.properties, src/modules/xine/deinterlace.c: Framework
+ inclusion of geometry
+
+2004-12-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/data_fx.properties: Correction to obscure data_show config
+
+ * src/modules/data_fx.properties: Correction to obscure data_show config
+
+2004-12-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c, src/modules/core/transition_composite.c,
+ src/modules/data_fx.properties: New geometry specification
+
+2004-12-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_tractor.c, src/modules/core/filter_data_feed.c,
+ src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c, src/modules/data_fx.properties,
+ src/modules/gtk2/producer_pango.c, src/modules/westley/producer_westley.c,
+ src/valerie/valerie_remote.c: Feed rework and fixes to westley and composite
+
+2004-12-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c: Mutex protection on put frame close
+
+ * src/framework/mlt_producer.c, src/framework/mlt_service.c: Mutex locking in
+ the get frame
+
+2004-12-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: blank_at method
+ added
+
+2004-12-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: split_at method
+ added
+
+2004-12-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_service.c,
+ src/modules/inigo/producer_inigo.c: Corrections to playlist manipulations and
+ producer type determination
+
+2004-12-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/modules/data_fx.properties,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+ Possible fixes to xlib errors
+
+2004-12-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_data_feed.c: ignore attr which are active, but have
+ no value
+
+ * src/modules/data_fx.properties: Minor mods for ETV data filters
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_factory.c, src/framework/mlt_field.c,
+ src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+ src/framework/mlt_properties.c, src/framework/mlt_property.c,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+ src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+ src/framework/mlt_types.h, src/inigo/inigo.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit_commands.c,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/filter_avresample.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/consumer_null.c,
+ src/modules/core/filter_brightness.c, src/modules/core/filter_channelcopy.c,
+ src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c,
+ src/modules/core/filter_gamma.c, src/modules/core/filter_luma.c,
+ src/modules/core/filter_mirror.c, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_region.c, src/modules/core/filter_rescale.c,
+ src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c,
+ src/modules/core/producer_colour.c, src/modules/core/producer_noise.c,
+ src/modules/core/producer_ppm.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/core/transition_mix.c,
+ src/modules/core/transition_region.c, src/modules/data_fx.properties,
+ src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+ src/modules/fezzik.ini, src/modules/fezzik/producer_fezzik.c,
+ src/modules/fezzik/producer_hold.c, src/modules/gtk2/consumer_gtk2.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/inigo/producer_inigo.c,
+ src/modules/normalize/filter_volume.c, src/modules/plus/filter_affine.c,
+ src/modules/plus/filter_charcoal.c, src/modules/plus/filter_sepia.c,
+ src/modules/plus/transition_affine.c, src/modules/resample/filter_resample.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c, src/modules/sox/filter_sox.c,
+ src/modules/valerie/consumer_valerie.c, src/modules/vorbis/producer_vorbis.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c,
+ src/modules/xine/filter_deinterlace.c, src/valerie/valerie_remote.c: Big
+ modification - switch to macros for parent class access
+
+2004-11-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+ src/framework/mlt_service.c, src/framework/mlt_tractor.c,
+ src/modules/sdl/consumer_sdl_still.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Extendable factories; general
+ producer related modifications; westley storage; sdl_still increased latency
+
+2004-11-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_still.c: Consumer sdl preview correction -
+ attach colour space conversion on start
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_producer.c, src/framework/mlt_service.c,
+ src/framework/mlt_service.h: More playlist modifications; service locking;
+ sticky services on frame
+
+2004-11-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_producer.c: Extendible blank producers
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/modules/sdl/consumer_sdl_still.c, src/modules/valerie/consumer_valerie.c:
+ Added ref_count method to properties; temporary work around for test card;
+ titles with valerie
+
+2004-11-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_transition.c,
+ src/modules/dv/consumer_libdv.c: Playlist and blank rearrangement, fix for
+ mlt_consumer and NULL
+
+2004-11-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_still.c: Increased delay for polling
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: Simplified
+ playlist access
+
+2004-11-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_multitrack.c: Behavioural change - tracks with hide
+ properties now affect length (might be problematic)
+
+2004-11-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_producer.c: Correction for direct playback of a cut
+
+2004-11-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: Fixes threaded pixbuf usage and removes
+ flash when swicthing between sdl preview modes
+
+2004-10-31 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_tokeniser.c, src/modules/fezzik.dict,
+ src/modules/gtk2/factory.c, src/modules/inigo/producer_inigo.c,
+ src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/westley/producer_westley.c: fixes for westley deserialise,
+ preview handling and tokenising amendment
+
+2004-10-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_charcoal.c: Minor optimisation
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_producer.c, src/framework/mlt_tractor.c, src/inigo/inigo.c,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+ src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c,
+ src/modules/core/producer_noise.c, src/modules/core/transition_composite.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+ Attempt at an aspect ratio clean up
+
+2004-10-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_preview.c: Oops - need to parse the size in
+ the preview
+
+ * mlt-config-template, src/framework/configure, src/miracle/configure,
+ src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+ src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/consumer_gtk2.h,
+ src/modules/gtk2/factory.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c,
+ src/modules/westley/producer_westley.c, src/valerie/configure: Minor config
+ fixes and gtk2 consumer added
+
+2004-10-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl_still.c: SDL Preview second checkin
+
+ * src/framework/mlt_consumer.c, src/inigo/inigo.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+ src/modules/sdl/consumer_sdl_still.c: SDL Preview second checkin
+
+2004-10-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/modules/sdl/Makefile, src/modules/sdl/configure,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.h,
+ src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c,
+ src/modules/sdl/factory.c: SDL Preview provisional checkin
+
+2004-10-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/core/transition_mix.c: audio mix and
+ repeated frames
+
+2004-10-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c, src/miracle/miracle_server.c,
+ src/miracle/miracle_server.h: Convenience functionality for properties load
+ and miracle_server_id function
+
+ * src/miracle/miracle_server.c: Server shutdown state oops
+
+ * src/miracle/miracle_server.c: Server shutdown state
+
+2004-10-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/valerie/consumer_valerie.c: Error property for valerie returned
+
+
+2004-10-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/consumer_westley.c, src/valerie/valerie_remote.c:
+ buffer fix and tractor handling
+
+ * src/miracle/miracle_connection.c, src/miracle/miracle_local.c,
+ src/miracle/miracle_server.c, src/miracle/miracle_unit_commands.c,
+ src/miracle/miracle_unit_commands.h, src/modules/valerie/consumer_valerie.c,
+ src/modules/westley/producer_westley.c, src/valerie/valerie.c,
+ src/valerie/valerie.h, src/valerie/valerie_parser.c,
+ src/valerie/valerie_parser.h, src/valerie/valerie_remote.c: Improved push
+ capabilities
+
+2004-10-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_service.c, src/modules/fezzik/producer_fezzik.c,
+ src/modules/valerie/consumer_valerie.c,
+ src/modules/westley/producer_westley.c: Fix for deep westleys and filter
+ in/out points
+
+ * src/framework/mlt_consumer.c: Oops - fix for consumer progressive
+
+ * docs/services.txt, src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_properties.c,
+ src/framework/mlt_tractor.c, src/inigo/inigo.c,
+ src/miracle/miracle_connection.c, src/miracle/miracle_connection.h,
+ src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+ src/modules/core/filter_rescale.c, src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/modules/plus/transition_affine.c: Some fixes for alpha masks
+
+2004-10-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure, src/modules/avformat/producer_avformat.c:
+ Fix for current cvs
+
+2004-10-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c: Mix on Mix and length corrections
+
+2004-10-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/framework.txt, docs/inigo.txt, docs/install.txt: Some documentation
+ updates - more to follow
+
+ * src/framework/mlt_producer.c: Removed fezzik usage from cloning
+
+2004-10-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_filter.c, src/framework/mlt_producer.c,
+ src/framework/mlt_service.c, src/framework/mlt_tractor.c,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/core/filter_data_show.c, src/modules/core/filter_watermark.c,
+ src/modules/plus/filter_affine.c: Revised attached filter handling and clones
+
+
+2004-10-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+ src/modules/core/transition_mix.c: More corrections to frame position and
+ audio/track handling
+
+ * src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_tractor.c,
+ src/modules/core/transition_mix.c: Corrects position and test_audio handling
+
+2004-10-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_tractor.c, src/inigo/inigo.c:
+ Multitrack rearrangement and tractor cleanup
+
+ * src/framework/mlt_parser.c, src/framework/mlt_producer.c: Yikes - another
+ corrections to cloning (oops)
+
+ * src/framework/mlt_multitrack.c, src/framework/mlt_producer.c: Corrections
+ to cloning
+
+ * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_factory.c,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/framework/mlt_multitrack.c, src/framework/mlt_parser.c,
+ src/framework/mlt_parser.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_types.h, src/modules/data_fx.properties,
+ src/modules/inigo/producer_inigo.c, src/modules/plus/filter_affine.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Cloning optimisations and
+ introduction of the service parser
+
+2004-10-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/inigo/producer_inigo.c: Allow filter attachment to clip
+
+2004-10-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_factory.c, src/framework/mlt_service.c,
+ src/framework/mlt_tractor.c, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_data.h, src/modules/core/filter_data_feed.c,
+ src/modules/core/filter_data_show.c, src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c, src/modules/data_fx.properties,
+ src/modules/dv/producer_libdv.c, src/modules/inigo/producer_inigo.c: Data
+ feed and show filters
+
+2004-09-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h: clip and mix manipulation on playlist
+
+2004-09-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_filter.c, src/framework/mlt_service.c,
+ src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c, src/modules/inigo/producer_inigo.c,
+ src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+ Corrections to filter attachment and in/out point handling
+
+ * src/framework/mlt_playlist.c, src/modules/inigo/producer_inigo.c: Ensure
+ join inherits all attached filters; inigo can attach to producer or previous
+ attachment
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/modules/inigo/producer_inigo.c: Checkpoint
+ for current managed cuts (prototype on mix)
+
+2004-09-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_rescale.c, src/modules/core/transition_composite.c:
+ First attempt at a composite clean up
+
+2004-09-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c: Clean up - added new usage options
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/modules/inigo/producer_inigo.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Splits, joins and repeats
+
+2004-09-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/consumer_westley.c: Fix for serialising multiple
+ overlapping mixes
+
+ * src/framework/mlt_playlist.c: Whoops - mix fix
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Corrects cuts with filters
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_transition.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Finalisation of first phase of cut
+ handling (unmanaged)
+
+ * src/framework/mlt_transition.c: Transitions ignore test frames
+
+2004-09-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/modules/inigo/producer_inigo.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Cut management part 2 - corrects
+ playlist split/join and a little bit of mix
+
+ * src/framework/mlt_properties.c: ...gah...
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+ src/framework/mlt_service.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Cut management part 1
+
+ * src/modules/westley/consumer_westley.c: fix for in/out during serialisation
+
+
+2004-09-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_tractor.c: Alpha from the tractor fix
+
+2004-09-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c: Whoops
+
+ * src/framework/mlt_factory.c, src/framework/mlt_properties.c,
+ src/miracle/miracle.c, src/miracle/miracle_local.c,
+ src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+ src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c:
+ Fix to compositing/watermark; miracle/mlt shutdown cleanup
+
+ * src/framework/mlt_service.c, src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c: In/out point handling on attached
+ filters revisted
+
+2004-09-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/consumers.ini, src/modules/avformat/producer_avformat.c,
+ src/modules/gtk2/producer_pixbuf.c: Minor fixes
+
+2004-09-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/valerie/valerie_response.h: Obtain stdio definitions
+
+ * src/miracle/miracle.c, src/miracle/miracle_server.c,
+ src/miracle/miracle_server.h: Extending miracles functionality
+
+2004-09-18 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, src/humperdink/Makefile, src/modules/dv/producer_libdv.c: Build
+ fix and temporary libdv compatability
+
+ * src/framework/mlt_frame.c: aspect ratio fix for test card
+
+ * src/framework/mlt_tractor.c: Aspect ratio fix
+
+ * src/modules/sdl/consumer_sdl.c: Aspect ratio modifications
+
+ * src/miracle/Makefile: Customising the miracle server part 1
+
+2004-09-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/miracle/miracle_connection.c, src/miracle/miracle_local.c,
+ src/miracle/miracle_unit.c, src/miracle/miracle_unit.h,
+ src/miracle/miracle_unit_commands.c, src/miracle/miracle_unit_commands.h,
+ src/modules/avformat/Makefile, src/modules/core/filter_watermark.c,
+ src/modules/core/producer_colour.c, src/modules/core/transition_region.c,
+ src/modules/gtk2/factory.c, src/modules/inigo/producer_inigo.c,
+ src/modules/plus/transition_affine.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/sox/Makefile, src/modules/valerie/Makefile,
+ src/modules/valerie/configure, src/modules/valerie/consumer_valerie.c,
+ src/modules/valerie/consumer_valerie.h, src/modules/valerie/factory.c,
+ src/modules/westley/configure, src/modules/westley/consumer_westley.c,
+ src/modules/westley/factory.c, src/modules/westley/producer_westley.c,
+ src/modules/westley/producer_westley.h, src/valerie/Makefile,
+ src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_parser.c,
+ src/valerie/valerie_parser.h, src/valerie/valerie_remote.c: Consumer valerie,
+ pushes, and assorted modifications
+
+2004-09-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/core/transition_luma.c: Work arounds
+ for scaling related issues
+
+2004-09-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: position fixing
+
+2004-09-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: Ugly temporary hack for aspect ratio
+
+ * src/framework/mlt_playlist.c, src/inigo/inigo.c,
+ src/modules/inigo/producer_inigo.c: Fixes for removed tracks before/after mix
+
+
+ * src/framework/mlt_field.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/modules/inigo/producer_inigo.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Adding the mix part 1
+
+2004-09-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_producer.c,
+ src/framework/mlt_service.c, src/modules/avformat/consumer_avformat.c,
+ src/modules/core/consumer_null.c, src/modules/dv/consumer_libdv.c,
+ src/modules/sdl/consumer_sdl.c: More work with events
+
+2004-09-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, docs/westley.txt,
+ src/modules/westley/producer_westley.c: Major westley rewrite - allows
+ attachable filters
+
+2004-09-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_producer.c, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/inigo/inigo.c,
+ src/modules/core/filter_region.c, src/modules/core/filter_watermark.c,
+ src/modules/core/transition_region.c, src/modules/dv/producer_libdv.c,
+ src/modules/inigo/producer_inigo.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/westley/consumer_westley.c: Filter attachments to services
+
+2004-09-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_multitrack.c, src/framework/mlt_tractor.c: Multitrack and
+ tractor producer-changed event
+
+2004-09-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: Fix occassional sdl core dumps
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_events.c, src/framework/mlt_events.h,
+ src/framework/mlt_playlist.c, src/modules/avformat/consumer_avformat.c,
+ src/modules/core/consumer_null.c, src/modules/dv/consumer_libdv.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c: event
+ fix for playlist and consumer-stopped event
+
+ * src/framework/Makefile, src/framework/mlt_events.c,
+ src/framework/mlt_events.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.c, src/framework/mlt_service.c,
+ src/framework/mlt_types.h, src/modules/plus/transition_affine.c: First draft
+ of event handling
+
+2004-08-31 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/framework/Makefile, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/modules/westley/consumer_westley.c,
+ src/valerie/Makefile: Minor make/configure mods and mlt_frame_waveform mod
+
+2004-08-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c: properly deal with evaluation of magnitude of 2s
+ complement for waveform generation
+
+ * src/framework/mlt_frame.c: new, faster waveform generator that emphasizes
+ gain as opposed to shape
+
+2004-08-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c: bugfix in waveform method
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h: add waveform method
+ to frame
+
+2004-08-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_multitrack.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_tractor.c, src/framework/mlt_tractor.h: Tractor
+ enhancements
+
+ * docs/framework.txt, src/framework/mlt_field.c, src/framework/mlt_field.h,
+ src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+ src/modules/inigo/producer_inigo.c, src/modules/westley/producer_westley.c:
+ New tractor constructor
+
+2004-08-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/modules/fezzik/producer_fezzik.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: Producer filter attach/detach
+ methods; major rework on westley consumer, minor on producer
+
+2004-08-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/framework.txt, setenv_mc, src/framework/mlt_consumer.c,
+ src/framework/mlt_field.c, src/framework/mlt_filter.c,
+ src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+ src/modules/core/producer_colour.c, src/modules/core/producer_noise.c,
+ src/modules/core/producer_ppm.c, src/modules/dv/producer_libdv.c,
+ src/modules/fezzik/producer_hold.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/westley/consumer_westley.c:
+ Mlt Ref Counts and Playlist split/join
+
+2004-08-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_local.c, src/miracle/miracle_unit.c: Unit purge
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: consumer purge
+
+2004-08-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_rescale.c, src/modules/gtk2/factory.c: Colour space
+ conversion with gdkpixbuf scaling
+
+ * src/modules/avformat/producer_avformat.c: Another attempted mjpeg work
+ around
+
+ * src/framework/mlt_consumer.c: Prefil consumer property
+
+2004-08-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_service.c: NULL accpectance for connect/disconnect
+
+2004-08-16 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_field.c,
+ src/framework/mlt_filter.c, src/framework/mlt_frame.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_service.c,
+ src/framework/mlt_tractor.c, src/framework/mlt_transition.c: NULL safety
+ checks
+
+2004-08-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: gop/b frame fix, http/pipe
+ handling and logging off
+
+2004-08-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c, src/modules/avformat/filter_avcolour_space.c,
+ src/modules/avformat/filter_avcolour_space.h: Colour space filter
+
+2004-08-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: Rudimentary rgb24 support
+
+ * src/modules/avformat/producer_avformat.c: optimisations
+
+2004-08-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_region.c: Flexible and animated shapes
+
+2004-08-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/configure, src/modules/avformat/producer_avformat.c:
+ gop size == 0 fix and update to current ffmpeg for cvs co
+
+ * src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+ src/modules/dv/producer_libdv.h: Fix for current libdv
+
+ * src/modules/avformat/producer_avformat.c: Pipe workaround
+
+2004-08-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_watermark.c, src/modules/core/transition_region.c:
+ Mutable shapes on regions
+
+2004-08-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_affine.c: Small modifications to allow seeking
+
+ * src/modules/sdl/consumer_sdl.c: Rectangle added to properties
+
+2004-07-31 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_invert.c: Minor fix to invert
+
+ * src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c: Mutable watermark producer and small
+ optimisation
+
+2004-07-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+ Minor affine modifications
+
+ * src/modules/plus/Makefile, src/modules/plus/configure,
+ src/modules/plus/factory.c, src/modules/plus/filter_affine.c,
+ src/modules/plus/filter_affine.h: Affine filter
+
+2004-07-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c: More mutable properties
+
+2004-07-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/Makefile: link to mad
+
+ * src/modules/core/filter_luma.c, src/modules/core/filter_mirror.c,
+ src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c: Mutable properties
+
+ * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: Allow attached
+ filters when used in playlists
+
+2004-07-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: Allows runtime modifications to
+ region fx
+
+ * src/modules/core/filter_region.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_region.c: Allows runtime modifications to region
+ fx
+
+2004-07-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: Pipe support for audio or video
+ only
+
+2004-07-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+ src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/modules/westley/consumer_westley.c: Filter cleanup and fixes
+
+2004-07-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/modules/xine/Makefile: Swig mods
+
+ * src/modules/avformat/Makefile, src/modules/core/Makefile,
+ src/modules/dv/Makefile, src/modules/fezzik/Makefile,
+ src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+ src/modules/normalize/Makefile, src/modules/plus/Makefile,
+ src/modules/plus/transition_affine.c, src/modules/resample/Makefile,
+ src/modules/sdl/Makefile, src/modules/sox/Makefile,
+ src/modules/vorbis/Makefile, src/modules/westley/Makefile: Fixes for swig
+
+2004-06-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c, src/modules/core/filter_luma.c,
+ src/modules/core/transition_luma.c: consumer avformat fix and silly stuff in
+ lumas
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/inigo/producer_inigo.c: stdout fix for avformat consumer and
+ change of defaults for inigo transition tracks
+
+2004-06-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/filter_sepia.c, src/modules/plus/transition_affine.c:
+ Sepia fix and affine/alpha clean up
+
+ * src/modules/plus/Makefile, src/modules/plus/configure,
+ src/modules/plus/factory.c, src/modules/plus/filter_sepia.c,
+ src/modules/plus/filter_sepia.h, src/modules/plus/transition_affine.c: affine
+ with alpha and a broken sepia
+
+2004-06-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/transition_affine.c: Affine silliness
+
+2004-06-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/modules/configure, src/modules/core/configure,
+ src/modules/core/transition_composite.c, src/modules/dv/configure,
+ src/modules/fezzik/configure, src/modules/gtk2/configure,
+ src/modules/inigo/configure, src/modules/normalize/configure,
+ src/modules/resample/configure, src/modules/sdl/configure,
+ src/modules/sdl/consumer_sdl.c, src/modules/sox/configure,
+ src/modules/vorbis/configure, src/modules/westley/configure,
+ src/modules/xine/configure: Portability modifications to scripts
+
+ * src/modules/plus/Makefile, src/modules/plus/configure,
+ src/modules/plus/factory.c, src/modules/plus/transition_affine.c,
+ src/modules/plus/transition_affine.h: Experimental affine transformation
+
+2004-06-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/plus/Makefile, src/modules/plus/configure,
+ src/modules/plus/factory.c, src/modules/plus/filter_charcoal.c,
+ src/modules/plus/filter_charcoal.h, src/modules/plus/filter_invert.c,
+ src/modules/plus/filter_invert.h: More silliness :-)
+
+2004-06-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure: version bump
+
+ * src/modules/avformat/configure: ffmpeg fixed date for cvs checkout
+
+ * src/modules/avformat/ffmpeg.patch: ffmpeg patch for mandrake build
+
+ * src/modules/avformat/producer_avformat.c: Temporary work around for missing
+ aspect ratio
+
+ * src/framework/mlt_properties.c: Rudimentary arithmetic property assignment
+
+2004-06-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/modules/core/producer_colour.c,
+ src/modules/core/producer_noise.c, src/modules/fezzik.ini,
+ src/modules/gtk2/producer_pixbuf.c, src/tests/charlie.c: Minor tweaks
+
+2004-05-30 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: slightly better seeking in drop
+ frame cases
+
+ * src/modules/sdl/consumer_sdl.c: real_time=0 fix
+
+ * src/modules/avformat/consumer_avformat.c: Update to latest ffmpeg cvs
+
+2004-05-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure: Yet another
+ way to configure ffmpeg
+
+ * src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: Sync with current ffmpeg CVS and
+ minor clean up
+
+2004-05-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/configure, src/framework/mlt_consumer.c,
+ src/framework/mlt_factory.c, src/framework/mlt_pool.c,
+ src/framework/mlt_pool.h, src/framework/mlt_repository.c: slight mods to
+ factory (for future module reporting); pool purge function; consumer drop
+ frame rework
+
+ * src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c: fix for avformat seek < gop; fix
+ for avformat consumer qscale; additional avformat consumer properties
+
+2004-05-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c: Removed unecessary locks in
+ avformat
+
+2004-05-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: audio off
+
+2004-05-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: aspect ratio and locking
+
+2004-05-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt: a clarification
+
+ * src/modules/core/filter_resize.c: set output frame aspect to consumer
+ sample aspect, not display aspect.
+
+ * src/modules/sdl/consumer_sdl.c: fix aspect handling when rescale != none
+
+2004-05-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: last sdl fix for now (sigh)
+
+ * src/modules/sdl/consumer_sdl.c: yet another sdl tweak (sigh)
+
+2004-05-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: last sdl fix for now (sigh)
+
+2004-05-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/consumer_avformat.c, src/modules/fezzik.ini,
+ src/modules/sox/Makefile: sox fix; remove consumer avformat diagnostic
+
+ * src/framework/Makefile, src/framework/mlt_consumer.c,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/consumer_null.c,
+ src/modules/core/consumer_null.h, src/modules/core/factory.c,
+ src/modules/core/producer_noise.c, src/modules/fezzik/producer_hold.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/vorbis/producer_vorbis.c: minor
+ clean ups; added a null consumer for easier valgrind testing
+
+2004-05-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c: audio/video processing swap
+
+2004-05-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/filter_sox.c: fix st.h include
+
+2004-05-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c: test card handling
+
+2004-05-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/modules/sdl/consumer_sdl.c: Audio read ahead and fine tuning
+
+ * src/framework/mlt_consumer.c, src/modules/avformat/producer_avformat.c,
+ src/modules/sdl/consumer_sdl.c: Clean up and border preservation
+
+2004-04-30 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/Makefile, src/framework/mlt_consumer.c,
+ src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/filter_mirror.c,
+ src/modules/fezzik.ini, src/modules/sdl/consumer_sdl.c: Sundry consumer
+ modifications; albino compile fix; minor mods to avformat producer
+
+2004-04-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_deque.h, src/framework/mlt_field.h,
+ src/framework/mlt_filter.h, src/framework/mlt_frame.h,
+ src/framework/mlt_manager.h, src/framework/mlt_multitrack.h,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.h,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.h, src/framework/mlt_repository.h,
+ src/framework/mlt_service.c, src/framework/mlt_service.h,
+ src/framework/mlt_tokeniser.h, src/framework/mlt_tractor.h,
+ src/framework/mlt_transition.h: C++ compatability
+
+2004-04-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure: version shunt
+
+ * README, configure, docs/install.txt, docs/services.txt,
+ src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c,
+ src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/filter_avresample.c,
+ src/modules/avformat/producer_avformat.c, src/modules/configure,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c: config
+ mods; avformat static or shared build; corrections to sdl
+
+2004-04-18 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, docs/services.txt, setenv: GPL checking (provisional
+ implementation), mc scaling docs
+
+ * src/framework/mlt.h: added tokeniser to mlt header
+
+ * src/modules/configure, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_rescale.c, src/modules/core/filter_rescale.h,
+ src/modules/fezzik.ini, src/modules/fezzik/Makefile,
+ src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/configure,
+ src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c: Rescaler and
+ fezzik rework (to allow inclusion of mc scaler)
+
+2004-04-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/Makefile: whoops - missed some libs
+
+ * src/albino/Makefile: albino Makefile cleanup
+
+ * src/modules/dv/Makefile, src/modules/normalize/Makefile,
+ src/modules/sox/Makefile: Makefile cleanup in modules
+
+ * src/modules/sox/Makefile, src/modules/sox/filter_sox.c: switched to
+ mlt_tokeniser and removed libst-config from Makefile
+
+ * src/framework/Makefile, src/framework/mlt_tokeniser.c,
+ src/framework/mlt_tokeniser.h: added mlt_tokeniser
+
+2004-04-16 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sox/filter_sox.c: add more comments
+
+ * src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_volume.c,
+ src/modules/core/filter_volume.h, src/modules/normalize/Makefile,
+ src/modules/normalize/configure, src/modules/normalize/factory.c,
+ src/modules/normalize/filter_volume.c, src/modules/normalize/filter_volume.h,
+ src/modules/sox/Makefile, src/modules/sox/configure,
+ src/modules/sox/factory.c, src/modules/sox/filter_sox.c,
+ src/modules/sox/filter_sox.h: moved filter_volume into a normalize module,
+ added new sox module with filter_sox
+
+2004-04-16 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/ffmpeg/Makefile, src/modules/ffmpeg/audio.sh,
+ src/modules/ffmpeg/configure, src/modules/ffmpeg/factory.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/ffmpeg/producer_ffmpeg.h,
+ src/modules/ffmpeg/video.sh: removed all ffmpeg files
+
+ * src/modules/ffmpeg/Makefile, src/modules/ffmpeg/configure,
+ src/modules/ffmpeg/consumer_ffmpeg.c, src/modules/ffmpeg/consumer_ffmpeg.h,
+ src/modules/ffmpeg/factory.c, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ src/modules/ffmpeg/filter_ffmpeg_dub.h: ffmpeg cleanup
+
+2004-04-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/fezzik/producer_fezzik.c: Change defaults to LGPL deinterlace
+ and resample
+
+ * src/modules/avformat/Makefile, src/modules/avformat/configure,
+ src/modules/avformat/factory.c, src/modules/avformat/filter_avdeinterlace.c,
+ src/modules/avformat/filter_avdeinterlace.h,
+ src/modules/avformat/filter_avresample.c,
+ src/modules/avformat/filter_avresample.h, src/modules/avformat/mmx.h: LGPL
+ deinterlace and resampler
+
+2004-04-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * configure, src/albino/Makefile, src/framework/Makefile,
+ src/framework/mlt_pool.c, src/humperdink/Makefile, src/inigo/Makefile,
+ src/miracle/Makefile, src/miracle/miracle_local.c, src/modules/Makefile,
+ src/modules/avformat/Makefile, src/modules/dv/Makefile,
+ src/modules/gtk2/Makefile, src/modules/resample/Makefile,
+ src/modules/sdl/Makefile, src/modules/vorbis/Makefile,
+ src/modules/westley/Makefile, src/tests/Makefile, src/valerie/Makefile,
+ src/valerie/valerie_socket.c: More configure and build tuning
+
+ * configure, src/modules/configure: Configure and build tuning
+
+ * configure, docs/install.txt, src/albino/Makefile, src/framework/Makefile,
+ src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+ src/modules/avformat/Makefile, src/modules/core/Makefile,
+ src/modules/dv/Makefile, src/modules/fezzik/Makefile,
+ src/modules/ffmpeg/Makefile, src/modules/gtk2/Makefile,
+ src/modules/inigo/Makefile, src/modules/resample/Makefile,
+ src/modules/sdl/Makefile, src/modules/vorbis/Makefile,
+ src/modules/westley/Makefile, src/modules/xine/Makefile, src/tests/Makefile,
+ src/valerie/Makefile: Configure and build tuning
+
+2004-04-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, src/framework/mlt_frame.c, src/modules/Makefile,
+ src/modules/avformat/consumer_avformat.c: Makefile error handling and
+ consumer avformat cleanup
+
+ * docs/install.txt: Installation docs update
+
+2004-04-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+ src/modules/fezzik.dict, src/modules/westley/producer_westley.c: field order
+ normalisation fix, add .vob to fezzik, field order detection for avformat
+
+2004-04-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_filter.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_properties.c,
+ src/framework/mlt_repository.c, src/inigo/inigo.c,
+ src/modules/dv/consumer_libdv.c, src/modules/resample/filter_resample.c,
+ src/modules/sdl/consumer_sdl.c: Memory leaks and resample rework
+
+2004-04-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c: test card and aspect ratio woes continued
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_properties.c: aspect ratio and test card woes
+
+2004-04-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_news, docs/framework.txt, src/framework/mlt_consumer.c,
+ src/framework/mlt_factory.c, src/framework/mlt_frame.c,
+ src/framework/mlt_properties.c, src/modules/fezzik/producer_hold.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/sdl/consumer_sdl.c: hold
+ modifications and test card env var
+
+2004-04-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/demo: remove setenv call
+
+2004-04-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: added setenv_mc
+
+ * setenv_mc, src/modules/sdl/consumer_sdl.c: added setenv_mc
+
+ * demo/demo.ini, demo/mlt_squeeze, demo/mlt_squeeze_box, docs/framework.txt,
+ docs/services.txt, src/modules/core/transition_composite.c: minor mods
+
+2004-03-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt: fix something that got disordered
+
+ * src/modules/westley/producer_westley.c: qualitfy paths of known properties
+ that take a filename with server virtual root
+
+2004-03-30 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/albino/Makefile, src/framework/Makefile,
+ src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_frame.c, src/humperdink/Makefile, src/inigo/Makefile,
+ src/miracle/Makefile, src/miracle/miracle_unit.c,
+ src/modules/avformat/Makefile, src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+ src/modules/dv/Makefile, src/modules/fezzik/Makefile,
+ src/modules/ffmpeg/Makefile, src/modules/gtk2/Makefile,
+ src/modules/inigo/Makefile, src/modules/resample/Makefile,
+ src/modules/sdl/Makefile, src/modules/vorbis/Makefile,
+ src/modules/westley/Makefile, src/modules/xine/Makefile, src/tests/Makefile,
+ src/valerie/Makefile: Minor optimisations, consumer avformat experimentation
+
+2004-03-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/modules/avformat/consumer_avformat.c,
+ src/modules/dv/consumer_libdv.c, src/modules/sdl/consumer_sdl.c: inherit
+ scheduling priority on any created thread
+
+2004-03-29 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_luma.c, src/modules/gtk2/filter_rescale.c:
+ bugfix limits in transition luma
+
+ * demo/consumers.ini, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c: aspect
+ fixes for rescale=none
+
+2004-03-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/valerie/valerie.c: insert fix
+
+ * README, src/framework/configure, src/framework/mlt.h,
+ src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+ src/framework/mlt_pool.c, src/modules/avformat/Makefile,
+ src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c,
+ src/modules/avformat/consumer_avformat.h, src/modules/avformat/factory.c,
+ src/modules/avformat/producer_avformat.c, src/modules/dv/consumer_libdv.c,
+ src/modules/dv/producer_libdv.c, src/modules/sdl/consumer_sdl.c: consumer
+ avformat added, various cleanups and consumer realtime switching
+
+2004-03-28 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, README, configure, mlt-framework.pc.in, mlt-miracle.pc.in,
+ mlt-valerie.pc.in: added pkgconfig files. fixed broken dist-clean make
+ target.
+
+2004-03-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_fade_black, demo/mlt_push, demo/mlt_squeeze, docs/TODO,
+ docs/dvcp.txt, docs/framework.txt, docs/inigo.txt, docs/install.txt,
+ docs/services.txt, docs/testing.txt, docs/valerie.txt, docs/westley.txt: Doc
+ formating
+
+2004-03-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/configure: make install part 2 - building configs
+
+2004-03-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/entity.westley, demo/new.westley, docs/westley.txt,
+ src/modules/westley/Makefile, src/modules/westley/producer_westley.c,
+ src/modules/westley/westley.dtd: added westley.dtd
+
+2004-03-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, configure, mlt-config-template, src/framework/configure,
+ src/miracle/configure, src/valerie/configure: make install part 2 - building
+ configs
+
+ * src/modules/fezzik/Makefile: make install fix
+
+2004-03-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/entity.westley, docs/westley.txt,
+ src/modules/westley/producer_westley.c: fix westley for mixed element text
+ and entity references
+
+2004-03-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, src/modules/Makefile: make install part 1
+
+ * Makefile, README, configure, src/albino/Makefile, src/framework/Makefile,
+ src/framework/config.h, src/humperdink/Makefile, src/inigo/Makefile,
+ src/miracle/Makefile, src/modules/Makefile, src/modules/avformat/Makefile,
+ src/modules/core/Makefile, src/modules/dv/Makefile,
+ src/modules/fezzik/Makefile, src/modules/ffmpeg/Makefile,
+ src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+ src/modules/resample/Makefile, src/modules/sdl/Makefile,
+ src/modules/vorbis/Makefile, src/modules/westley/Makefile,
+ src/modules/xine/Makefile, src/tests/Makefile, src/valerie/Makefile: make
+ install part 1
+
+ * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_pool.c, src/framework/mlt_properties.c,
+ src/modules/dv/producer_libdv.c, src/modules/fezzik.dict,
+ src/modules/fezzik/producer_fezzik.c, src/modules/sdl/consumer_sdl.c: pooling
+ and properties checks; dv decoder stack; factory cleanup registering
+
+2004-03-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/README, demo/entity.westley, docs/services.txt, docs/westley.txt,
+ src/miracle/miracle_unit_commands.c, src/modules/westley/producer_westley.c:
+ enhance miracle LOAD command to accept a service: prefix. enhance
+ producer_westley to apply parameters on url as entities. bugfix
+ producer_westley memory leak.
+
+ * demo/README, demo/pango.westley, src/modules/fezzik/producer_hold.c,
+ src/modules/westley/producer_westley.c: fixed westley/fezzik integration when
+ both service and resource supplied.
+
+2004-03-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_push, demo/new.westley, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: reorganized consumer_westley. added
+ branch tracking and other bugfixes to producer_westley.
+
+2004-03-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_tractor.c: tractor fix
+
+2004-03-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_fade_black, demo/mlt_push, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: added track hiding to westley
+
+2004-03-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_fade_black, demo/mlt_title_over_gfx,
+ demo/mlt_titleshadow_watermark: couple of fixes to hidden tracks
+
+ * src/framework/mlt_multitrack.c: ignore length of hidden tracks
+
+ * demo/consumers.ini, demo/luma1.pgm, demo/mlt_clock_in_and_out,
+ demo/mlt_fade_black, demo/mlt_my_name_is, demo/mlt_news, demo/mlt_squeeze,
+ demo/mlt_title_over_gfx, demo/mlt_voiceover: demo mods for reversed tracks
+
+ * src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_producer.c, src/framework/mlt_tractor.c,
+ src/framework/mlt_transition.c, src/modules/inigo/producer_inigo.c: track
+ reversal and hidden tracks
+
+ * demo/demo, demo/demo.ini, demo/mlt_news, demo/mlt_squeeze: news and squeeze
+ added
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_tractor.c, src/modules/core/transition_composite.c,
+ src/modules/resample/filter_resample.c: Tractor frame handling reworked; fix
+ to composite for key diffs of 1; added mlt_consumer_new for consistency
+
+2004-03-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/README, demo/consumers.ini, demo/demo.ini, demo/mlt_fade_black,
+ demo/mlt_jcut, demo/mlt_jcut2, demo/mlt_lcut, demo/mlt_push, demo/mlt_ticker,
+ docs/services.txt, src/modules/core/producer_colour.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c:
+ remove some progressive flag handling in field renderers bugfix compositing
+ images wider than the frame added more demos
+
+2004-03-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/demo.ini, demo/mlt_jcut, demo/mlt_jcut2: added J Cut demos
+
+2004-03-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_local.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+ src/miracle/miracle_unit_commands.h, src/valerie/valerie.c,
+ src/valerie/valerie.h: added clear to the miracle command set and valerie api
+
+
+2004-03-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/install.txt: minor typos
+
+ * README, demo/consumers.ini, docs/framework.txt, docs/install.txt,
+ docs/services.txt, docs/westley.txt, src/albino/albino.c,
+ src/humperdink/client.c, src/modules/gtk2/producer_pango.c,
+ src/modules/westley/producer_westley.c: documentation updates change some
+ references to dv1394d in the example clients to Miracle. more bugfixes for
+ producer_westley iconv for pango
+
+2004-03-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/miracle/miracle_commands.c,
+ src/miracle/miracle_unit_commands.c: root corrections to miracle
+
+ * src/valerie/valerie.c: quick valerie fix
+
+ * docs/install.txt: Added install.txt
+
+2004-03-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c: null pointer check in end_playlist
+
+2004-03-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c,
+ src/modules/core/producer_colour.c, src/modules/dv/consumer_libdv.c,
+ src/modules/fezzik/Makefile, src/modules/fezzik/configure,
+ src/modules/fezzik/factory.c, src/modules/fezzik/producer_hold.c,
+ src/modules/fezzik/producer_hold.h, src/modules/resample/filter_resample.c,
+ src/tests/dan.c, src/tests/pango.c, src/tests/pixbuf.c: producer hold,
+ experimental ac3 audio support
+
+2004-03-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c: touchup on the producer in/out
+ applied to parent entry
+
+ * demo/circle.svg, demo/demo.kino, demo/new.westley, demo/svg.westley,
+ src/framework/mlt_filter.c, src/framework/mlt_playlist.c,
+ src/modules/fezzik.dict, src/modules/fezzik/producer_fezzik.c,
+ src/modules/westley/producer_westley.c: smarter and harder producer_westley
+
+2004-03-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+ src/modules/fezzik.dict, src/modules/fezzik/producer_fezzik.c,
+ src/modules/sdl/consumer_sdl.c, src/tests/hello.c: in point fix, low latency
+ sdl, minor fixes
+
+2004-03-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/framework.txt, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/modules/Makefile, src/modules/configure,
+ src/modules/core/producer_noise.c, src/modules/fezzik.dict,
+ src/modules/fezzik/producer_fezzik.c: fezzik gets a rhyming dictionary
+
+ * docs/framework.txt, docs/services.txt, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_mirror.c, src/modules/core/filter_mirror.h,
+ src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c,
+ src/modules/core/producer_noise.c, src/modules/core/producer_noise.h,
+ src/modules/fezzik/producer_fezzik.c, src/tests/hello.c: Noise and mirrors
+
+2004-03-18 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/modules/avformat/producer_avformat.c: revert
+ avformat pts offset change and note bug in docs
+
+ * src/modules/inigo/producer_inigo.c: fix brokenness
+
+2004-03-18 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/framework.txt, docs/westley.txt, src/framework/config.h,
+ src/framework/mlt_factory.c, src/framework/mlt_frame.h,
+ src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_types.h,
+ src/modules/core/transition_composite.c, src/modules/sdl/consumer_sdl.c,
+ src/tests/Makefile, src/tests/hello.c: provisional framework docs and
+ corrections
+
+2004-03-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/modules/avformat/producer_avformat.c,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_channelcopy.c,
+ src/modules/core/filter_channelcopy.h,
+ src/modules/resample/filter_resample.c: added filter_channelcopy. enhance
+ filter_resample to reproduce channels when producer does not create as many
+ as consumer requested.
+
+ * src/modules/core/filter_volume.c: bugfix segfault in audio normaliser as
+ well as logical bug in smoothing.
+
+ * docs/services.txt, src/modules/avformat/producer_avformat.c,
+ src/modules/fezzik/producer_fezzik.c, src/modules/inigo/producer_inigo.c:
+ fezzik now accepts service:resource and strips \'avformat:\' before fallback
+ avformat construction. avformat now accepts urls with a format and format
+ parameters designation. updated services.txt for above changes. added a video
+ pts offset to avformat.
+
+2004-03-16 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/luma1.pgm, demo/mlt_obscure, docs/services.txt,
+ src/modules/core/composite_line_yuv_mmx.S, src/modules/core/filter_luma.c,
+ src/modules/core/transition_luma.c, src/modules/fezzik/producer_fezzik.c:
+ updated services docs plus minor fixes discovered during
+
+2004-03-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/README, demo/consumers.ini, demo/demo, demo/mlt_clock_in_and_out,
+ demo/mlt_voiceover: notes for the demo
+
+ * demo/demo.ini: bring into sync with changes
+
+ * src/modules/sdl/consumer_sdl.c: default progressive off
+
+ * demo/circle.png, demo/circle.svg, demo/consumers.ini, demo/luma1.pgm,
+ demo/mlt_bouncy_ball, demo/mlt_composite_transition,
+ demo/mlt_fade_in_and_out, demo/mlt_obscure, demo/mlt_title_over_gfx,
+ demo/mlt_titleshadow_watermark, demo/mlt_voiceover: some demo updates
+
+ * src/modules/core/transition_luma.c: fix distortion in smoothness
+
+ * src/modules/core/filter_gamma.c: fix broken gamma
+
+ * src/modules/core/transition_luma.c: fix field rendering
+
+ * src/modules/core/transition_composite.c: bugfixes with field rendering
+
+ * src/modules/dv/producer_libdv.c: fix aspect
+
+2004-03-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/consumers.ini, demo/demo, src/framework/mlt_consumer.c,
+ src/modules/core/transition_luma.c, src/modules/sdl/consumer_sdl.c: more
+ sdl/consumer tuning and demo updates
+
+2004-03-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * demo/mlt_voiceover, src/framework/mlt_deque.c,
+ src/framework/mlt_properties.c, src/framework/mlt_property.c,
+ src/framework/mlt_property.h, src/modules/sdl/consumer_sdl.c: more small
+ optimisations
+
+ * demo/demo: demo framework added
+
+ * demo/demo, demo/demo.ini, demo/luma1.pgm, demo/mlt_all,
+ demo/mlt_audio_stuff, demo/mlt_avantika_title, demo/mlt_bouncy,
+ demo/mlt_bouncy_ball, demo/mlt_clock_in_and_out,
+ demo/mlt_composite_transition, demo/mlt_effect_in_middle,
+ demo/mlt_fade_in_and_out, demo/mlt_intro, demo/mlt_levels,
+ demo/mlt_my_name_is, demo/mlt_obscure, demo/mlt_slideshow,
+ demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark, demo/mlt_voiceover,
+ demo/mlt_watermark, demo/pango.westley, demo/watermark1.png,
+ docs/westley.txt, setenv, src/inigo/io.c, src/modules/dv/producer_libdv.c,
+ src/modules/sdl/consumer_sdl.c: demo framework added
+
+2004-03-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/Makefile, src/modules/core/composite_line_yuv_mmx.S,
+ src/modules/core/filter_resize.c, src/modules/core/transition_composite.c,
+ src/modules/gtk2/filter_rescale.c: added very preliminary mmx for composite.
+ bugfixes to -x and too small rescaling.
+
+2004-03-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/modules/core/transition_mix.c, src/modules/core/transition_region.c: RPN
+ clean up for frames
+
+ * docs/inigo.txt, docs/westley.txt, src/framework/mlt_consumer.c,
+ src/modules/westley/producer_westley.c: Minor fixes to westley and
+ mlt_consumer; first draft westley docs
+
+2004-03-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c: pgm scaling in transition_composite.
+ optimisations for luma producer.
+
+2004-03-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+ src/modules/core/filter_resize.c, src/modules/core/producer_ppm.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/modules/dv/producer_libdv.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/westley/producer_westley.c: add
+ luma to composite. rework aspect handling to use sample aspect. workaround
+ westley segfault when another instance of libxml2 is used. improved inline
+ xml handling in westley - pango and svg.
+
+2004-03-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/modules/dv/consumer_libdv.c: experimental
+ tuning
+
+2004-03-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/xine/attributes.h, src/modules/xine/xineutils.h: add missing
+ header
+
+2004-03-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c,
+ src/modules/sdl/consumer_sdl.c: tunable read ahead buffer and fix for luma
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/humperdink/client.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+ src/modules/dv/consumer_libdv.c, src/modules/sdl/consumer_sdl.c,
+ src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_status.c,
+ src/valerie/valerie_status.h: consumer read ahead and int32_t migration
+
+2004-03-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_luma.c: reorg transition_luma to support
+ producer
+
+ * src/modules/Makefile, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_deinterlace.c, src/modules/core/filter_deinterlace.h,
+ src/modules/core/producer_colour.c, src/modules/xine/Makefile,
+ src/modules/xine/configure, src/modules/xine/cpu_accel.c,
+ src/modules/xine/deinterlace.c, src/modules/xine/deinterlace.h,
+ src/modules/xine/factory.c, src/modules/xine/filter_deinterlace.c,
+ src/modules/xine/filter_deinterlace.h, src/modules/xine/xineutils.h: added
+ xine-based accellerated deinterlace
+
+2004-03-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_frame.h, src/framework/mlt_properties.c,
+ src/framework/mlt_service.h, src/framework/mlt_types.h,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_region.c,
+ src/modules/core/transition_region.c, src/modules/core/transition_region.h:
+ transition region
+
+2004-03-03 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_deinterlace.c: optimise deinterlace path
+
+ * src/modules/core/producer_colour.c: producer_colour
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/producer_colour.c,
+ src/modules/core/producer_colour.h: producer_colour
+
+ * src/framework/mlt_frame.c: more accurate and scaled rgb to yuv conversion
+
+2004-03-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_multitrack.c, src/framework/mlt_pool.c,
+ src/framework/mlt_pool.h, src/framework/mlt_properties.c,
+ src/framework/mlt_property.c, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_types.h, src/inigo/inigo.c,
+ src/modules/avformat/producer_avformat.c, src/modules/core/filter_region.c,
+ src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c: Yet
+ more sdl hacking, region memory leak fix, mlt_position changed to int32_t,
+ experimental hash in properties
+
+2004-03-03 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/core/filter_region.c,
+ src/modules/core/transition_composite.c,
+ src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/westley/producer_westley.c: some bugfixes, filter_shape producer,
+ pixbuf takes svg xml, fezzik can take a service name
+
+2004-03-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: More SDL fixes
+
+2004-03-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: yet more sdl hacks
+
+2004-03-01 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c, src/modules/dv/producer_libdv.c,
+ src/modules/gtk2/scale_line_22_yuv_mmx.S: much improved mmx yuv scaler added
+ producer_libdv quality property improve avformat aspect_ratio and frame_rate
+ reporting
+
+2004-03-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/sdl/consumer_sdl.c: Minor sdl hack
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+ src/framework/mlt_factory.h, src/framework/mlt_frame.c,
+ src/framework/mlt_producer.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/sdl/consumer_sdl.c: sdl hacks
+
+2004-02-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_region.c,
+ src/modules/core/filter_region.h, src/modules/core/filter_watermark.c,
+ src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h: regionalised fx part 1
+
+ * src/framework/mlt_factory.c, src/modules/core/filter_watermark.c,
+ src/modules/dv/producer_libdv.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c: unique ids
+
+2004-02-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/scale_line_22_yuv_mmx.S: bugfix mmx scaling with
+ performance loss :-(
+
+2004-02-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/core/filter_resize.c,
+ src/modules/dv/consumer_libdv.c: Scaling experimentation
+
+2004-02-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c,
+ src/modules/gtk2/scale_line_22_33_mmx.S,
+ src/modules/gtk2/scale_line_22_yuv_mmx.S: mmx version of non-nearest, 2x2
+ rescaling
+
+2004-02-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: composite alignment fix
+
+2004-02-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c,
+ src/modules/gtk2/scale_line_22_33_mmx.S: updated mmx yuv scaling
+
+2004-02-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_luma.c, src/modules/core/filter_luma.h,
+ src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c: composite aspect ratio fix (again ;-)),
+ added fill compositing test case, filter luma, mlt_properties_pass and sundry
+ fixes
+
+2004-02-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: composite key frames
+
+ * docs/TODO, src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/modules/avformat/producer_avformat.c,
+ src/modules/core/filter_deinterlace.c, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/sdl/consumer_sdl.c: service
+ stack, various fixes
+
+2004-02-24 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c: field rendering fix and disable
+ scaling height when normalising pixel aspect when output pixel aspect < 1
+
+2004-02-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/framework/mlt_frame.c, src/framework/mlt_producer.h,
+ src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c,
+ src/modules/core/filter_watermark.h, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/resample/filter_resample.c:
+ watermark added, minor mods to mlt framework required
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_filter.c,
+ src/framework/mlt_filter.h, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/modules/core/filter_brightness.c,
+ src/modules/core/filter_deinterlace.c, src/modules/core/filter_gamma.c,
+ src/modules/core/filter_greyscale.c, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_resize.c, src/modules/core/transition_composite.c,
+ src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c: Filter optimisations and cleanup part 1
+
+2004-02-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_producer.c,
+ src/modules/avformat/producer_avformat.c,
+ src/modules/fezzik/producer_fezzik.c: Minor fixes
+
+ * src/modules/core/transition_luma.c, src/modules/sdl/consumer_sdl.c: sdl
+ rework (prepatory read-ahead implementation) and luma work around
+
+ * src/framework/mlt_pool.c, src/framework/mlt_pool.h,
+ src/modules/core/transition_luma.c: Big luma optimisations, minor pooling
+ optimisations
+
+2004-02-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c:
+ composite alpha operations, make obscure alpha aware
+
+2004-02-21 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+ src/tests/Makefile, src/tests/dan.c: fix broken aspect handling again
+
+2004-02-21 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_pool.c, src/modules/avformat/producer_avformat.c,
+ src/modules/dv/producer_libdv.c: avformat whoops, pooling claridication and
+ removal of dv leak
+
+2004-02-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/Makefile, src/framework/Makefile, src/framework/mlt_frame.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_pool.c,
+ src/framework/mlt_pool.h, src/framework/mlt_properties.c,
+ src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+ src/modules/avformat/producer_avformat.c, src/modules/core/producer_ppm.c,
+ src/modules/core/transition_luma.c, src/modules/dv/producer_libdv.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/resample/filter_resample.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/vorbis/producer_vorbis.c, src/tests/Makefile,
+ src/valerie/Makefile: Memory pooling part 2 and other optimisations
+
+2004-02-19 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_luma.c: more dissolve optimisation
+
+ * src/modules/core/transition_luma.c: optimise dissolve case
+
+2004-02-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/framework/Makefile, src/framework/mlt_factory.c,
+ src/framework/mlt_frame.c, src/framework/mlt_pool.c,
+ src/framework/mlt_pool.h, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/framework/mlt_types.h,
+ src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+ src/modules/core/producer_ppm.c, src/modules/core/transition_luma.c,
+ src/modules/dv/producer_libdv.c, src/modules/ffmpeg/producer_ffmpeg.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+ src/modules/resample/filter_resample.c, src/modules/vorbis/producer_vorbis.c:
+ Memory pooling
+
+2004-02-19 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c: field rendering and alignment for
+ composite, bugfixes for luma, pixbuf and pango
+
+2004-02-18 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c: create consumer_progressive property on frame
+
+
+ * src/modules/sdl/consumer_sdl.c: default progressive on
+
+ * src/modules/westley/consumer_westley.c: consumer_westley now only puts
+ in/out as element attributes and not property elements
+
+ * src/modules/core/filter_deinterlace.c,
+ src/modules/core/transition_composite.c, src/modules/core/transition_luma.c:
+ split getting of b_frame image and composite
+
+2004-02-18 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/albino/Makefile, src/framework/Makefile, src/framework/mlt_consumer.c,
+ src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+ src/framework/mlt_frame.c, src/framework/mlt_properties.c,
+ src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+ src/miracle/miracle_local.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit_commands.c, src/modules/avformat/Makefile,
+ src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+ src/modules/core/filter_obscure.c, src/modules/core/filter_resize.c,
+ src/modules/core/transition_composite.c, src/modules/dv/Makefile,
+ src/modules/fezzik/Makefile, src/modules/ffmpeg/Makefile,
+ src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/inigo/Makefile,
+ src/modules/resample/Makefile, src/modules/sdl/Makefile,
+ src/modules/sdl/consumer_sdl.c, src/modules/vorbis/Makefile,
+ src/modules/westley/Makefile, src/modules/westley/producer_westley.c,
+ src/tests/Makefile, src/valerie/Makefile: Optimisations (part 0), pixel v
+ percentage, reworked aspect ratio calcs, ante/post properties for dv
+ consumers, avformat rework, westley root
+
+2004-02-16 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/transition_composite.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/sdl/consumer_sdl.c: bug fixes
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+ src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+ src/modules/core/filter_resize.c, src/modules/core/producer_ppm.c,
+ src/modules/core/producer_ppm.h, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/dv/producer_libdv.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c:
+ westley serialises with entry in/out; full field, aspect, and colour space
+ normalisation; scaling overlays to consumer size; tagged frame mallocs with
+ //IRRIGATE ME
+
+2004-02-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_consumer.c, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h: Properties rename and dump function
+
+ * docs/testing-20040110.txt, src/framework/mlt_consumer.c,
+ src/framework/mlt_consumer.h, src/framework/mlt_frame.c,
+ src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+ src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c:
+ Defaults for PAL/NTSC on producers and consumers
+
+2004-02-13 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_brightness.c,
+ src/modules/core/filter_brightness.h, src/modules/core/filter_volume.c,
+ src/modules/core/transition_mix.c, src/modules/gtk2/filter_rescale.c: added
+ brightness filter, added smooth ramping to audio processing, added start/end
+ interpolation points to filter_mix and filter_volume
+
+2004-02-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/Makefile, mlt/README, mlt/configure, mlt/docs/dvcp.txt,
+ mlt/docs/inigo.txt, mlt/docs/services.txt, mlt/docs/testing-20040110.txt,
+ mlt/docs/testing.txt, mlt/docs/valerie.txt, mlt/setenv,
+ mlt/src/albino/Makefile, mlt/src/albino/albino.c, mlt/src/framework/Makefile,
+ mlt/src/framework/config.h, mlt/src/framework/configure,
+ mlt/src/framework/mlt.h, mlt/src/framework/mlt_consumer.c,
+ mlt/src/framework/mlt_consumer.h, mlt/src/framework/mlt_factory.c,
+ mlt/src/framework/mlt_factory.h, mlt/src/framework/mlt_field.c,
+ mlt/src/framework/mlt_field.h, mlt/src/framework/mlt_filter.c,
+ mlt/src/framework/mlt_filter.h, mlt/src/framework/mlt_frame.c,
+ mlt/src/framework/mlt_frame.h, mlt/src/framework/mlt_manager.h,
+ mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+ mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_properties.h,
+ mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+ mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_repository.h,
+ mlt/src/framework/mlt_service.c, mlt/src/framework/mlt_service.h,
+ mlt/src/framework/mlt_tractor.c, mlt/src/framework/mlt_tractor.h,
+ mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+ mlt/src/framework/mlt_types.h, mlt/src/humperdink/Makefile,
+ mlt/src/humperdink/client.c, mlt/src/humperdink/client.h,
+ mlt/src/humperdink/io.c, mlt/src/humperdink/io.h,
+ mlt/src/humperdink/remote.c, mlt/src/inigo/Makefile, mlt/src/inigo/inigo.c,
+ mlt/src/inigo/io.c, mlt/src/inigo/io.h, mlt/src/miracle/Makefile,
+ mlt/src/miracle/configure, mlt/src/miracle/miracle.c,
+ mlt/src/miracle/miracle_commands.c, mlt/src/miracle/miracle_commands.h,
+ mlt/src/miracle/miracle_connection.c, mlt/src/miracle/miracle_connection.h,
+ mlt/src/miracle/miracle_local.c, mlt/src/miracle/miracle_local.h,
+ mlt/src/miracle/miracle_log.c, mlt/src/miracle/miracle_log.h,
+ mlt/src/miracle/miracle_server.c, mlt/src/miracle/miracle_server.h,
+ mlt/src/miracle/miracle_unit.c, mlt/src/miracle/miracle_unit.h,
+ mlt/src/miracle/miracle_unit_commands.c,
+ mlt/src/miracle/miracle_unit_commands.h, mlt/src/modules/Makefile,
+ mlt/src/modules/configure, mlt/src/modules/core/Makefile,
+ mlt/src/modules/core/configure, mlt/src/modules/core/factory.c,
+ mlt/src/modules/core/filter_deinterlace.c,
+ mlt/src/modules/core/filter_deinterlace.h,
+ mlt/src/modules/core/filter_gamma.c, mlt/src/modules/core/filter_gamma.h,
+ mlt/src/modules/core/filter_greyscale.c,
+ mlt/src/modules/core/filter_greyscale.h,
+ mlt/src/modules/core/filter_resize.c, mlt/src/modules/core/filter_resize.h,
+ mlt/src/modules/core/filter_volume.c, mlt/src/modules/core/filter_volume.h,
+ mlt/src/modules/core/producer_ppm.c, mlt/src/modules/core/producer_ppm.h,
+ mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/core/transition_composite.h,
+ mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/core/transition_luma.h,
+ mlt/src/modules/core/transition_mix.c, mlt/src/modules/core/transition_mix.h,
+ mlt/src/modules/dv/Makefile, mlt/src/modules/dv/configure,
+ mlt/src/modules/dv/consumer_libdv.c, mlt/src/modules/dv/consumer_libdv.h,
+ mlt/src/modules/dv/factory.c, mlt/src/modules/dv/producer_libdv.c,
+ mlt/src/modules/dv/producer_libdv.h, mlt/src/modules/ffmpeg/Makefile,
+ mlt/src/modules/ffmpeg/audio.sh, mlt/src/modules/ffmpeg/configure,
+ mlt/src/modules/ffmpeg/consumer_ffmpeg.c,
+ mlt/src/modules/ffmpeg/consumer_ffmpeg.h, mlt/src/modules/ffmpeg/factory.c,
+ mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ mlt/src/modules/ffmpeg/filter_ffmpeg_dub.h,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.h, mlt/src/modules/ffmpeg/video.sh,
+ mlt/src/modules/gtk2/Makefile, mlt/src/modules/gtk2/configure,
+ mlt/src/modules/gtk2/factory.c, mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pango.h,
+ mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/inigo/Makefile,
+ mlt/src/modules/inigo/configure, mlt/src/modules/inigo/factory.c,
+ mlt/src/modules/inigo/producer_inigo.c,
+ mlt/src/modules/inigo/producer_inigo.h, mlt/src/modules/resample/Makefile,
+ mlt/src/modules/resample/configure, mlt/src/modules/resample/factory.c,
+ mlt/src/modules/resample/filter_resample.c,
+ mlt/src/modules/resample/filter_resample.h, mlt/src/modules/sdl/Makefile,
+ mlt/src/modules/sdl/configure, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/modules/sdl/consumer_sdl.h, mlt/src/modules/sdl/factory.c,
+ mlt/src/modules/westley/Makefile, mlt/src/modules/westley/configure,
+ mlt/src/modules/westley/consumer_westley.c,
+ mlt/src/modules/westley/consumer_westley.h,
+ mlt/src/modules/westley/factory.c,
+ mlt/src/modules/westley/producer_westley.c,
+ mlt/src/modules/westley/producer_westley.h, mlt/src/tests/Makefile,
+ mlt/src/tests/charlie.c, mlt/src/tests/clock16ntsc.pgm,
+ mlt/src/tests/clock16pal.pgm, mlt/src/tests/dan.c, mlt/src/tests/dissolve.c,
+ mlt/src/tests/io.c, mlt/src/tests/io.h, mlt/src/tests/luma.c,
+ mlt/src/tests/pango.c, mlt/src/tests/pixbuf.c, mlt/src/tests/setenv,
+ mlt/src/tests/test.png, mlt/src/valerie/Makefile, mlt/src/valerie/configure,
+ mlt/src/valerie/valerie.c, mlt/src/valerie/valerie.h,
+ mlt/src/valerie/valerie_notifier.c, mlt/src/valerie/valerie_notifier.h,
+ mlt/src/valerie/valerie_parser.c, mlt/src/valerie/valerie_parser.h,
+ mlt/src/valerie/valerie_remote.c, mlt/src/valerie/valerie_remote.h,
+ mlt/src/valerie/valerie_response.c, mlt/src/valerie/valerie_response.h,
+ mlt/src/valerie/valerie_socket.c, mlt/src/valerie/valerie_socket.h,
+ mlt/src/valerie/valerie_status.c, mlt/src/valerie/valerie_status.h,
+ mlt/src/valerie/valerie_tokeniser.c, mlt/src/valerie/valerie_tokeniser.h,
+ mlt/src/valerie/valerie_util.c, mlt/src/valerie/valerie_util.h: remove child
+ mlt dir
+
+ * docs/TODO, src/miracle/miracle_local.c: add TODO
+
+2004-02-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_producer.c: test card handling
+
+ * src/miracle/miracle_local.c: optional segv handling
+
+2004-02-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_local.c: change segv handler to use log facility
+
+ * src/valerie/valerie_notifier.c: cleanup
+
+ * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+ src/miracle/miracle_local.c, src/valerie/valerie_notifier.c: segv handler,
+ playlist_move bugfix, resize_yuv422 optimisation
+
+2004-02-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_unit.c: first of a few local tests
+
+ * src/miracle/miracle_commands.c: first of a few local tests
+
+ * docs/testing-20040110.txt, src/framework/mlt_frame.c,
+ src/miracle/miracle_unit.c, src/miracle/miracle_unit_commands.c,
+ src/modules/dv/consumer_libdv.c, src/valerie/valerie_notifier.c,
+ src/valerie/valerie_notifier.h: Miracle mods - clean working, test card fix,
+ silence dv when not playing
+
+2004-02-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/testing-20040110.txt, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+ src/miracle/miracle_unit.c, src/valerie/valerie_notifier.c,
+ src/valerie/valerie_status.h: Miracle mods
+
+2004-02-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * setenv, src/framework/mlt_producer.c, src/modules/fezzik/producer_fezzik.c,
+ src/modules/resample/filter_resample.c,
+ src/modules/westley/producer_westley.c: bugfixes
+
+2004-02-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_filter.c, src/framework/mlt_frame.c: filter fixes
+
+2004-02-09 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_volume.c: remove spurious return in get_audio
+
+2004-02-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/miracle/miracle_unit.c, src/modules/dv/consumer_libdv.c: brought by a
+ resizable bunny
+
+ * src/modules/dv/consumer_libdv.c: brought by a bunny
+
+ * docs/services.txt, src/modules/gtk2/producer_pango.c: pango colour handling
+
+
+2004-02-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/modules/core/transition_luma.c: luma funkiness
+
+ * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/modules/core/transition_composite.c,
+ src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c:
+ pixbuf, composite and fezzik mirrors
+
+2004-02-07 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/producer_westley.c: support in/out on entry and track
+
+2004-02-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/producer_pango.c: pango producer rework
+
+ * src/modules/avformat/producer_avformat.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/westley/producer_westley.c: Minor corrections, rescale=nearest
+ for sdl
+
+2004-02-07 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/filter_rescale.c: rescale aspect handling redux
+
+ * src/modules/avformat/producer_avformat.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c: fixup and disable rescale changes
+
+2004-02-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/core/filter_volume.c,
+ src/modules/dv/producer_libdv.c, src/modules/fezzik/producer_fezzik.c,
+ src/modules/gtk2/filter_rescale.c, src/modules/resample/filter_resample.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: some bugfixes, westley property
+ handling reorg, make rescale respect the aspect ratio, make resize update the
+ aspect ratio, add resize to fezzik
+
+2004-02-06 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, src/modules/core/filter_obscure.c,
+ src/modules/core/transition_composite.c: composite
+
+ * src/framework/mlt_factory.c, src/framework/mlt_tractor.c,
+ src/miracle/miracle_unit.c, src/modules/Makefile,
+ src/modules/fezzik/Makefile, src/modules/fezzik/configure,
+ src/modules/fezzik/factory.c, src/modules/fezzik/producer_fezzik.c,
+ src/modules/fezzik/producer_fezzik.h, src/modules/inigo/producer_inigo.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: adding the rock thrower...
+
+2004-02-05 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/resample/filter_resample.c: resample fix
+
+ * docs/services.txt, setenv, src/framework/mlt_frame.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_producer.c,
+ src/framework/mlt_transition.c, src/miracle/miracle_unit.c,
+ src/modules/dv/producer_libdv.c, src/modules/ffmpeg/consumer_ffmpeg.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/resample/filter_resample.c,
+ src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: westley/libxml2 mods, mcdv/mpeg
+ release integration
+
+2004-02-05 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/inigo.txt, src/framework/mlt_frame.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: bugfixes to westley
+
+2004-02-04 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/gtk2/pixops.c: final rescale improvement and some optimisation
+
+ * src/modules/gtk2/pixops.c: near final rescale improvements?
+
+ * src/modules/gtk2/pixops.c: interim rescale improvements
+
+ * src/modules/gtk2/pixops.c: interim rescale improvements
+
+ * src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h: interim rescale
+ improvements
+
+2004-02-04 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_factory.c, src/framework/mlt_field.c,
+ src/framework/mlt_repository.c, src/framework/mlt_tractor.c,
+ src/inigo/inigo.c, src/miracle/miracle_unit.c, src/modules/Makefile,
+ src/modules/core/filter_obscure.c, src/modules/inigo/configure,
+ src/modules/inigo/factory.c, src/modules/inigo/producer_inigo.c,
+ src/modules/inigo/producer_inigo.h, src/modules/westley/producer_westley.c:
+ pre-beta cleanup part 1
+
+2004-02-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/inigo/inigo.c, src/modules/avformat/producer_avformat.c,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_obscure.c,
+ src/modules/core/filter_obscure.h, src/modules/inigo/Makefile,
+ src/modules/inigo/configure, src/modules/inigo/producer_inigo.c,
+ src/modules/vorbis/Makefile: obscurer filter, consistency mods and bug fixes
+
+ * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_deque.c,
+ src/framework/mlt_deque.h, src/framework/mlt_factory.c,
+ src/framework/mlt_field.c, src/framework/mlt_frame.c,
+ src/framework/mlt_manager.h, src/framework/mlt_repository.c,
+ src/framework/mlt_types.h: added deque, api design for manager, minor affine
+ tweaks, experimental destructor work
+
+2004-01-31 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt: doc updates
+
+ * src/modules/core/filter_volume.c: configurable window size on volume
+ normalisation, also set default of max_gain to 20dB
+
+2004-01-30 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c: updated affine
+
+2004-01-30 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_frame.c, src/modules/avformat/producer_avformat.c,
+ src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+ src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c,
+ src/modules/gtk2/filter_rescale.h, src/modules/gtk2/have_mmx.S,
+ src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/scale_line_22_33_mmx.S,
+ src/modules/vorbis/Makefile: some bugfixes and rescale filter
+
+2004-01-28 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_volume.c: comment some diagnostics
+
+ * docs/services.txt, src/modules/core/filter_volume.c: doc updates; property
+ changes, and tweaks for volume filter normalisation
+
+2004-01-27 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/core/filter_volume.c, src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: westley bugfixes and audio
+ normalisation
+
+2004-01-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * README, docs/services.txt, src/framework/mlt_multitrack.c,
+ src/miracle/miracle_unit.c, src/modules/Makefile,
+ src/modules/avformat/producer_avformat.c, src/modules/inigo/producer_inigo.c,
+ src/modules/vorbis/Makefile, src/modules/vorbis/configure,
+ src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.c,
+ src/modules/vorbis/producer_vorbis.h: vorbis producer added, clean up on clip
+ handling in multitrack
+
+2004-01-26 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: westley updates for non-inline
+ serialisation and code cleanup
+
+2004-01-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_properties.c, src/miracle/miracle_unit.c,
+ src/modules/avformat/producer_avformat.c: mutex protection of avformat,
+ miracle avformat usage, and destrector reversal
+
+ * README, docs/services.txt, src/modules/avformat/producer_avformat.c: Added
+ avformat
+
+ * README, docs/inigo.txt, src/framework/mlt_producer.c, src/inigo/inigo.c,
+ src/modules/Makefile, src/modules/avformat/Makefile,
+ src/modules/avformat/configure, src/modules/avformat/factory.c,
+ src/modules/avformat/producer_avformat.c,
+ src/modules/avformat/producer_avformat.h, src/modules/inigo/producer_inigo.c,
+ src/modules/sdl/consumer_sdl.c: Added avformat
+
+2004-01-25 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * src/framework/mlt_filter.c, src/framework/mlt_transition.c,
+ src/modules/core/transition_luma.c, src/modules/inigo/producer_inigo.c,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/producer_westley.c: updated westley
+
+ * src/tests/dan.c: test cvs
+
+2004-01-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/westley/consumer_westley.c,
+ src/modules/westley/consumer_westley.c: xml based westley serialisation
+
+ * mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_tractor.c,
+ mlt/src/framework/mlt_types.h, mlt/src/modules/westley/consumer_westley.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_tractor.c,
+ src/framework/mlt_types.h, src/modules/westley/consumer_westley.c: xml based
+ westley serialisation
+
+2004-01-21 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, mlt/docs/services.txt,
+ mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+ mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_service.c,
+ mlt/src/framework/mlt_service.h, mlt/src/framework/mlt_tractor.c,
+ mlt/src/framework/mlt_types.h, mlt/src/modules/Makefile,
+ mlt/src/modules/westley/Makefile, mlt/src/modules/westley/configure,
+ mlt/src/modules/westley/consumer_westley.c,
+ mlt/src/modules/westley/consumer_westley.h,
+ mlt/src/modules/westley/factory.c,
+ mlt/src/modules/westley/producer_westley.c,
+ mlt/src/modules/westley/producer_westley.h, mlt/src/tests/dan.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+ src/framework/mlt_repository.c, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+ src/framework/mlt_types.h, src/modules/Makefile,
+ src/modules/westley/Makefile, src/modules/westley/configure,
+ src/modules/westley/consumer_westley.c,
+ src/modules/westley/consumer_westley.h, src/modules/westley/factory.c,
+ src/modules/westley/producer_westley.c,
+ src/modules/westley/producer_westley.h, src/tests/dan.c: added
+ modules/westley
+
+2004-01-20 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/inigo.txt, mlt/docs/inigo.txt, mlt/src/modules/dv/consumer_libdv.c,
+ src/modules/dv/consumer_libdv.c: updated libdv consumer
+
+2004-01-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/inigo.txt, docs/testing-20040110.txt, mlt/docs/inigo.txt,
+ mlt/docs/testing-20040110.txt, mlt/src/framework/mlt_consumer.c,
+ mlt/src/framework/mlt_consumer.h, mlt/src/framework/mlt_frame.c,
+ mlt/src/framework/mlt_tractor.c, mlt/src/inigo/inigo.c,
+ mlt/src/miracle/miracle_unit.c, mlt/src/miracle/miracle_unit_commands.c,
+ mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/core/transition_mix.c, mlt/src/modules/sdl/consumer_sdl.c,
+ src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_frame.c, src/framework/mlt_tractor.c, src/inigo/inigo.c,
+ src/miracle/miracle_unit.c, src/miracle/miracle_unit_commands.c,
+ src/modules/core/transition_luma.c, src/modules/core/transition_mix.c,
+ src/modules/sdl/consumer_sdl.c: inigo docs load/stop corrections
+
+2004-01-17 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_playlist.c,
+ mlt/src/framework/mlt_playlist.h, mlt/src/framework/mlt_producer.c,
+ mlt/src/modules/Makefile, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/modules/Makefile: insert/move/remove dvcp operations
+
+2004-01-17 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/core/transition_mix.c, src/modules/core/transition_mix.c:
+ default mix to 0.5
+
+ * docs/services.txt, mlt/docs/services.txt, mlt/src/miracle/miracle_log.c,
+ mlt/src/miracle/miracle_unit.c, mlt/src/modules/Makefile,
+ mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+ mlt/src/modules/core/factory.c, mlt/src/modules/core/filter_volume.c,
+ mlt/src/modules/core/filter_volume.h,
+ mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/core/transition_composite.h,
+ mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/core/transition_mix.c, mlt/src/modules/core/transition_mix.h,
+ mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/modules/resample/Makefile,
+ mlt/src/modules/resample/configure, mlt/src/modules/resample/factory.c,
+ mlt/src/modules/resample/filter_resample.c,
+ mlt/src/modules/resample/filter_resample.h, mlt/src/tests/luma.c,
+ mlt/src/tests/pango.c, src/miracle/miracle_log.c, src/miracle/miracle_unit.c,
+ src/modules/Makefile, src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_volume.c,
+ src/modules/core/filter_volume.h, src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h, src/modules/core/transition_luma.c,
+ src/modules/core/transition_mix.c, src/modules/core/transition_mix.h,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/resample/Makefile, src/modules/resample/configure,
+ src/modules/resample/factory.c, src/modules/resample/filter_resample.c,
+ src/modules/resample/filter_resample.h, src/tests/luma.c, src/tests/pango.c:
+ new volume, mix, and resample filters and transitions
+
+2004-01-15 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/inigo/inigo.c, src/inigo/inigo.c: inigo usage message
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_tractor.c,
+ mlt/src/modules/inigo/producer_inigo.c, src/framework/mlt_frame.c,
+ src/framework/mlt_tractor.c, src/modules/inigo/producer_inigo.c: finally -
+ multitrack inigo serialisation
+
+ * mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_tractor.c,
+ src/framework/mlt_producer.c, src/framework/mlt_tractor.c: in/out
+ specification on .inigo serialisations
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_multitrack.c,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_producer.c,
+ mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_tractor.c,
+ mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+ mlt/src/inigo/inigo.c, mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/inigo/producer_inigo.c, mlt/src/tests/charlie.c,
+ src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/framework/mlt_properties.c, src/framework/mlt_tractor.c,
+ src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+ src/inigo/inigo.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/inigo/producer_inigo.c,
+ src/tests/charlie.c: partial corrections to serialisation
+
+2004-01-14 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+ mlt/src/framework/mlt_tractor.c, mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/dv/consumer_libdv.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/framework/mlt_tractor.c,
+ src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c,
+ src/modules/ffmpeg/producer_ffmpeg.c: some temporary fixes
+
+ * mlt/src/modules/dv/consumer_libdv.c, src/modules/dv/consumer_libdv.c: Minor
+ mods
+
+ * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+ mlt/src/framework/mlt_producer.c, mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_producer.c, src/modules/ffmpeg/producer_ffmpeg.c: Minor
+ mods
+
+ * mlt/src/framework/mlt_frame.c, src/framework/mlt_frame.c: Minor mods
+
+ * docs/testing-20040110.txt, mlt/docs/testing-20040110.txt,
+ mlt/src/framework/mlt_consumer.c, mlt/src/framework/mlt_factory.c,
+ mlt/src/framework/mlt_field.c, mlt/src/framework/mlt_field.h,
+ mlt/src/framework/mlt_filter.c, mlt/src/framework/mlt_filter.h,
+ mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+ mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+ mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_properties.h,
+ mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+ mlt/src/framework/mlt_tractor.c, mlt/src/framework/mlt_tractor.h,
+ mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+ mlt/src/framework/mlt_types.h, mlt/src/inigo/inigo.c,
+ mlt/src/miracle/miracle_unit.c, mlt/src/modules/core/producer_ppm.c,
+ mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/core/transition_luma.c, mlt/src/modules/dv/Makefile,
+ mlt/src/modules/dv/configure, mlt/src/modules/dv/consumer_libdv.c,
+ mlt/src/modules/dv/consumer_libdv.h, mlt/src/modules/dv/factory.c,
+ mlt/src/modules/dv/producer_libdv.c,
+ mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/inigo/producer_inigo.c, mlt/src/modules/sdl/consumer_sdl.c,
+ src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+ src/framework/mlt_field.c, src/framework/mlt_field.h,
+ src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+ src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+ src/framework/mlt_property.c, src/framework/mlt_property.h,
+ src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+ src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+ src/framework/mlt_types.h, src/inigo/inigo.c, src/miracle/miracle_unit.c,
+ src/modules/core/producer_ppm.c, src/modules/core/transition_composite.c,
+ src/modules/core/transition_luma.c, src/modules/dv/Makefile,
+ src/modules/dv/configure, src/modules/dv/consumer_libdv.c,
+ src/modules/dv/consumer_libdv.h, src/modules/dv/factory.c,
+ src/modules/dv/producer_libdv.c, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/inigo/producer_inigo.c,
+ src/modules/sdl/consumer_sdl.c: Removal of timecodes, consumer libdv,
+ serialisation of inigo
+
+2004-01-13 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * README, docs/testing-20040110.txt, mlt/README,
+ mlt/docs/testing-20040110.txt, mlt/setenv, setenv: minor doc updates
+
+2004-01-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/testing-20040110.txt, mlt/docs/testing-20040110.txt,
+ mlt/src/albino/Makefile, mlt/src/modules/configure, src/albino/Makefile,
+ src/modules/configure: minor testing update
+
+2004-01-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/testing-20040110.txt, docs/testing.txt, mlt/docs/testing-20040110.txt,
+ mlt/docs/testing.txt: update testing.txt for miracle and complete initial
+ testing.txt results
+
+ * docs/services.txt, mlt/docs/services.txt: change bluefish arg
+
+ * docs/testing-20040110.txt, mlt/docs/testing-20040110.txt: updated with user
+ acceptance test results
+
+2004-01-12 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/inigo/inigo.c, mlt/src/modules/inigo/producer_inigo.c,
+ src/inigo/inigo.c, src/modules/inigo/producer_inigo.c: minor corrections
+
+ * mlt/src/inigo/inigo.c, src/inigo/inigo.c: minor corrections
+
+ * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: minor
+ corrections
+
+ * mlt/src/albino/albino.c, mlt/src/miracle/miracle_commands.c,
+ mlt/src/miracle/miracle_connection.c, src/albino/albino.c,
+ src/miracle/miracle_commands.c, src/miracle/miracle_connection.c: minor
+ corrections
+
+ * mlt/src/inigo/inigo.c, mlt/src/modules/inigo/producer_inigo.c,
+ src/inigo/inigo.c, src/modules/inigo/producer_inigo.c: inigo rewrite,
+ producer, serialise and deserialise
+
+ * docs/services.txt, docs/testing-20040110.txt, mlt/docs/services.txt,
+ mlt/docs/testing-20040110.txt, mlt/src/framework/mlt_frame.c,
+ mlt/src/framework/mlt_playlist.c, mlt/src/inigo/inigo.c,
+ mlt/src/miracle/miracle_unit.c, mlt/src/modules/Makefile,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c, mlt/src/modules/inigo/Makefile,
+ mlt/src/modules/inigo/configure, mlt/src/modules/inigo/factory.c,
+ mlt/src/modules/inigo/producer_inigo.c,
+ mlt/src/modules/inigo/producer_inigo.h, mlt/src/modules/sdl/consumer_sdl.c,
+ src/framework/mlt_frame.c, src/framework/mlt_playlist.c, src/inigo/inigo.c,
+ src/miracle/miracle_unit.c, src/modules/Makefile,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/inigo/Makefile,
+ src/modules/inigo/configure, src/modules/inigo/factory.c,
+ src/modules/inigo/producer_inigo.c, src/modules/inigo/producer_inigo.h,
+ src/modules/sdl/consumer_sdl.c: inigo rewrite, producer, serialise and
+ deserialise
+
+2004-01-12 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/testing-20040110.txt, docs/testing.txt, mlt/docs/testing-20040110.txt,
+ mlt/docs/testing.txt: adding testing.txt and initial test results
+
+ * docs/services.txt, mlt/docs/services.txt: pango markup encoding
+
+ * docs/services.txt, mlt/docs/services.txt,
+ mlt/src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.c:
+ doc updates and better control of pixbuf composite property propogation
+
+ * mlt/src/inigo/inigo.c, mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/gtk2/producer_pango.c, mlt/src/modules/gtk2/producer_pango.h,
+ src/inigo/inigo.c, src/modules/core/transition_composite.c,
+ src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.h: better
+ propogating of producer and transition properties to the frame in pango and
+ composite; add pango support to inigo
+
+2004-01-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_frame.c, src/framework/mlt_frame.c: small change to
+ prevent segfault in some transitions time specifications
+
+2004-01-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.c:
+ multitrack eof handling
+
+ * docs/dvcp.txt, docs/valerie.txt, mlt/docs/dvcp.txt, mlt/docs/valerie.txt,
+ mlt/src/framework/mlt_playlist.c, mlt/src/miracle/miracle_unit.c,
+ mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+ src/framework/mlt_playlist.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c: uset and
+ doco
+
+2004-01-11 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/tests/dissolve.c, mlt/src/tests/luma.c, src/tests/dissolve.c,
+ src/tests/luma.c: remove no longer necessary blanks
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/tests/Makefile,
+ mlt/src/tests/clock16ntsc.pgm, mlt/src/tests/clock16pal.pgm,
+ mlt/src/tests/dan.c, mlt/src/tests/dissolve.c, mlt/src/tests/luma.c,
+ mlt/src/tests/pango.c, mlt/src/tests/pixbuf.c, src/framework/mlt_frame.c,
+ src/modules/core/transition_luma.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/tests/Makefile,
+ src/tests/clock16ntsc.pgm, src/tests/clock16pal.pgm, src/tests/dan.c,
+ src/tests/dissolve.c, src/tests/luma.c, src/tests/pango.c,
+ src/tests/pixbuf.c: 4 new tests, bugfixes in pango, pixbuf, transition_luma,
+ and mlt_frame_audio_mix
+
+2004-01-11 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c: eof=continue
+ and eof=pause
+
+ * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: end of
+ playlist position fix
+
+2004-01-10 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/sdl/consumer_sdl.c, mlt/src/tests/dan.c,
+ src/framework/mlt_frame.c, src/modules/core/transition_luma.c,
+ src/modules/sdl/consumer_sdl.c, src/tests/dan.c: attempt to retain samples in
+ mlt_frame_mix_audio, make consumers request the number of samples to
+ get_audio
+
+2004-01-10 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: in/out fix
+
+ * mlt/src/inigo/inigo.c, src/inigo/inigo.c: inigo gets transitions
+
+ * mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+ mlt/src/miracle/miracle_unit.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/miracle/miracle_unit.c: more int64 frame
+ addressing in playlist
+
+2004-01-09 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+ mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+ mlt/src/humperdink/client.c, mlt/src/humperdink/remote.c,
+ mlt/src/miracle/miracle_local.c, mlt/src/miracle/miracle_unit.c,
+ mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+ mlt/src/modules/dv/producer_libdv.c, mlt/src/valerie/valerie.c,
+ mlt/src/valerie/valerie.h, mlt/src/valerie/valerie_status.c,
+ mlt/src/valerie/valerie_status.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_property.c,
+ src/framework/mlt_property.h, src/humperdink/client.c,
+ src/humperdink/remote.c, src/miracle/miracle_local.c,
+ src/miracle/miracle_unit.c, src/miracle/miracle_unit.h,
+ src/miracle/miracle_unit_commands.c, src/modules/dv/producer_libdv.c,
+ src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_status.c,
+ src/valerie/valerie_status.h: int64 based comms and more unit functionality
+
+ * mlt/src/miracle/miracle.c, mlt/src/miracle/miracle_local.c,
+ src/miracle/miracle.c, src/miracle/miracle_local.c: albino
+
+ * Makefile, mlt/Makefile, mlt/setenv, mlt/src/albino/Makefile,
+ mlt/src/albino/albino.c, mlt/src/framework/mlt_transition.c,
+ mlt/src/framework/mlt_transition.h, mlt/src/miracle/Makefile, setenv,
+ src/albino/Makefile, src/albino/albino.c, src/framework/mlt_transition.c,
+ src/framework/mlt_transition.h, src/miracle/Makefile: albino
+
+2004-01-08 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/inigo/inigo.c, src/inigo/inigo.c: inigo track test
+
+ * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_playlist.c,
+ mlt/src/framework/mlt_playlist.h, mlt/src/framework/mlt_properties.c,
+ mlt/src/miracle/miracle_unit.c, mlt/src/miracle/miracle_unit.h,
+ mlt/src/miracle/miracle_unit_commands.c, mlt/src/modules/dv/producer_libdv.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_properties.c, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+ src/modules/dv/producer_libdv.c: More miracle mods
+
+2004-01-08 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.c: some
+ fixes to the fixes
+
+ * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_frame.c,
+ mlt/src/framework/mlt_frame.h, mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/dv/producer_libdv.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/tests/dan.c, src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/modules/core/transition_luma.c, src/modules/dv/producer_libdv.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/sdl/consumer_sdl.c,
+ src/tests/dan.c: move audio sample calculator to mlt_frame and use from
+ ffmpeg and mcmpeg, add mlt_frame_audio_mix, add audio_crossfade to
+ transition_luma, add to docs
+
+2004-01-07 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, docs/services.txt, mlt/Makefile, mlt/docs/services.txt,
+ mlt/setenv, mlt/src/framework/Makefile, mlt/src/framework/mlt_frame.c,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/humperdink/Makefile,
+ mlt/src/humperdink/client.c, mlt/src/humperdink/client.h,
+ mlt/src/humperdink/io.c, mlt/src/humperdink/io.h,
+ mlt/src/humperdink/remote.c, mlt/src/inigo/inigo.c, mlt/src/miracle/Makefile,
+ mlt/src/miracle/miracle.c, mlt/src/miracle/miracle_commands.c,
+ mlt/src/miracle/miracle_commands.h, mlt/src/miracle/miracle_connection.c,
+ mlt/src/miracle/miracle_connection.h, mlt/src/miracle/miracle_local.c,
+ mlt/src/miracle/miracle_local.h, mlt/src/miracle/miracle_log.c,
+ mlt/src/miracle/miracle_log.h, mlt/src/miracle/miracle_server.c,
+ mlt/src/miracle/miracle_server.h, mlt/src/miracle/miracle_unit.c,
+ mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+ mlt/src/miracle/miracle_unit_commands.h, mlt/src/modules/core/producer_ppm.c,
+ mlt/src/modules/dv/producer_libdv.c, mlt/src/modules/ffmpeg/audio.sh,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c, mlt/src/modules/ffmpeg/video.sh,
+ mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/valerie/Makefile, mlt/src/valerie/valerie.c,
+ mlt/src/valerie/valerie.h, setenv, src/framework/Makefile,
+ src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/humperdink/Makefile, src/humperdink/client.c, src/humperdink/client.h,
+ src/humperdink/io.c, src/humperdink/io.h, src/humperdink/remote.c,
+ src/inigo/inigo.c, src/miracle/Makefile, src/miracle/miracle.c,
+ src/miracle/miracle_commands.c, src/miracle/miracle_commands.h,
+ src/miracle/miracle_connection.c, src/miracle/miracle_connection.h,
+ src/miracle/miracle_local.c, src/miracle/miracle_local.h,
+ src/miracle/miracle_log.c, src/miracle/miracle_log.h,
+ src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+ src/miracle/miracle_unit.c, src/miracle/miracle_unit.h,
+ src/miracle/miracle_unit_commands.c, src/miracle/miracle_unit_commands.h,
+ src/modules/core/producer_ppm.c, src/modules/dv/producer_libdv.c,
+ src/modules/ffmpeg/audio.sh, src/modules/ffmpeg/producer_ffmpeg.c,
+ src/modules/ffmpeg/video.sh, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+ src/modules/sdl/consumer_sdl.c, src/valerie/Makefile, src/valerie/valerie.c,
+ src/valerie/valerie.h: miracle part 1
+
+2004-01-06 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/core/transition_luma.c,
+ mlt/src/modules/core/transition_luma.h, src/modules/core/transition_luma.c,
+ src/modules/core/transition_luma.h: add forgotten files
+
+ * mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+ mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+ mlt/src/modules/core/factory.c, mlt/src/modules/dv/producer_libdv.c,
+ mlt/src/tests/dan.c, src/framework/mlt_transition.c,
+ src/framework/mlt_transition.h, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/dv/producer_libdv.c, src/tests/dan.c: added luma transition and
+ new frame properties
+
+2004-01-03 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+ mlt/src/framework/mlt_producer.c, mlt/src/inigo/inigo.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/inigo/inigo.c, src/modules/ffmpeg/producer_ffmpeg.c: more complete
+ next/prev clip behaviour
+
+2004-01-02 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/gtk2/producer_pango.c, mlt/src/modules/gtk2/producer_pango.h,
+ mlt/src/tests/dan.c, src/framework/mlt_frame.c,
+ src/modules/core/transition_composite.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pango.h, src/tests/dan.c: fixup and optimize edge
+ conditions of composite; updated property handling of producer_pango
+
+2004-01-02 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, mlt/docs/services.txt,
+ mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_properties.c,
+ mlt/src/framework/mlt_properties.h, mlt/src/framework/mlt_types.h,
+ mlt/src/inigo/inigo.c, mlt/src/modules/dv/producer_libdv.c,
+ mlt/src/modules/ffmpeg/Makefile, mlt/src/modules/ffmpeg/configure,
+ mlt/src/modules/ffmpeg/consumer_ffmpeg.c,
+ mlt/src/modules/ffmpeg/consumer_ffmpeg.h, mlt/src/modules/ffmpeg/factory.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/modules/sdl/consumer_sdl.c,
+ src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+ src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+ src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/framework/mlt_types.h, src/inigo/inigo.c,
+ src/modules/dv/producer_libdv.c, src/modules/ffmpeg/Makefile,
+ src/modules/ffmpeg/configure, src/modules/ffmpeg/consumer_ffmpeg.c,
+ src/modules/ffmpeg/consumer_ffmpeg.h, src/modules/ffmpeg/factory.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c: incomplete next/prev clip behaviour
+
+2004-01-01 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_factory.c,
+ mlt/src/framework/mlt_factory.h, mlt/src/framework/mlt_multitrack.c,
+ mlt/src/framework/mlt_producer.c, mlt/src/modules/ffmpeg/audio.sh,
+ mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.h, mlt/src/modules/ffmpeg/video.sh,
+ src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_multitrack.c, src/framework/mlt_producer.c,
+ src/modules/ffmpeg/audio.sh, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/ffmpeg/producer_ffmpeg.h,
+ src/modules/ffmpeg/video.sh: ntsc fixes and service doco for discussion
+
+2003-12-31 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/ffmpeg/Makefile, mlt/src/modules/ffmpeg/configure,
+ mlt/src/modules/ffmpeg/factory.c, mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ mlt/src/modules/ffmpeg/filter_ffmpeg_dub.h,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c, src/modules/ffmpeg/Makefile,
+ src/modules/ffmpeg/configure, src/modules/ffmpeg/factory.c,
+ src/modules/ffmpeg/filter_ffmpeg_dub.c,
+ src/modules/ffmpeg/filter_ffmpeg_dub.h, src/modules/ffmpeg/producer_ffmpeg.c,
+ src/modules/gtk2/producer_pixbuf.c: ffmpeg audio dub
+
+2003-12-30 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/sdl/consumer_sdl.c, src/modules/ffmpeg/producer_ffmpeg.c,
+ src/modules/sdl/consumer_sdl.c: correction on playlist ffmpeg sizing issue
+ and additional sdl tweaks
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_multitrack.c,
+ mlt/src/inigo/inigo.c, mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/modules/sdl/consumer_sdl.c,
+ src/framework/mlt_frame.c, src/framework/mlt_multitrack.c, src/inigo/inigo.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/sdl/consumer_sdl.c: More sdl experimental mods, pixbuf writable
+ work around and minor fixes
+
+2003-12-29 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+ mlt/src/framework/mlt_producer.c, mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/sdl/consumer_sdl.c, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+ src/modules/ffmpeg/producer_ffmpeg.c, src/modules/sdl/consumer_sdl.c: Many
+ ffmpeg and sdl mods
+
+2003-12-28 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.c: SDL a/v
+ sync issues [incomplete]
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+ mlt/src/inigo/inigo.c, mlt/src/modules/Makefile,
+ mlt/src/modules/core/factory.c, mlt/src/modules/core/producer_ppm.c,
+ mlt/src/modules/core/producer_ppm.h, mlt/src/modules/ffmpeg/Makefile,
+ mlt/src/modules/ffmpeg/configure, mlt/src/modules/ffmpeg/factory.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+ mlt/src/modules/ffmpeg/producer_ffmpeg.h, mlt/src/modules/sdl/consumer_sdl.c,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/inigo/inigo.c,
+ src/modules/Makefile, src/modules/core/factory.c,
+ src/modules/core/producer_ppm.c, src/modules/core/producer_ppm.h,
+ src/modules/ffmpeg/Makefile, src/modules/ffmpeg/configure,
+ src/modules/ffmpeg/factory.c, src/modules/ffmpeg/producer_ffmpeg.c,
+ src/modules/ffmpeg/producer_ffmpeg.h, src/modules/sdl/consumer_sdl.c: Added
+ ffmpeg producer
+
+2003-12-27 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * Makefile, README, configure, mlt/Makefile, mlt/README, mlt/configure,
+ mlt/setenv, mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_playlist.c,
+ mlt/src/inigo/Makefile, mlt/src/inigo/inigo.c, mlt/src/inigo/io.c,
+ mlt/src/inigo/io.h, mlt/src/modules/core/filter_resize.c,
+ mlt/src/modules/core/filter_resize.h, mlt/src/modules/core/producer_ppm.c,
+ mlt/src/modules/core/producer_ppm.h, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/tests/charlie.c, setenv, src/framework/mlt_frame.c,
+ src/framework/mlt_playlist.c, src/inigo/Makefile, src/inigo/inigo.c,
+ src/inigo/io.c, src/inigo/io.h, src/modules/core/filter_resize.c,
+ src/modules/core/filter_resize.h, src/modules/core/producer_ppm.c,
+ src/modules/core/producer_ppm.h, src/modules/sdl/consumer_sdl.c,
+ src/tests/charlie.c: ppm ffmpeg
+
+2003-12-26 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+ mlt/src/modules/core/factory.c, mlt/src/modules/core/filter_gamma.c,
+ mlt/src/modules/core/filter_gamma.h, mlt/src/modules/core/filter_resize.h,
+ mlt/src/tests/io.c, src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_gamma.c,
+ src/modules/core/filter_gamma.h, src/modules/core/filter_resize.h,
+ src/tests/io.c: Gamma filter
+
+ * mlt/src/tests/charlie.c, src/tests/charlie.c: quit fix for SDL
+
+ * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: playlist
+ fps fix
+
+ * mlt/src/tests/io.c, mlt/src/tests/io.h, src/tests/io.c, src/tests/io.h:
+ added io files
+
+ * mlt/src/tests/charlie.c, src/tests/charlie.c: SDL transport callback
+
+ * mlt/src/framework/mlt_property.c, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/tests/Makefile, mlt/src/tests/charlie.c,
+ src/framework/mlt_property.c, src/modules/sdl/consumer_sdl.c,
+ src/tests/Makefile, src/tests/charlie.c: SDL transport callback
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+ mlt/src/modules/sdl/consumer_sdl.c, src/framework/mlt_frame.c,
+ src/framework/mlt_frame.h, src/modules/sdl/consumer_sdl.c: More SDL tweaks
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/modules/sdl/consumer_sdl.h, mlt/src/tests/charlie.c,
+ src/framework/mlt_frame.c, src/modules/sdl/consumer_sdl.c,
+ src/modules/sdl/consumer_sdl.h, src/tests/charlie.c: More SDL updates
+
+ * mlt/src/modules/core/filter_resize.c, mlt/src/modules/sdl/consumer_sdl.c,
+ src/modules/core/filter_resize.c, src/modules/sdl/consumer_sdl.c: SDL updates
+ and resizing fix
+
+2003-12-25 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_field.c, mlt/src/framework/mlt_field.h,
+ mlt/src/framework/mlt_filter.c, mlt/src/framework/mlt_filter.h,
+ mlt/src/framework/mlt_playlist.c, mlt/src/modules/core/Makefile,
+ mlt/src/modules/core/configure, mlt/src/modules/core/factory.c,
+ mlt/src/modules/core/filter_resize.c, mlt/src/modules/core/filter_resize.h,
+ mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/tests/charlie.c, src/framework/mlt_field.c,
+ src/framework/mlt_field.h, src/framework/mlt_filter.c,
+ src/framework/mlt_filter.h, src/framework/mlt_playlist.c,
+ src/modules/core/Makefile, src/modules/core/configure,
+ src/modules/core/factory.c, src/modules/core/filter_resize.c,
+ src/modules/core/filter_resize.h, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/gtk2/producer_pixbuf.h, src/modules/sdl/consumer_sdl.c,
+ src/tests/charlie.c: field and playlist enhancements, producer pixbuf reorg
+
+2003-12-24 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/Makefile, mlt/src/framework/mlt.h,
+ mlt/src/framework/mlt_field.c, mlt/src/framework/mlt_field.h,
+ mlt/src/framework/mlt_filter.c, mlt/src/framework/mlt_filter.h,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_types.h, mlt/src/tests/charlie.c, mlt/src/tests/setenv,
+ src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_field.c,
+ src/framework/mlt_field.h, src/framework/mlt_filter.c,
+ src/framework/mlt_filter.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_types.h, src/tests/charlie.c,
+ src/tests/setenv: field and playlist provisional implementations
+
+2003-12-23 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/sdl/consumer_sdl.c, mlt/src/tests/charlie.c,
+ mlt/src/tests/dan.c, src/modules/sdl/consumer_sdl.c, src/tests/charlie.c,
+ src/tests/dan.c: SDL fixes on close
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+ mlt/src/modules/Makefile, mlt/src/tests/charlie.c, mlt/src/tests/setenv,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/modules/Makefile,
+ src/tests/charlie.c, src/tests/setenv: test frame services
+
+2003-12-23 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt_consumer.c, mlt/src/framework/mlt_consumer.h,
+ mlt/src/framework/mlt_frame.h, mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pango.h,
+ mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/tests/dan.c,
+ src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_frame.h, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pango.h, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/gtk2/producer_pixbuf.h, src/tests/dan.c: add video_standard enum
+ to mlt_frame, add mlt_consumer_properties, add properties to gtk2 producers
+ and bluefish consumer
+
+2003-12-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/Makefile, mlt/src/modules/dv/producer_libdv.c,
+ mlt/src/tests/charlie.c, src/modules/Makefile,
+ src/modules/dv/producer_libdv.c, src/tests/charlie.c: minor tidy up
+
+2003-12-22 ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/Makefile, mlt/src/modules/gtk2/configure,
+ mlt/src/tests/dan.c, src/modules/Makefile, src/modules/gtk2/configure,
+ src/tests/dan.c: allow for building mainconcept and bluefish plugins outside
+ cvs
+
+ * mlt/src/framework/mlt_frame.c, mlt/src/modules/gtk2/Makefile,
+ mlt/src/modules/gtk2/factory.c, mlt/src/modules/gtk2/producer_pango.c,
+ mlt/src/modules/gtk2/producer_pango.h,
+ mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/tests/dan.c,
+ src/framework/mlt_frame.c, src/modules/gtk2/Makefile,
+ src/modules/gtk2/factory.c, src/modules/gtk2/producer_pango.c,
+ src/modules/gtk2/producer_pango.h, src/modules/gtk2/producer_pixbuf.c,
+ src/modules/gtk2/producer_pixbuf.h, src/tests/dan.c: add sample aspect ratio
+ scaling output to producer_pixbuf, fix a bug in rgb to yuv conversions, add
+ producer_pango
+
+2003-12-22 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/framework/mlt.h, mlt/src/framework/mlt_repository.c,
+ src/framework/mlt.h, src/framework/mlt_repository.c: c++ compatability
+
+ * README, mlt/README, mlt/src/framework/Makefile, mlt/src/framework/mlt.h,
+ mlt/src/framework/mlt_factory.c, mlt/src/framework/mlt_factory.h,
+ mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_repository.h,
+ mlt/src/modules/core/Makefile, mlt/src/modules/dv/Makefile,
+ mlt/src/modules/gtk2/Makefile, mlt/src/modules/sdl/Makefile,
+ mlt/src/tests/Makefile, mlt/src/tests/charlie.c, mlt/src/tests/dan.c,
+ src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_factory.c,
+ src/framework/mlt_factory.h, src/framework/mlt_multitrack.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_repository.c,
+ src/framework/mlt_repository.h, src/modules/core/Makefile,
+ src/modules/dv/Makefile, src/modules/gtk2/Makefile, src/modules/sdl/Makefile,
+ src/tests/Makefile, src/tests/charlie.c, src/tests/dan.c: Factory
+ implementation
+
+2003-12-19 lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+ * mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+ mlt/src/modules/core/factory.c, mlt/src/modules/core/filter_deinterlace.c,
+ mlt/src/modules/core/filter_deinterlace.h,
+ mlt/src/modules/core/filter_greyscale.c,
+ mlt/src/modules/core/filter_greyscale.h, mlt/src/modules/core/producer_ppm.c,
+ mlt/src/modules/core/producer_ppm.h,
+ mlt/src/modules/core/transition_composite.c,
+ mlt/src/modules/core/transition_composite.h, src/modules/core/Makefile,
+ src/modules/core/configure, src/modules/core/factory.c,
+ src/modules/core/filter_deinterlace.c, src/modules/core/filter_deinterlace.h,
+ src/modules/core/filter_greyscale.c, src/modules/core/filter_greyscale.h,
+ src/modules/core/producer_ppm.c, src/modules/core/producer_ppm.h,
+ src/modules/core/transition_composite.c,
+ src/modules/core/transition_composite.h: Added files rejected by import
+
+ * Makefile, README, configure, mlt/Makefile, mlt/README, mlt/configure,
+ mlt/src/framework/Makefile, mlt/src/framework/config.h,
+ mlt/src/framework/configure, mlt/src/framework/mlt_consumer.c,
+ mlt/src/framework/mlt_consumer.h, mlt/src/framework/mlt_factory.c,
+ mlt/src/framework/mlt_factory.h, mlt/src/framework/mlt_filter.c,
+ mlt/src/framework/mlt_filter.h, mlt/src/framework/mlt_frame.c,
+ mlt/src/framework/mlt_frame.h, mlt/src/framework/mlt_manager.h,
+ mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+ mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+ mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+ mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_properties.h,
+ mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+ mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_repository.h,
+ mlt/src/framework/mlt_service.c, mlt/src/framework/mlt_service.h,
+ mlt/src/framework/mlt_tractor.c, mlt/src/framework/mlt_tractor.h,
+ mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+ mlt/src/framework/mlt_types.h, mlt/src/miracle/configure,
+ mlt/src/miracle/miracle.c, mlt/src/miracle/miracle_commands.c,
+ mlt/src/miracle/miracle_commands.h, mlt/src/miracle/miracle_connection.c,
+ mlt/src/miracle/miracle_connection.h, mlt/src/miracle/miracle_local.c,
+ mlt/src/miracle/miracle_local.h, mlt/src/miracle/miracle_log.c,
+ mlt/src/miracle/miracle_log.h, mlt/src/miracle/miracle_server.c,
+ mlt/src/miracle/miracle_server.h, mlt/src/miracle/miracle_unit.c,
+ mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+ mlt/src/miracle/miracle_unit_commands.h, mlt/src/modules/Makefile,
+ mlt/src/modules/configure, mlt/src/modules/dv/Makefile,
+ mlt/src/modules/dv/configure, mlt/src/modules/dv/factory.c,
+ mlt/src/modules/dv/producer_libdv.c, mlt/src/modules/dv/producer_libdv.h,
+ mlt/src/modules/gtk2/Makefile, mlt/src/modules/gtk2/configure,
+ mlt/src/modules/gtk2/factory.c, mlt/src/modules/gtk2/producer_pixbuf.c,
+ mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/sdl/Makefile,
+ mlt/src/modules/sdl/configure, mlt/src/modules/sdl/consumer_sdl.c,
+ mlt/src/modules/sdl/consumer_sdl.h, mlt/src/modules/sdl/factory.c,
+ mlt/src/tests/charlie.c, mlt/src/tests/dan.c, mlt/src/tests/test.png,
+ mlt/src/valerie/Makefile, mlt/src/valerie/configure,
+ mlt/src/valerie/valerie.c, mlt/src/valerie/valerie.h,
+ mlt/src/valerie/valerie_notifier.c, mlt/src/valerie/valerie_notifier.h,
+ mlt/src/valerie/valerie_parser.c, mlt/src/valerie/valerie_parser.h,
+ mlt/src/valerie/valerie_remote.c, mlt/src/valerie/valerie_remote.h,
+ mlt/src/valerie/valerie_response.c, mlt/src/valerie/valerie_response.h,
+ mlt/src/valerie/valerie_socket.c, mlt/src/valerie/valerie_socket.h,
+ mlt/src/valerie/valerie_status.c, mlt/src/valerie/valerie_status.h,
+ mlt/src/valerie/valerie_tokeniser.c, mlt/src/valerie/valerie_tokeniser.h,
+ mlt/src/valerie/valerie_util.c, mlt/src/valerie/valerie_util.h,
+ src/framework/Makefile, src/framework/config.h, src/framework/configure,
+ src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+ src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+ src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+ src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+ src/framework/mlt_manager.h, src/framework/mlt_multitrack.c,
+ src/framework/mlt_multitrack.h, src/framework/mlt_playlist.c,
+ src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+ src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+ src/framework/mlt_properties.h, src/framework/mlt_property.c,
+ src/framework/mlt_property.h, src/framework/mlt_repository.c,
+ src/framework/mlt_repository.h, src/framework/mlt_service.c,
+ src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+ src/framework/mlt_tractor.h, src/framework/mlt_transition.c,
+ src/framework/mlt_transition.h, src/framework/mlt_types.h,
+ src/miracle/configure, src/miracle/miracle.c, src/miracle/miracle_commands.c,
+ src/miracle/miracle_commands.h, src/miracle/miracle_connection.c,
+ src/miracle/miracle_connection.h, src/miracle/miracle_local.c,
+ src/miracle/miracle_local.h, src/miracle/miracle_log.c,
+ src/miracle/miracle_log.h, src/miracle/miracle_server.c,
+ src/miracle/miracle_server.h, src/miracle/miracle_unit.c,
+ src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+ src/miracle/miracle_unit_commands.h, src/modules/Makefile,
+ src/modules/configure, src/modules/dv/Makefile, src/modules/dv/configure,
+ src/modules/dv/factory.c, src/modules/dv/producer_libdv.c,
+ src/modules/dv/producer_libdv.h, src/modules/gtk2/Makefile,
+ src/modules/gtk2/configure, src/modules/gtk2/factory.c,
+ src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+ src/modules/sdl/Makefile, src/modules/sdl/configure,
+ src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.h,
+ src/modules/sdl/factory.c, src/tests/charlie.c, src/tests/dan.c,
+ src/tests/test.png, src/valerie/Makefile, src/valerie/configure,
+ src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_notifier.c,
+ src/valerie/valerie_notifier.h, src/valerie/valerie_parser.c,
+ src/valerie/valerie_parser.h, src/valerie/valerie_remote.c,
+ src/valerie/valerie_remote.h, src/valerie/valerie_response.c,
+ src/valerie/valerie_response.h, src/valerie/valerie_socket.c,
+ src/valerie/valerie_socket.h, src/valerie/valerie_status.c,
+ src/valerie/valerie_status.h, src/valerie/valerie_tokeniser.c,
+ src/valerie/valerie_tokeniser.h, src/valerie/valerie_util.c,
+ src/valerie/valerie_util.h: Initial revision
--- /dev/null
+# Doxyfile 1.5.7
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = mlt
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.3.8
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH = src/framework/
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = "properties=\xrefitem properties \"Property\" \"Properties Dictionary\"" "event=\xrefitem event \"Event\" \"Events Dictionary\""
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST = YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page. This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = src/framework
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file .
+
+QHG_LOCATION =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, 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 software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 2 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 General Public License for more details.
+
+ You should have received a copy of the GNU 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+SUBDIRS = src/framework \
+ src/inigo \
+ src/valerie \
+ src/miracle \
+ src/humperdink \
+ src/albino \
+ src/modules \
+ profiles
+
+all clean:
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ $(MAKE) -s -C $$subdir depend || exit 1; \
+ $(MAKE) -C $$subdir $@ || exit 1; \
+ done
+
+distclean:
+ rm mlt-config packages.dat; \
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ $(MAKE) -C $$subdir $@ || exit 1; \
+ done; \
+ rm config.mak;
+
+dist-clean: distclean
+
+include config.mak
+
+install:
+ install -d "$(DESTDIR)$(prefix)/bin"
+ install -d "$(DESTDIR)$(prefix)/include"
+ install -d "$(DESTDIR)$(libdir)"
+ install -d "$(DESTDIR)$(libdir)/mlt"
+ install -d "$(DESTDIR)$(libdir)/pkgconfig"
+ install -d "$(DESTDIR)$(prefix)/share/mlt"
+ install -c -m 755 mlt-config "$(DESTDIR)$(bindir)"
+ install -c -m 644 *.pc "$(DESTDIR)$(libdir)/pkgconfig"
+ install -m 644 packages.dat "$(DESTDIR)$(prefix)/share/mlt/"
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \
+ done; \
+ if test -z "$(DESTDIR)"; then \
+ /sbin/ldconfig 2> /dev/null || true; \
+ fi
+
+uninstall:
+ rm -f "$(DESTDIR)$(bindir)"/mlt-config
+ rm -f "$(DESTDIR)$(libdir)"/pkgconfig/mlt-*.pc
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \
+ done
+ rm -rf "$(DESTDIR)$(prefix)/include/mlt"
+ rm -rf "$(DESTDIR)$(prefix)/share/mlt"
+
+dist:
+ [ -d "mlt-$(version)" ] && rm -rf "mlt-$(version)" || echo
+ svn export http://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt "mlt-$(version)"
+ tar -cvzf "mlt-$(version).tar.gz" "mlt-$(version)"
--- /dev/null
+MLT Release Notes
+-----------------
+
+Version 0.3.8
+
+The recommended version of FFmpeg for use with this release is SVN r17923.
+
+This almost entirely a bugfix release to coincide with the Kdenlive 0.7.3
+release. See the ChangeLog (SVN log) for details.
+
+framework:
+* added mlt_cache API
+* improved doxygen documentation comments
+* added some 15 fps profiles
+* improved color property handling (support web-style '#' color value)
+* add const qualifier to many string parameters
+
+modules:
+* core: improved brightness filter
+* core: added image crop filter
+* frei0r: added support for producer/source plugins
+* frei0r: added support for color parameters
+* sdl: added window_background color property
+
+
+Version 0.3.6
+
+The recommended version of FFmpeg for use with this release is SVN r16849.
+
+This almost entirely a bugfix release to coincide with the Kdenlive 0.7.2
+release. See the ChangeLog (SVN log) for details.
+
+framework:
+* added mlt_log logging API
+* improved doxygen documentation comments
+
+avformat module:
+* consumer: report list of muxers when f=list and codecs when acodec=list or
+ vcodec=list
+* consumer: added support for an=1 or acodec=none and vn=1 or vcodec=none
+* producer: list available demuxers and decoders when constructor arg is like
+ f-list[[,]acodec-list][[,]vcodec-list]
+
+
+Version 0.3.4
+
+The recommended version of FFmpeg for use with this release is SVN r16313.
+
+This almost entirely a bugfix release. See the ChangeLog (SVN log) for details.
+There are a few notes:
+
+framework:
+* improved doxygen documentation comments (work in progress)
+ published docs are at http://mltframework.org/doxygen/
+
+avformat module:
+* added support for AVOption parsing to producer
+* added filter_swscale as alternative rescaler
+* added recommended FFmpeg revision to configure option --help
+* use recommended FFmpeg revision with --avformat-svn on release versions
+* added configure option --avformat-no-codecs
+* added configure option --avformat-no-filters
+
+misc:
+* new profile atsc_1080i_50
+* added --disable-sse option to configure script
+* improved build for OS X and x86-64 and improved handling of mmx/sse
+
+
+Version 0.3.2
+
+In addition to bug fixes detailed in the ChangeLog, here is a list of
+enhancements.
+
+framework:
+* deprecated mlt-config; use pkg-config instead
+* added more HD profiles
+
+modules:
+* sdl: added fullscreen property
+* sox: sox v14.1.0 compatibility
+* gtk: added force_reload property to producer_pixbuf
+* frei0r: added support for YAML Tiny metadata
+* frei0r: added keyframe support on double and boolean parameters
+* oldfilm: added keyframe support for filter_vignette
+* kdenlive: added filter_freeze
+
+inigo:
+* added -version, -silent, and -progress options
+* improved output of usage information
+* removed realtime process scheduling
+
+
+Version 0.3.0
+
+framework:
+* fix bugs with introduction of mlt_profile in v0.2.4
+* added versioning to libs
+* remove module registry and add dynamic module loading:
+ added mlt_repository_register, mlt_repository_consumers,
+ mlt_repository_filters, mlt_repository_producers, mlt_repository_transitions
+* new module metadata system based on YAML Tiny:
+ added mlt_repository_register_metadata, mlt_repository_metadata,
+ mlt_repository_languages, mlt_properties_is_sequence,
+ mlt_properties_parse_yaml, mlt_properties_serialise_yaml, and
+ added metaschema.yaml Kwalify schema file
+* mlt_consumer: added threaded, non-lossy processing when real_time=-1
+* added autoclose property to mlt_playlist for sequential processing
+ of very large playlists (prevents resource exhaustion)
+* mlt_factory_init now returns the global mlt_repository
+* change mlt_repository_fetch to mlt_repository_create
+* change mlt_factory_prefix to mlt_factory_directory
+* added mlt_field_disconnect_service
+
+modules:
+* move all modules from $datadir to $libdir
+* new oldfilm module by Marco Gittler
+* new frei0r module by Marco Gittler
+* new dgraft module by Dan Dennedy for inverse telecine (not ready yet)
+* avformat: added support for multi-threaded encoding and decoding
+* consumer_avformat: added support for AVOption to support all ffmpeg options
+ using ffmpeg-style property names
+* consumer_avformat: added support for dual pass encoding
+* qimage: added support for Qt4
+* sox: added support for sox v14.0.0
+* transition_composite: added animatable geometry-type "pan" property to crop
+ and pan instead of automatic down-scale
+
+inigo:
+* added -query option to lookup module metadata
+* added -profile option and support for progress=1 for kdenlive
+
+
+Version 0.2.4
+
+* framework: new extensible profiles system to replace MLT_NORMALISATION
+* module avformat: interlaced coding support for ffmpeg/libavcodec
+* module avformat: build improvements for --avformat-svn
+* new effectv module with BurningTV video filter
+* module qimage: added support for psd, xcf and exr images
+* numerous bugfixes
+
+
+Version 0.2.3
+
+* Addition of kdenlive module
+* Support for ffmpeg from subversion
+* Support for ffmpeg libswscale
+* Copyright and license cleanup
+
+
+Version 0.2.2
+
+* Prepared specifically for the kdenlive 0.3 release.
+* Contains some patches to support rgb24a output for the gdk-pixbuf and qimage
+ producers as well as some minor bugfixes.
+
+
+Version 0.2.1
+
+* Many improvements since initial releases due to development of Shotcut and
+ Jahshaka editing interfaces.
+
+
+Version 0.1.1
+
+* Minor modifications and bug fixes from the previous release. Better
+ ffmpeg/avformat integration and more reliable playback.
+
+
+Version 0.1.0
+
+* First official release
--- /dev/null
+MLT/Miracle README
+------------------
+
+ Sponsored by Ushodaya Enterprises Limited
+ Written by Charles Yates <charles.yates@pandora.be>
+ and Dan Dennedy <dan@dennedy.org>
+
+ MLT is a LGPL multimedia framework designed for television broadcasting,
+ and Miracle is a GPL multi-unit video playout server with realtime
+ effects.
+
+ This document provides a quick reference for the minimal configuration,
+ build and installation of MLT. See the docs directory for usage and
+ development details.
+
+
+Configuration
+-------------
+
+ Configuration is triggered by running:
+
+ ./configure
+
+ More information on usage is found by running:
+
+ ./configure --help
+
+ NB: This script must be run to register new services after a CVS checkout
+ or subsequent update.
+
+
+Compilation
+-----------
+
+ Once configured, it should be sufficient to run:
+
+ make
+
+ to compile the system.
+
+
+Testing
+-------
+
+ To execute the mlt tools without installation, or to test a new version
+ on a system with an already installed mlt version, you should run:
+
+ . setenv
+
+ NB: This applies to your current shell only and it assumes a bash or
+ regular bourne shell is in use.
+
+
+Installation
+------------
+
+ The install is triggered by running:
+
+ make install
+
+
+More Information
+----------------
+
+ For more detailed information, please refer to docs/install.txt.
--- /dev/null
+#!/bin/sh
+
+export version=0.3.9
+export soversion=1
+
+show_help()
+{
+ cat << EOF
+Non-autotool config script for MLT.
+
+Help options:
+
+ --help - this information
+
+General build options:
+
+ --prefix=directory - install prefix for path (default: $prefix)
+ --libdir=directory - lib directory (default: $prefix/lib)
+ --enable-gpl - Enable GPL components
+ --disable-debug - Compile without debug support (default: on)
+ --disable-mmx - Compile without MMX support (default: on)
+ --disable-sse - Compile without SSE support (default: on)
+ --arch='arch' - Compile for a specific architecture (default: none)
+ --cpu='cpu' - Compile for a specific CPU (default: none)
+
+Module disables options:
+
+EOF
+
+ for i in src/modules/*
+ do
+ [ -d $i ] && [ "`basename $i`" != "CVS" ] && echo `basename $i` `[ -f $i/gpl ] && echo [GPL]`
+ done |
+ awk '{ printf( " --disable-%-14.14s- Disable the %s module %s\n", $1, $1, $2 ); }'
+
+ echo
+ echo " NOTE: libraries marked [GPL] will not be built unless --enable-gpl is stipulated."
+ echo
+}
+
+build_config()
+{
+ (
+ echo "version=$version"
+ echo "soversion=$soversion"
+ echo "prefix=$prefix"
+ echo "libdir=$libdir"
+ echo "bindir=$prefix/bin"
+ echo "targetos=$targetos"
+
+ [ "$mmx" = "true" ] &&
+ echo "MMX_FLAGS=-DUSE_MMX"
+
+ [ "$sse" = "true" ] &&
+ echo "SSE_FLAGS=-DUSE_SSE"
+
+ [ "$debug" = "true" ] &&
+ echo "DEBUG_FLAGS=-g"
+
+ echo "LARGE_FILE=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE"
+
+ [ "$arch" != "" ] && echo "TARGETARCH=-march=$arch"
+ [ "$cpu" != "" ] && echo "TARGETCPU=-mcpu=$cpu"
+ echo "OPTIMISATIONS=-O2 -pipe -fomit-frame-pointer"
+
+ echo "CFLAGS+=-Wall -fPIC -DPIC \$(TARGETARCH) \$(TARGETCPU) \$(OPTIMISATIONS) \$(MMX_FLAGS) \$(SSE_FLAGS) \$(DEBUG_FLAGS) \$(LARGE_FILE)"
+
+ case $targetos in
+ Darwin)
+ sysctl -a hw | grep "x86_64: 1" > /dev/null && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64"
+ echo "CFLAGS+=-D__DARWIN__ `sdl-config --cflags`"
+ echo "SHFLAGS=-dynamiclib"
+ echo "LDFLAGS+=`sdl-config --libs`"
+ ;;
+ Linux)
+ [ "$(uname -m)" = "x86_64" ] && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64"
+ echo "OPTIMISATIONS+=-ffast-math"
+ echo "CFLAGS+=-pthread"
+ echo "SHFLAGS=-shared"
+ echo "LIBDL=-ldl"
+ echo "RDYNAMIC=-rdynamic"
+ ;;
+ FreeBSD)
+ [ "$(uname -m)" = "x86_64" ] && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64"
+ echo "OPTIMISATIONS+=-ffast-math"
+ echo "CFLAGS+=-pthread"
+ echo "SHFLAGS=-shared"
+ echo "RDYNAMIC=-rdynamic"
+ ;;
+ *)
+ ;;
+ esac
+ echo "LIBSUF=$LIBSUF"
+ ) > config.mak
+
+ echo "#!/bin/sh" > mlt-config
+ (
+ echo export version=$version
+ echo export prefix=$prefix
+ echo export libdir=$libdir
+ echo export bindir=$prefix/bin
+ ) >> mlt-config
+
+ cat < mlt-config-template >> mlt-config
+
+ echo -n > packages.dat
+}
+
+build_pkgconfig()
+{
+ for i in framework valerie miracle
+ do
+ echo prefix="$prefix" > mlt-$i.pc
+ (
+ echo exec_prefix=$prefix
+ echo libdir=$libdir
+ echo includedir=$prefix/include
+ echo version=$version
+ echo cflags=`grep ^$i packages.dat | cut -f 2`
+ echo libs=`grep ^$i packages.dat | cut -f 3`
+ ) >> mlt-$i.pc
+ cat mlt-$i.pc.in >>mlt-$i.pc
+ done
+}
+
+# Debug mode
+set +x
+
+# Define build directory for scripts called
+export build_dir=`dirname $0`
+export prefix=/usr/local
+export libdir=""
+export help=0
+export debug=true
+export mmx=true
+export sse=true
+export gpl=false
+export arch=
+export cpu=
+export targetos=
+
+# Determine OS
+targetos=$(uname -s)
+# Chose appropriate suffix for libraries
+case $targetos in
+ Darwin)
+ LIBSUF=".dylib"
+ ;;
+ Linux|FreeBSD)
+ LIBSUF=".so"
+ ;;
+ *)
+ LIBSUF=".so"
+ ;;
+esac
+export LIBSUF
+
+# Iterate through arguments
+for i in "$@"
+do
+ case $i in
+ --help ) help=1 ;;
+ --prefix=* ) prefix="${i#--prefix=}" ;;
+ --libdir=* ) libdir="${i#--libdir=}" ;;
+ --disable-debug ) debug=false ;;
+ --disable-mmx ) mmx=false; sse=false ;;
+ --disable-sse ) sse=false ;;
+ --enable-gpl ) gpl=true ;;
+ --arch=* ) arch="${i#--arch=}" ;;
+ --cpu=* ) cpu="${i#--cpu=}" ;;
+ esac
+done
+
+# Determine the libdir if it's not specified in the args
+[ "$libdir" = "" ] && libdir=$prefix/lib
+
+# Double check MMX (Darwin, Linux and FreeBSD supported, may end up disabling MMX on other platforms incorrectly)
+if [ "$mmx" = "true" ]
+then
+ case $targetos in
+ Darwin)
+ sysctl -a hw | grep "mmx: 1" > /dev/null || mmx=false
+ ;;
+ Linux)
+ grep mmx /proc/cpuinfo > /dev/null 2>&1 || mmx=false
+ ;;
+ FreeBSD)
+ [ "$(make -V MACHINE_CPU:Mmmx)" ] || mmx=false
+ ;;
+ *)
+ grep mmx /proc/cpuinfo > /dev/null 2>&1 || mmx=false
+ ;;
+ esac
+fi
+
+# Double check SSE (Darwin, Linux and FreeBSD supported, may end up disabling SSE on other platforms incorrectly)
+if [ "$sse" = "true" ]
+then
+ case $targetos in
+ Darwin)
+ sysctl -a hw | grep "sse: 1" > /dev/null || sse=false
+ ;;
+ Linux)
+ grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false
+ ;;
+ FreeBSD)
+ [ "$(make -V MACHINE_CPU:Msse)" ] || sse=false
+ ;;
+ *)
+ grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false
+ ;;
+ esac
+fi
+
+# Show help if requested
+if [ $help = 1 ]
+then
+ show_help
+else
+ # Log the configuration history
+ date >> config.log
+ echo "$0 $@" >> config.log
+
+ build_config
+fi
+
+# Iterate through each of the components
+for i in framework modules inigo valerie miracle humperdink
+do
+ if [ -x src/$i/configure ]
+ then
+ [ $help = 0 ] && echo "Configuring `basename $i`:"
+ olddir=`pwd`
+ cd src/$i
+ ./configure "$@"
+ [ $? != 0 ] && exit 1
+ cd $olddir
+ fi
+done
+
+# Build the pkg-config files
+build_pkgconfig
+
+# Report GPL Usage
+[ $help != 1 ] &&
+( [ "$gpl" = "false" ] &&
+echo "GPL Components are disabled" ||
+echo "GPL License Used" )
--- /dev/null
+MLT Demo Notes
+
+Before running the demo script, make sure you '. setenv' from the parent
+directory. Also, please create clips clip1.dv, clip2.dv, clip3.dv, clip1.mpeg,
+clip2.mpeg, clip3.mpeg, and music1.ogg. Please make sure clips are at least 500
+frames duration.
+
+These notes explain the the concepts presented in each demonstration and
+what details to look for.
+
+First, a note on consumers. When you start the script, the main menu asks
+you to choose a consumer. A consumer is like a viewer, but it could also
+write to a stream/file. The "SDL" consumer is the popular Simple DirectMedia
+Layer audio and video output. The "Westley" consumer generates an XML
+representation of the service network. That can be played directly due to the
+westley producer plugin. See docs/westley.txt for more information. The
+"MainConcept DV" consumer refers to the proprietary MLT plugin required to
+use MLT with MainConcept DV, DVCPro, and MPEG codecs. "/dev/dv1394/0" refers
+to a device file for transmitting DV over FireWire using the Linux dv1394 kernel
+module. The "BlueFish444" consumer is another proprietary plugin to use
+the BlueFish444 manufactured SDI video/audio output cards with MLT.
+
+And now the demos...
+
+All clips
+
+ Simply builds a playlist containing each video clip, and you can transport
+ between them using j and k keys.
+
+Filter in/out
+
+ A video filter can be applied to a portion of a producer (clip, playlist,
+ or multitrack). This examples shows the greyscale filter.
+
+Watermark
+
+ A graphic can overlay video in realtime with support for alpha channel.
+ This example uses a PNG file with an alpha channel. Distortion is explicitly
+ enabled here so the otherwise circular graphic is scaled to fill the
+ compositing region. By default, compositing honours the aspect ratio of the
+ overlay.
+
+My name is...
+
+ Titles are very easy to composite in realtime. The titler uses Pango
+ with the FreeType2 rendering backend. This means it supports high
+ quality scalable font rendering with anti-aliasing, unicode (UTF-8),
+ and Pango markup capabilities. The compsiting here respects the aspect
+ ratio of the rendered title in the first two title pieces but distorts
+ the final one. This demo also shows the motion and scaling capabilities
+ of the compositor in conjunction with honouring aspect. The compositor
+ is doing field-based rendering. So, when displayed non-progressively
+ with SDL, you can see motion artifacts during animation.
+
+A composite transition
+
+ The compositor also handles video over video as demonstrated in this
+ usage of the compositor to create a special transition. This demonstration
+ also crossfades the audio during the transition! Progressive rendering
+ is explicitly enabled on the compositor due to the poor results that
+ would otherwise occur due to scaling an interleaved video frame and moving
+ the video in a reverse direction horizontally.
+
+Fade in and out
+
+ A simple series of transitions betwen 3 clips using dissolves and audio
+ crossfades. This is easy :-).
+
+Clock in and out
+
+ Wipe transitions are very easy and highly extensible as they are generated
+ using a very convenient lookup table based upon the luma of an image.
+ This image can be a 16 bit PGM (grayscale bitmap) or the luma channel of
+ any video producer. A number of high quality wipes can be downloaded from
+ http://mlt.sf.net/. It also performs field rendering.
+ The second wipe demonstrates the ability to control the direction of the
+ wipe as well.
+
+Obscure
+
+ A popular requirement in news production is to obscure a face, obscenity,
+ or trademarked logo. This demonstrates using a simple rectangular
+ obscure filter applied to a region of the image. The second example is more
+ advanced and shows using the "region" filter to select the image area and a
+ property of the region filter to "shape" the region using the alpha channel
+ of another image (circle.png) and another property to "filter" the region
+ using the obscure filter.
+
+Audio Stuff
+
+ A music bed sound track can be mixed with a video. The sound track of the
+ video clip has a "floating" amplitude normalisation filter applied.
+ Typically, audio normalisation applies a constant gain factor across the
+ entire duration of an audio segment from a single source where the
+ gain factor is automatically determined by anaylsing the maximum "power"
+ or peak levels. However, in news production, a popular requirement is to
+ to dynamically boost the amplitude in soft areas and reduce the amplitude
+ in louder areas. Thus, the gain analysis is performed using a "sliding
+ window" approach. This example also applies a constant gain factor of
+ 0.5 (50%) to the normalised audio of the video clip (to get a nicer
+ mix level).
+
+Audio and Video Levels
+
+ Audio can be normalised by setting a target amplitude level in decibels.
+ A gamma curve can be applied to the luma channel of video.
+
+Shadowed Title and Watermark
+
+ Two instances of the titler are used to create a shadow effect.
+ The aspect ratio of the watermark in this example is not distorted. Since
+ the original image is a circle with square pixels--a computer-generated
+ image--and ITU BT.601 video is not composed of square samples. Therefore,
+ the compositor normalises the pixel aspect ratio of the overlay to the
+ destination image, and the circular image remains circular on the analog
+ video output. Finally, a greyscale filter is applied to the watermark
+ while its opacity is set at 30%.
+
+Station Promo into Story?
+
+ Here is fun demo that might show using a still graphic with some music
+ to introduce a show. A luma wipe with an audio crossfade transitions from
+ the show title or station promotional material.
+
+Voiceover 2 clips with title
+
+ A common news production requirement to have a "voiceover" audio track
+ to a clip or even multiple clips as demonstrated here. Likewise, it is
+ common to place a title caption on the video at the same time! This
+ demo has a little fun with the titler at the sake of practicality :-)
+ The foreground of the title is transparent while the opacity of the
+ background is reduced to blend with the video. Meanwhile, the compositor
+ stretches the image to fill the bottom slice of the video--not suitable
+ for overscan displays ;-)
+
+ Also, pay close attention to the mixing levels of the audio tracks.
+ The audio of the video fades out as the voiceover track (just music
+ in this demo) fades in. Then, the voiceover remains mixed with the
+ ambient audio at a 60% level. Finally, the voiceover fades out smoothly
+ from the 60% level to nothing.
+
+GJ-TTAvantika title
+
+ This demo requires a special TrueType font called Avantika. If you have the
+ font, register it with fontconfig using the fc-cache utility. This
+ demonstrates i18n capabilities of the titler and the alignment capabilities
+ of both the titler and the compositor. The titler centre aligns
+ the two lines of text, and the compositor centre aligns the title
+ horizontally on the frame.
+
+Title over graphic
+
+ You can superimpose a title over a graphic over video! Also,
+ you can apply a luma wipe to the compositor!
+
+Slideshow
+
+ This demo requires any number of JPEG images with the extension ".jpg"
+ in a subdirectory named "Scotland."
+
+Bouncy, Bouncy
+
+ The "watermark" filter encapsulates the compositor, and you have full
+ control over the compositor properties. Who says a watermark can not
+ also be a video?!
+
+Bouncy, Bouncy Ball
+
+ A variation on the above Bouncy, Bouncy demo that applies a shape, or
+ alpha producer, to the the compositing region.
+
+Breaking News
+
+ This demonstrates layout capabilities of the compositor.
+
+Squeeze Transitions
+
+ This demonstrates a distorting barndoor-like wipe.
+
+
+J Cut
+
+ A J cut is an edit where the audio cuts before the video.
+ It gets its name from the way it looks on a NLE timeline user interface.
+ When the audio cuts over, it does an audio crossfade over the duration of
+ one frame. This makes the audio cut slightly less abrupt and avoids any
+ "click" due to mismatched sample levels at the edit point. The video edit
+ is a hard cut.
+
+L Cut
+
+ An L cut is an edit where the video cuts before the audio.
+ It gets its name from the way it looks on a NLE timeline user interface.
+ This demo shows a very quick dissolve over 5 frames for a soft video cut.
+ Like the J Cut demo, an audio crossfade for the duration of one frame makes
+ an audio edit nearly instantaneous while being slightly softened and
+ avoiding aberrations.
+
+Fade from/to black/silence
+
+ Of course, it is possible using MLT to fade from black on video and silence
+ on audio as well fade to black and silence.
+
+Push wipe
+
+ A push wipe is a somewhat fancier transition than most standard wipes
+ because it involves motion. The new video clip "pushes" the old video
+ clip off one edge. If you can preview on an analog monitor you will notice
+ how smooth the motion is due to field-based rendering.
+
+Ticker tape
+
+ A very minimal reverse crawling title neard the bottom of the screen.
+ The goal of the demo is show fluid motion of the field-based rendering of
+ the compositor when viewed on an analog monitor using a DV or BlueFish444
+ consumer. The demo also shows the potientional for using and extending the
+ existing set of services for a full blown news ticker implementation.
--- /dev/null
+<svg width='300' height='300'><circle cx='150' cy='150' r='150' fill='white'/></svg>
--- /dev/null
+SDL Default sdl
+SDL Half D1 sdl:360x288 rescale=nearest resize=1
+SDL High Latency sdl buffer=12 rescale=none
+SDL Progressive sdl progressive=1
+Westley to Terminal westley
+Westley to File westley:
+MainConcept DV to /dev/dv1394/0 mcdv:/dev/dv1394/0 rescale=nearest buffer=25
+libdv to /dev/dv1394/0 libdv:/dev/dv1394/0 rescale=nearest buffer=25
+BlueFish444 PAL bluefish:1
+BlueFish444 NTSC bluefish:1 standard=NTSC
+BlueFish444 PAL Prog LL bluefish:1 progressive=1 buffer=1 frames=4
+BlueFish444 NTSC Prog LL bluefish:1 standard=NTSC progressive=1 buffer=1 frames=4
--- /dev/null
+#!/bin/bash
+
+function show_consumers( )
+{
+ awk -F '\t' '{ printf( "%d. %s\n", ++ i, $1 ); }' < consumers.ini
+}
+
+function get_consumer( )
+{
+ option=$1
+ [ "$option" != "" ] && [ $option -gt 0 ] && sed 's/\t\+/\t/g' < consumers.ini | cut -f 2 | head -n $option | tail -n -1
+}
+
+function show_menu( )
+{
+ sed 's/\t\+/\t/g' < demo.ini |
+ awk -F '\t' '{ printf( "%2d. %-30.30s", ++ i, $2 ); if ( i % 2 == 0 ) printf( "\n" ); } END { if ( i % 2 == 1 ) printf( "\n" ); }'
+}
+
+function check_dependencies( )
+{
+ option=$1
+ if [ $option -gt 0 ]
+ then
+ deps=`sed 's/\t\+/\t/g' < demo.ini | cut -f 3 | head -n $option | tail -n -1`
+ if [ "$deps" != "" ]
+ then
+ echo "$deps" |
+ tr ',' '\n' |
+ while read dep
+ do
+ ls $dep > /dev/null 2>&1
+ val=$?
+ [ $val != 0 ] && echo Failed to find $dep >&2 && echo $val
+ done
+ fi
+ echo 0
+ fi
+}
+
+function get_demo( )
+{
+ option=$1
+ if [ $option -gt 0 ]
+ then
+ cut -f 1 demo.ini | head -n $option | tail -n -1
+ fi
+}
+
+while [ 1 ]
+do
+
+ echo Select Consumer
+ echo
+
+ show_consumers
+
+ echo
+ echo 0. Exit
+ echo
+ echo -n "Option: "
+ read option
+ echo
+
+ [ "$option" == "0" ] && break
+
+ export MLT_CONSUMER=`get_consumer $option`
+
+ while [ "$option" != "0" -a "$MLT_CONSUMER" != "" ]
+ do
+ echo Choose Demo
+ echo
+
+ show_menu
+
+ echo
+ echo -n "Option: "
+ read option
+ echo
+
+ [ "$option" == "" ] && break
+
+ demo=`get_demo $option`
+ usable=`check_dependencies $option`
+
+ if [ "$usable" = "0" -a "$demo" != "" ]
+ then
+ if [ "$MLT_CONSUMER" == "westley:" ]
+ then export WESTLEY_CONSUMER="westley:$demo.westley"
+ bash $demo -consumer $WESTLEY_CONSUMER
+ inigo +$demo.txt out=100 $demo.westley $demo.westley -filter watermark:watermark1.png composite.fill=1 composite.geometry=85%,5%:10%x10%
+ elif [ "$MLT_CONSUMER" == "westley" ]
+ then bash $demo -consumer $MLT_CONSUMER | less
+ else bash $demo -consumer $MLT_CONSUMER
+ fi
+ elif [ "$usable" != "" ]
+ then
+ echo
+ echo Unable to locate suitable files for the demo - please provide them.
+ read pause
+ fi
+
+ stty sane
+ done
+
+done
--- /dev/null
+mlt_all All clips clip*
+mlt_effect_in_middle Filter in/out clip1.mpeg
+mlt_watermark Watermark clip2.dv,watermark1.png
+mlt_my_name_is My name is... clip3.dv
+mlt_composite_transition A composite transition clip1.dv,clip2.mpeg
+mlt_fade_in_and_out Fade in and out clip1.dv,clip2.mpeg,clip3.dv
+mlt_clock_in_and_out Clock in and out clip2.dv,clip1.dv,clip3.mpeg
+mlt_obscure Obscure clip2.mpeg,circle.png
+mlt_audio_stuff Audio Stuff clip*.dv,music1.ogg
+mlt_levels Audio and Video Levels clip*.dv
+mlt_titleshadow_watermark Shadowed Title and Watermark clip3.dv
+mlt_intro Station Promo into Story? watermark1.png,clip3.mpeg,music1.ogg
+mlt_voiceover Voiceover 2 clips with title clip1.dv,clip2.mpeg,music1.ogg
+mlt_avantika_title GJ-TTAvantika title pango.westley
+mlt_title_over_gfx Title over graphic watermark1.png,clip1.dv
+mlt_slideshow Slideshow Scotland
+mlt_bouncy Bouncy, Bouncy clip1.dv,clip3.dv
+mlt_bouncy_ball Bouncy, Bouncy Ball clip1.mpeg,clip3.mpeg,circle.png
+mlt_news Breaking News clip1.dv,clip2.dv
+mlt_squeeze Squeeze Transitions clip1.dv,clip2.dv,clip3.dv
+mlt_squeeze_box Squeeze Box clip1.dv,clip2.dv,clip3.dv
+mlt_jcut J Cut clip1.dv,clip2.dv
+mlt_lcut L Cut clip1.dv,clip2.dv
+mlt_fade_black Fade from/to black/silence clip3.mpeg
+mlt_push Push wipe clip1.mpeg, clip2.mpeg
+mlt_ticker Ticker tape clip1.dv
+mlt_attributes Attributes clip1.dv
+mlt_slideshow_black Composite slideshow Scotland
--- /dev/null
+<?xml version="1.0"?>
+<smil xmlns:smil2="http://www.w3.org/2001/SMIL20/Language">
+ <seq title="MLT" copyright="2004" abstract="foo man choo">
+ <video src="clip2.dv" clipBegin="171" clipEnd="450"/>
+ </seq>
+ <seq title="Demo">
+ <video src="clip2.dv" clipBegin="0" clipEnd="170"/>
+ </seq>
+ <seq>
+ <video src="clip1.dv" clipBegin="0" clipEnd="159"/>
+ <video src="clip3.dv" clipBegin="0" clipEnd="106"/>
+ </seq>
+</smil>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE westley SYSTEM "../src/modules/westley/westley.dtd" [
+ <!ENTITY name "Westley">
+]>
+<westley>
+ <producer id="producer0">
+ <property name="mlt_service">pango</property>
+ <property name="text">Hello &name;,
+My name is Inigo Montoya.</property>
+ </producer>
+</westley>
--- /dev/null
+inigo \
+clip* \
+$*
--- /dev/null
+inigo clip1.dv \
+meta.attr.location=1 meta.attr.location.markup="Location" \
+meta.attr.exclusive=1 meta.attr.exclusive.markup="Exclusive" \
+meta.attr.special=1 meta.attr.special.markup="Special" \
+meta.attr.super=1 meta.attr.super.0="Line 1" meta.attr.super.1="Line 2" \
+-filter data_show \
+$*
--- /dev/null
+inigo \
+clip*.dv \
+-track music1.ogg \
+-filter volume:0.5 normalise= track=0 \
+-transition mix out=9999 a_track=0 b_track=1 \
+$*
--- /dev/null
+inigo \
+pango.westley \
+$*
--- /dev/null
+inigo \
+clip3.dv \
+-filter \
+watermark:clip1.dv \
+composite.start=10%,10%:20%x20% \
+composite.key[33]=30%,70%:25%x25% \
+composite.key[66]=70%,30%:15%x15% \
+composite.end=70%,70%:20%x20% \
+composite.out=100 \
+$*
--- /dev/null
+inigo \
+clip3.dv \
+-track \
+clip1.dv \
+-transition \
+region:circle \
+composite.geometry="10%,10%:20%x20%;33=30%,70%:25%x25%;66=70%,30%:15%x15%;-1=70%,70%:20%x20%" \
+composite.out=100 \
+composite.softness=0.1 \
+a_track=0 \
+b_track=1 \
+in=0 \
+out=5000 \
+$*
--- /dev/null
+inigo \
+clip2.dv in=100 out=174 -blank 99 clip3.dv in=100 \
+-track \
+-blank 49 clip3.mpeg in=100 out=249 \
+-transition luma:luma1.pgm softness=0.5 in=50 out=74 a_track=0 b_track=1 \
+-transition luma:luma1.pgm softness=0.2 in=175 out=199 a_track=0 b_track=1 reverse=1 \
+$*
--- /dev/null
+inigo \
+clip1.dv out=74 \
+-track \
+-blank 49 clip2.mpeg \
+-transition composite:57%,10%:33%x33% end=0%,0%:100%x100% progressive=1 distort=true in=50 out=74 a_track=0 b_track=1 \
+-transition mix:-1 in=50 out=74 a_track=0 b_track=1 \
+$*
--- /dev/null
+inigo \
+clip1.mpeg in=100 out=500 \
+-filter greyscale in=100 out=199 \
+$*
--- /dev/null
+inigo \
+colour:black out=199 \
+-track \
+clip3.mpeg in=100 out=299 \
+-transition luma in=0 out=49 a_track=0 b_track=1 \
+-transition luma in=150 out=199 a_track=0 b_track=1 reverse=1 \
+-filter volume in=0 out=49 track=1 gain=0 end=1.0 \
+-filter volume in=150 out=199 track=1 gain=1.0 end=0 \
+$*
--- /dev/null
+inigo \
+clip1.dv out=74 -blank 99 clip3.dv in=25 \
+-track \
+-blank 49 clip2.mpeg out=149 \
+-transition luma in=50 out=74 a_track=0 b_track=1 \
+-transition luma in=175 out=199 a_track=0 b_track=1 reverse=1 \
+-transition mix:-1 in=50 out=74 a_track=0 b_track=1 \
+-transition mix:-1 in=175 out=199 a_track=0 b_track=1 reverse=1 \
+$*
--- /dev/null
+inigo \
+music1.ogg in=100 out=224 \
+-track \
+watermark1.png out=124 \
+clip3.mpeg \
+-mix 25 \
+-mixer luma resource=luma1.pgm softness=0.2 \
+-transition mix:-1 in=100 out=124 \
+$*
--- /dev/null
+inigo \
+-blank 49 \
+clip2.dv in=100 \
+-track \
+clip1.dv out=99 \
+-transition \
+mix start=0 end=1 in=49 out=50 a_track=1 b_track=0 \
+-transition \
+mix:1 in=51 out=99 a_track=1 b_track=0 \
+$*
--- /dev/null
+inigo \
+clip1.dv out=100 \
+-track \
+-blank 49 \
+clip2.dv in=100 \
+-transition \
+luma in=50 out=55 a_track=0 b_track=1 \
+-transition \
+mix:1 in=50 out=98 a_track=1 b_track=0 \
+-transition \
+mix start=1 end=0 in=99 out=100 a_track=1 b_track=0 \
+$*
--- /dev/null
+inigo \
+*.dv \
+-filter gamma:1.5 \
+-filter volume normalise=-20db \
+$*
--- /dev/null
+inigo \
+clip3.dv \
+-track \
+"+My name is Inigo Montoya.txt" out=99 -blank 49 "+Prepare to die!.txt" out=99 \
+-track \
+-blank 74 "+You killed my father.txt" out=74 \
+-transition composite:50%,20%:5%x4% end=10%,20%:80%x12% distort=1 halign=centre valign=centre in=0 out=99 a_track=0 b_track=1 \
+-transition composite:0%,70%:100%x10% end=100%,70%:100%x10% in=75 out=149 a_track=0 b_track=2 \
+-transition composite:25%,25%:50%x50%! in=150 out=249 a_track=0 b_track=1 \
+$*
--- /dev/null
+inigo \
+colour:black out=199 \
+-track \
+clip1.dv in=0 out=0 -repeat 99 clip1.dv \
+-track \
+clip2.dv out=199 \
+-track \
+pango: text=" Breaking News
+ MLT Rocks India" bgcolour=0xff000080 out=149 \
+pango: text=" Breaking News
+ MLT Rocks the World" bgcolour=0xff000080 out=349 \
+-transition mix:0.5 always_active=1 a_track=0 b_track=2 \
+-transition composite geometry=50%,15%:37.5%x40% a_track=0 b_track=1 in=0 out=174 \
+-transition composite geometry=10%,15%:37.5%x40% a_track=0 b_track=2 in=0 out=199 \
+-transition composite geometry="50%,15%:37.5%x40%;-1=0%,0%:100%x100%" a_track=0 b_track=1 in=175 out=199 distort=1 \
+-transition composite geometry=10%,65%:90%x20% a_track=0 b_track=3 in=0 out=199 \
+-transition composite geometry=10%,65%:90%x20% a_track=1 b_track=3 in=200 out=499 \
+$*
--- /dev/null
+inigo \
+clip2.mpeg \
+-filter obscure:25%,25%:25%x25%:10x10 in=0 out=68 \
+-filter region:circle.png filter=obscure composite.start=55%,25%:12%x50% in=68 out=200 \
+$*
--- /dev/null
+inigo \
+-blank 49 colour:black out=25 -blank 999 \
+-track \
+clip3.dv in=200 out=275 \
+-track \
+-blank 49 \
+clip2.dv in=200 \
+-transition \
+composite in=50 out=75 a_track=0 b_track=1 \
+start=0,0:100%x100%:100 \
+end=100%,0:100%x100%:100 \
+-transition \
+composite in=50 out=75 a_track=0 b_track=2 \
+start=-100%,0:100%x100%:100 \
+end=0,0:100%x100%:100 \
+-transition \
+mix:-1 in=50 out=75 a_track=1 b_track=2 \
+$*
--- /dev/null
+inigo \
+Scotland/.all.jpg ttl=75 \
+-filter luma:luma1.pgm luma.softness=0.1 luma.invert=0 \
+$*
--- /dev/null
+inigo Scotland/.all.jpg ttl=100 \
+-filter watermark:colour:black reverse=1 composite.geometry="15%,15%:10%,10%;0.1625=0,0:100%x100%;-.1625=;-1=70%,70%:10%x10%" composite.mirror_off=1 composite.cycle=100 composite.fill=1 composite.valign=c composite.halign=c \
+$*
--- /dev/null
+inigo \
+clip1.dv out=124 clip2.dv out=149 clip3.dv in=75 out=224 clip1.dv \
+-track \
+-blank 99 colour:black out=49 -blank 99 colour:black out=49 -blank 99 colour:black out=49 \
+-group progressive=1 distort=1 \
+-transition composite geometry="0%,0%:100%x100%;25=50%,0%:5%x100%;-1=0%,0%:100%x100%" a_track=1 b_track=0 in=100 out=149 \
+-transition composite geometry="0%,0%:100%x100%;25=0%,50%:100%x5%;-1=0%,0%:100%x100%" a_track=1 b_track=0 in=250 out=299 \
+-transition composite geometry="0%,0%:100%x100%;25=100%,0%:5%x100%;-1=0%,0%:100%x100%" a_track=1 b_track=0 in=400 out=449 \
+$*
--- /dev/null
+inigo \
+clip1.dv out=124 clip2.dv out=149 clip3.dv in=75 out=224 clip1.dv \
+-track \
+-blank 99 colour:black out=49 -blank 99 colour:black out=49 -blank 99 colour:black out=49 \
+-group progressive=1 \
+-transition composite:0%,0%:100%x100% key[25]=50%,0%:5%x100% end=0%,0%:100%x100% a_track=1 b_track=0 in=100 out=149 \
+-transition composite:0%,0%:100%x100% key[25]=0%,50%:100%x5% end=0%,0%:100%x100% a_track=1 b_track=0 in=250 out=299 \
+-transition composite:0%,0%:100%x100% key[25]=100%,0%:5%x100% end=0%,0%:100%x100% a_track=1 b_track=0 in=400 out=449 \
+$*
--- /dev/null
+inigo \
+clip1.dv out=299 \
+-track \
+colour:black out=299 \
+-track \
+"+The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog..txt" \
+out=299 \
+-transition \
+composite a_track=0 b_track=1 out=299 distort=1 \
+start=0,70%:100%x64:100 \
+-transition \
+composite a_track=0 b_track=2 out=299 titles=1 \
+start=100%,70%:999%x20% \
+end=-299%,70%:999%x20% \
+$*
--- /dev/null
+inigo \
+ watermark1.png out=9999 \
+-track \
+ "+title over gfx.txt" fgcolour=0x000000ff \
+-track \
+ clip1.dv \
+-transition \
+ composite start=30%,20%:40%x60% \
+ in=50 \
+ out=199 \
+ a_track=0 \
+ b_track=1 \
+ distort=1 \
+-transition \
+ composite:0%,75%:100%x20%:0 \
+ in=50 \
+ out=199 \
+ a_track=2 \
+ b_track=0 \
+ key[24]=0%,75%:100%x20%:100 \
+ key[-25]=0%,75%:100%x20%:100 \
+ luma=luma1.pgm \
+ end=0%,75%:100%x20%:0 \
+ distort=1 \
+$*
--- /dev/null
+inigo \
+"+hello~world.txt" align=1 out=1000 \
+-track "+hello~world.txt" align=1 out=1000 fgcolour=0x000000ff \
+-track watermark1.png out=1000 \
+-track clip3.dv \
+-filter greyscale track=2 \
+-transition composite:21%,11%:100%x100%:50 end=61%,41%:100%x100% out=99 a_track=3 b_track=1 \
+-transition composite:20%,10%:100%x100% end=60%,40%:100%x100% out=99 a_track=3 b_track=0 \
+-transition composite:85%,80%:10%x10%:30 out=1000 a_track=3 b_track=2 \
+$*
--- /dev/null
+inigo \
+"+voice over demo.txt" \
+ font="Sans Bold 72" \
+ fgcolour=0x00000000 \
+ bgcolour=0xff9933aa \
+ pad=10 \
+-track music1.ogg \
+-track clip1.dv out=149 clip2.mpeg \
+-transition \
+ mix:0.0 \
+ end=0.6 \
+ in=75 \
+ out=99 \
+ a_track=2 \
+ b_track=1 \
+-transition \
+ mix:0.6 \
+ in=100 \
+ out=299 \
+ a_track=2 \
+ b_track=1 \
+-transition \
+ mix:0.6 \
+ end=0.0 \
+ in=300 \
+ out=324 \
+ a_track=2 \
+ b_track=1 \
+-transition \
+ composite:0%,80%:100%x20% \
+ distort=1 \
+ in=100 \
+ out=299 \
+ a_track=2 \
+ b_track=0 \
+$*
--- /dev/null
+inigo \
+clip2.dv out=1000 \
+-track \
+watermark1.png out=1000 \
+-transition composite fill=1 in=0 out=1000 a_track=0 b_track=1 geometry=85%,5%:10%x10% \
+$*
--- /dev/null
+<playlist>
+ <entry>
+ <multitrack>
+ <playlist>
+ <producer id="foo" in="100" out="149">
+ <property name="resource">clip2.mpeg</property>
+ </producer>
+ <blank length="25"/>
+ <entry producer="foo" in="10" out="59"/>
+ </playlist>
+ <playlist>
+ <blank length="25"/>
+ <producer id="bar" in="100" out="149">
+ <property name="resource">clip3.mpeg</property>
+ </producer>
+ <entry out="99" producer="bar"/>
+ </playlist>
+ </multitrack>
+ <filter track="0">
+ <property name="mlt_service">greyscale</property>
+ </filter>
+ <transition in="25" out="49" a_track="0" b_track="1">
+ <property name="mlt_service">luma</property>
+ </transition>
+ <transition in="75" out="99" a_track="0" b_track="1" mlt_service="luma">
+ <property name="reverse" value="1"/>
+ </transition>
+ </entry>
+
+ <entry>
+ <multitrack>
+ <playlist>
+ <entry producer="foo" in="100" out="149"/>
+ <blank length="25"/>
+ <entry producer="foo" in="10" out="59"/>
+ </playlist>
+ <playlist>
+ <blank length="25"/>
+ <entry producer="bar" in="100" out="149"/>
+ <entry out="99" producer="bar"/>
+ </playlist>
+ </multitrack>
+ <transition in="25" out="49" a_track="0" b_track="1">
+ <property name="mlt_service">luma</property>
+ </transition>
+ <transition in="75" out="99" a_track="0" b_track="1" mlt_service="luma">
+ <property name="reverse">1</property>
+ </transition>
+ </entry>
+
+</playlist>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<westley>
+ <producer id="video">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ <producer id="title">
+ <property name="mlt_service">pango</property>
+ <property name="resource">+.txt</property>
+ <property name="font">GJ-TTAvantika 36</property>
+ <property name="align">1</property>
+ <property name="fgcolour">0xffffddff</property>
+ <property name="bgcolour">0x8c101080</property>
+ <property name="pad">8</property>
+ <property name="text"><![CDATA[ʾúlÉäºÉÒ qà ö»ÉÉ<
+HÃÉà ~ÉÒ+à eôÒ`òÂ÷ +y«ÉKÉ §ÉÉWð~É]]></property>
+ </producer>
+ <tractor>
+ <multitrack>
+ <track producer="title"/>
+ <track producer="video"/>
+ </multitrack>
+ <transition in="0" out="150">
+ <property name="mlt_service">composite</property>
+ <property name="a_track">1</property>
+ <property name="b_track">0</property>
+ <property name="start">-70%,65%:100%x35%:0</property>
+ <property name="key[25]">0,65%:100%x35%:100</property>
+ <property name="key[125]">0,65%:100%x35%:100</property>
+ <property name="end">0,65%:100%x35%:0</property>
+ <property name="halign">centre</property>
+ <property name="valign">centre</property>
+ </transition>
+ </tractor>
+</westley>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
+ <!ENTITY st0 "fill-rule:evenodd;clip-rule:evenodd;fill:url(#aigrd1);stroke:none;">
+ <!ENTITY st1 "stroke-width:0.8755;">
+ <!ENTITY st2 "fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;stroke:none;">
+ <!ENTITY st3 "fill:none;stroke:none;">
+ <!ENTITY st4 "fill-rule:evenodd;clip-rule:evenodd;fill:url(#aigrd2);stroke:none;">
+ <!ENTITY st5 "fill-rule:evenodd;clip-rule:evenodd;stroke:none;">
+ <!ENTITY st6 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+]>
+<westley>
+<playlist>
+ <entry>
+ <producer out="99" length="99">
+ <property name="mlt_service">pixbuf</property>
+ <property name="resource">
+
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
+<svg width="24pt" height="24pt" viewBox="0 0 24 24" xml:space="preserve">
+ <g id="Layer_x0020_2" style="&st6;">
+ <path style="&st1;" d="M1,12.1c0,6.1,5,11.1,11.1,11.1c6.1,0,11.1-5,11.1-11.1c0-6.1-5-11.1-11.1-11.1C5.9,1.1,1,6,1,12.1z"/>
+ <radialGradient id="aigrd1" cx="8.7681" cy="8.0513" r="13.7645" fx="8.7681" fy="8.0513" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#FED182"/>
+ <stop offset="0.2809" style="stop-color:#FFB310"/>
+ <stop offset="1" style="stop-color:#996E04"/>
+ </radialGradient>
+ <path style="&st0;" d="M21.3,12c0,5.2-4.2,9.4-9.4,9.4c-5.2,0-9.4-4.2-9.4-9.4c0-5.2,4.2-9.4,9.4-9.4c5.2,0,9.4,4.2,9.4,9.4z"/>
+ </g>
+ <g id="Layer_x0020_3" style="&st6;">
+ <path style="&st5;" d="M16.4,7.2c-0.5,0.1-0.9,0.3-1.2,0.7c0,0-3,4.2-4.1,5.6c-0.9-0.9-2.5-2.5-2.5-2.5c-0.3-0.3-0.8-0.5-1.3-0.5c-0.5,0-0.9,0.2-1.3,0.5c-0.7,0.7-0.7,1.8,0,2.5l3.9,3.9c0.4,0.4,0.9,0.5,1.4,0.5c0.5,0,1-0.3,1.3-0.7l5.4-7.1
+ c0.6-0.8,0.5-1.9-0.3-2.5c-0.4-0.3-0.8-0.4-1.3-0.4z"/>
+ <path style="&st2;" d="M16.6,7.4c-0.5,0.1-0.9,0.3-1.2,0.7c0,0-3,4.2-4.1,5.6c-0.9-0.9-2.5-2.5-2.5-2.5c-0.3-0.3-0.8-0.5-1.3-0.5c-0.5,0-0.9,0.2-1.3,0.5c-0.7,0.7-0.7,1.8,0,2.5l3.9,3.9c0.4,0.4,0.9,0.5,1.4,0.5c0.5,0,1-0.3,1.3-0.7l5.4-7.1
+ c0.6-0.8,0.5-1.9-0.3-2.5c-0.4-0.3-0.8-0.4-1.3-0.4z"/>
+ <radialGradient id="aigrd2" cx="8.6733" cy="8.6187" r="11.0872" fx="8.6733" fy="8.6187" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="1" style="stop-color:#F0E1BD"/>
+ </radialGradient>
+ <path style="&st4;" d="M16.5,7.3c-0.5,0.1-0.9,0.3-1.2,0.7c0,0-3,4.2-4.1,5.6C10.3,12.6,8.6,11,8.6,11c-0.3-0.3-0.8-0.5-1.3-0.5c-0.5,0-0.9,0.2-1.3,0.5c-0.7,0.7-0.7,1.8,0,2.5l3.9,3.9c0.4,0.4,0.9,0.5,1.4,0.5c0.5,0,1-0.3,1.3-0.7l5.4-7.1
+ c0.6-0.8,0.5-1.9-0.3-2.5c-0.4-0.3-0.8-0.4-1.3-0.4z"/>
+ </g>
+ <g id="Layer_x0020_4" style="&st6;">
+ <path style="&st3;" d="M24,24H0V0h24v24z"/>
+ </g>
+</svg>
+
+ </property>
+ </producer>
+ </entry>
+</playlist>
+</westley>
--- /dev/null
+See http://www.mltframework.org/twiki/bin/view/MLT/ToDo
--- /dev/null
+Miracle Control Protocol (DVCP) Reference Documentation
+
+Copyright (C) 2004 Ushodaya Enterprised Limited
+Author: Dan Dennedy <dan@dennedy.org>
+Last Revision: 2004-03-20
+
+
+General Format
+--------------
+ DVCP is an ASCII-based request/response TCP protocol much like FTP and
+ inspired by the SGI MVCP (Multiport Video Computer Protocol). Each
+ command is three to eight characters long followed by zero or more
+ arguments. Every item (command or argument) in the request is delimited
+ by a space and terminated with a new line. Arguments that contain spaces
+ must be surrounded by double quotation marks. The new line must contain
+ a line feed optionally preceeded by a carriage return. There are no
+ request header lines or body.
+
+
+Response Codes
+--------------
+ Responses consist of a numeric result code followed by a space folowed
+ by a brief textual description of the result. No quoting is applied to
+ descriptions regardless if it contains spaces. The result codes are
+ grouped by the hundreds into general categories of responses. Anything
+ in the 200-299 range is considered a success and anything 300 and above
+ is an error or exception. Most responses do not contain a body except
+ some of the success results that report information and sometimes the
+ 500 Server Error returns specific information.
+
+ A 200 result code contains no body.
+ A 201 result code contains one or more lines in the body, and an empty
+ line terminates the response.
+ A 202 result code contains only a single response line in the body.
+
+ Errors in the 400 range indicate a normally handled error where the
+ command could not perform its action due to protocol syntax errors or
+ problems with validation of one or more of the arguments. This usually
+ indicates that the client is responsible for performing an illegal
+ request.
+
+ Errors in the 500 range indicate a server error or exception.
+
+ The following is a list of response codes and their descriptions:
+ 200 OK
+ 201 OK
+ 202 OK
+ 400 Unknown command
+ 401 Operation timed out
+ 402 Argument missing
+ 403 Unit not found
+ 404 Failed to locate or open clip
+ 405 Argument value out of range
+ 500 Server Error
+
+
+Establishing a Connection
+-------------------------
+ One can connect to the miracle server using telnet or a custom client,
+ preferrably one developed using the valerie client API. The default port
+ is 5250. Connections can be broken at will or use the BYE command to
+ request the server to terminate the connection.
+
+
+General Command Information
+---------------------------
+
+ All commands are case insensitive. Arguments may or may not be case
+ sensitive. There are two categories of commands: global and unit. Global
+ commands operate at the server level. Unit commands address a specific
+ unit. miracle is a multi-unit system. Units are named as U? where ?
+ is the unit number, for example, U0. As units are added to the server,
+ the unit number increases; the first unit is U0.
+
+ The command HELP lists all commands known to the server with a brief
+ description of their purpose and arguments. Most commands take zero or
+ one argument outside of the unit name. Sometimes an argument is
+ optional, and an optional argument always follows required arguments.
+ All units command required a unit name argument.
+
+ {} = required argument
+ [] = optional argument
+ () = one of a set of pre-defined values
+
+
+Global Commands
+---------------
+
+HELP
+ List the commands and their brief description.
+
+BYE
+ Close the connection.
+
+SHUTDOWN
+ Shutdown the server and all client connections.
+
+SET {key=value}
+ Set a global server configuration property.
+ Currently, the only planned key is "root" to set the base directory
+ path for the CLS and LOAD commands. The default root value is /.
+
+GET {key}
+ Get the current value of a configuration property.
+ The value is returned by itself in the body of the response.
+
+CLS {path}
+ List the clips and subdirectories at {path} on the server.
+ Only subdirectories, non-hidden regular files, symbolic links, and NFS
+ shares are supported.
+ The response body contains one line per item.
+ The name of the subdirectory/file is always surrounded by double
+ quotation marks in case it contains spaces.
+ Subdirectories are listed before files and have a trailing / in their
+ name.
+ File entries have a size value in bytes in the second column position.
+
+RUN {file}
+ Process the commands in a file located on the server.
+ Commands are executed one after the other with no delay until the end
+ of file is reached or a command returns a response code not in the 200
+ range.
+ The response body contains each command sent along with its arguments,
+ followed by each command's response status code and response body.
+
+
+STATUS
+ Responds with the output of USTA for each unit and accepts no further
+ input. Each time the state of the unit changes, a new row is returned by
+ the server containing the state of the unit.
+
+Unit Management
+
+ The following global commands manage the DV units within the server.
+ Currently there is a maximum of four units, and units can not be
+ removed. Each unit may be in an online or offline state. Offline units
+ can not be used, and any unit commands issued against an offline unit
+ results in a 403 response.
+
+NLS
+ * NOT IMPLEMENTED IN MIRACLE YET *
+
+
+UADD mlt-consumer[:argument]
+ Add a unit based upon the mlt-consumer id and optional constructor
+ argument.
+ If the consumer is not found, then it still added but in an
+ offline manner. Later, by adding the device to the bus, the unit will
+ automatically become online.
+ The response body contains the name of the new unit: U0, U1, U2, or U3.
+ Channel is an optional setting.
+
+ULS
+ List the units.
+ The response body contains a space-delimited row for each unit in the
+ server containing the following columns:
+ - unit name (one of U0, U1, U2, or U3)
+ - mlt-consumer[:argument] from uadd
+ - 1394 node GUID (defunt - always 0 with miracle for now)
+ - online flag (1 = online, 0 = offline)
+
+SHUTDOWN
+ Shutdown the server.
+
+
+Unit Commands
+-------------
+
+ The first argument of any unit command is the unit name (U0 - U3). A
+ unit must be loaded with a file before it can play anything. A "clip"
+ refers to the presence of a file loaded into the unit. A clip can
+ contain an in and out point to set the playback region. The default in
+ point is 0, and the default out point is the number of frames in the
+ file minus one. Therefore, all frame positions are zero-based.
+
+USET {unit} {key=value}
+ Set a unit's configuration property.
+ Key is one of the following: eof, points.
+
+ Property "eof" determines what the playback engine does when it reaches
+ the end of a clip. The eof property takes one of the following values:
+ stop, loop, continue or pause. The default is pause.
+
+ Property "points" determines whether the playback engine restricts the
+ playback region to the in and out points. It takes one of the following
+ values: use, ignore.
+
+UGET {unit} {key}
+ Get a unit's configuration property.
+ Key is one of the following: eof, points.
+ The response body contains only the key's value. See USET for information
+ about each property.
+
+LIST {unit}
+ List the clips associated to the unit.
+ The response body consists of two sections - the first section is a single row
+ containing the generation number of the playlist associated to the unit (an
+ integer starting from 0 which is incremented on each action which changes the
+ playlist). The second sections contais a space-delimited row for each clip in the
+ units playlistcontaining the following columns:
+ - clip index (starts from 0)
+ - file name
+ - in point
+ - out point
+ - real length of the files
+ - calculated length of file
+ When USET points=use is specified (default), the calculated size is (out-in)+1.
+ When points are ignored, the real length of the file is returned.
+
+LOAD {unit} {filename} [in out]
+ Load a clip into the unit.
+ Optionally set the in and out points to the specified absolute frame numbers.
+ Sets the current position to the first frame in the clip.
+ Preface the filename with '!' to tell the disk reader thread to remove only
+ duplicate frames from the tail of its buffer queue (from a previously loaded
+ and playing clip). Otherwise, miracle flushes all of its buffers upon LOAD
+ to make the effect of LOAD instantaneous. The LOAD !, USET eof=pause, and
+ extended USTA information can be used for client-side playlists (see the
+ demo programs).
+
+APND {unit} {filename} [in out]
+ Append a clip onto the unit's playlist.
+ Optionally set the in and out points to the specified absolute frame numbers.
+
+INSERT {unit} {filename} [ [+|-]clip [ in out ] ]
+ Insert a clip into the units playlist at the specified clip index or relative
+ to the currently playing clip index.
+
+REMOVE {unit} [ [+|-]clip ]
+ Removes a clip from the specified clip index or position relative to the
+ currently playing clip index.
+
+CLEAN {unit}
+ Removes all by the playing clip.
+
+WIPE {unit}
+ Removes all clips before the playing clip.
+
+MOVE {unit} [+|-]clip [ [+|-]clip ]
+ Move a clip in the playlist to position specified or position relative to the
+ currently playing clip.
+
+PLAY {unit} [speed]
+ Commence unit playback from the current position.
+ The default speed is 100% if not specified.
+ Speed is represented as a percentage value multiplied by 10. Therefore
+ the default playback speed is 1000 (1X or 100%), 2X is 2000.
+ Negative speed values play in reverse.
+
+STOP {unit}
+ Terminate the unit playback resulting in no video being sent.
+
+PAUSE {unit}
+ Pause the unit playback causing the current frame position to he held
+ indefinitely.
+
+REW {unit}
+ Rewind the unit.
+ If the unit it playing, then REW sets the playback speed to -2000
+ (200%).
+ If the unit is stopped, then the frame position is reset to the first
+ frame. First frame depends upon the "points" unit configuration property
+ and whether an in point has been established for the clip using the SIN
+ command.
+ Set the currently loaded clip's in point.
+ Frame is zero-based and absolute. It is not dependent upon the clip's
+ current in point.
+ A frame-number of -1, resets the in point to 0.
+
+FF {unit}
+ Fast forward the unit.
+ If the unit it playing, then FF sets the playback speed to 2000 (200%
+ in reverse).
+ If the unit is stopped, then the frame position is reset to the first
+ frame. First frame depends upon the "points" unit configuration property
+ and whether an in point has been established for the clip using the SIN
+ command.
+
+STEP {unit} {number-of-frames}
+ Adjust the current frame position by the number of frames specified.
+ Number-of-frames can accept positive or negative values.
+
+GOTO {unit} {frame-number} [ [+|-]clip ]
+ Set the current frame position to frame-number.
+ Frame-number is zero-based and absolute within the clip, which means it is
+ relative to the file beginning and not the clip in point.
+ It does not alter the playback status of the unit.
+
+SIN {unit} {frame-number} [ [+|-]clip ]
+ Set the currently loaded clip's in point.
+ The in point is the logical starting frame of the clip.
+ Frame is zero-based and absolute. It is not dependent upon the clip's
+ current in point.
+ A frame-number of -1, resets the in point to 0.
+
+SOUT {unit} {frame-number} [ [+|-]clip ]
+ Set the currently loaded clip's out point.
+ The out point is the logical last frame of the clip.
+ Frame is zero-based and absolute. It is not dependent upon the clip's
+ current out point.
+ A frame-number of -1, resets the out point to the number of frames in
+ the file minus 1.
+
+USTA {unit}
+ Get the unit status report.
+ The response body contains the following fields delimited by spaces:
+ - unit number: U0, U1, U2, or U3 without the "U" prefix
+ - mode: (offline|not_loaded|playing|stopped|paused|disconnected|unknown)
+ "unknown" means the unit has not been added
+ "disconnected" means the server has closed the connection to the client.
+ - current clip name: filename
+ - current position: in absolute frame number units
+ - speed: playback rate in (percent * 10)
+ - fps: frames-per-second of loaded clip
+ - current in-point: starting frame number
+ - current out-point: ending frame number
+ - length of the clip
+ - buffer tail clip name: filename
+ - buffer tail position: in absolute frame number units
+ - buffer tail in-point: starting frame number
+ - buffer tail out-point: ending frame number
+ - buffer tail length: length of clip in buffer tail
+ - seekable flag: indicates if the current clip is seekable (relates to head)
+ - playlist generation number
+ - current clip index (relates to head)
+
+ The status contains information based not only on the current frame being
+ output (current above) but also based upon the most recent frame read by
+ the disk reader thread and added to the tail of the input buffer queue
+ (buffer tail above).
+
+XFER {unit} {target-unit}
+ Transfer the unit's clip to the target unit.
+ The clip inherently includes the in- and out-point information.
+ The target unit's "points" configuration property is set to "use."
+
+
+
+
--- /dev/null
+Framework Documentation
+
+Copyright (C) 2004 Ushodaya Enterprises Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-10-08
+
+
+MLT FRAMEWORK
+-------------
+
+Preamble:
+
+ MLT is a multimedia framework designed for television broadcasting. As such,
+ it provides a pluggable architecture for the inclusion of new audio/video
+ sources, filters, transitions and playback devices.
+
+ The framework provides the structure and utility functionality on which
+ all of the MLT applications and services are defined.
+
+ On its own, the framework provides little more than 'abstract classes' and
+ utilities for managing resources, such as memory, properties, dynamic object
+ loading and service instantiation.
+
+ This document is split roughly into 3 sections. The first section provides a
+ basic overview of MLT, the second section shows how it's used and the final
+ section shows structure and design, with an emphasis on how the system is
+ extended.
+
+
+Target Audience:
+
+ This document is provided as a 'road map' for the framework and should be
+ considered mandatory reading for anyone wishing to develop code at the MLT
+ level.
+
+ This includes:
+
+ 1. framework maintainers;
+ 2. module developers;
+ 3. application developers;
+ 4. anyone interested in MLT.
+
+ The emphasis of the document is in explaining the public interfaces, as
+ opposed to the implementation details.
+
+ It is not required reading for the MLT client/server integration - please
+ refer to valerie.txt and dvcp.txt for more details on this area.
+
+
+SECTION 1 - BASIC OVERVIEW
+--------------------------
+
+Basic Design Information:
+
+ MLT is written in C.
+
+ The framework has no dependencies other than the standard C99 and POSIX
+ libraries.
+
+ It follows a basic Object Oriented design paradigm, and as such, much of the
+ design is loosely based on the Producer/Consumer design pattern.
+
+ It employs Reverse Polish Notation for the application of audio and video FX.
+
+ The framework is designed to be colour space neutral - the currently
+ implemented modules, however, are very much 8bit YUV422 oriented. In theory,
+ the modules could be entirely replaced.
+
+ A vague understanding of these terms is assumed throughout the remainder of
+ this document.
+
+
+Structure and Flow:
+
+ The general structure of an MLT 'network' is simply the connection of a
+ 'producer' to a 'consumer':
+
+ +--------+ +--------+
+ |Producer|-->|Consumer|
+ +--------+ +--------+
+
+ A typical consumer requests MLT Frame objects from the producer, does
+ something with them and when finished with a frame, closes it.
+
+ /\ A common confusion with the producer/consumer terminology used here is
+ /!!\ that a consumer may 'produce' something. For example, the libdv consumer
+ \!!/ produces DV and the libdv producer seems to consume DV. However, the
+ \/ naming conventions refer only to producers and consumers of MLT Frames.
+
+ To put it another way - a producer produces MLT Frame objects and a consumer
+ consumes MLT Frame objects.
+
+ An MLT Frame essentially provides an uncompressed image and its associated
+ audio samples.
+
+ Filters may also be placed between the producer and the consumer:
+
+ +--------+ +------+ +--------+
+ |Producer|-->|Filter|-->|Consumer|
+ +--------+ +------+ +--------+
+
+ A service is the collective name for producers, filters, transitions and
+ consumers.
+
+ The communications between a connected consumer and producer or service are
+ carried out in 3 phases:
+
+ * get the frame
+ * get the image
+ * get the audio
+
+ MLT employs 'lazy evaluation' - the image and audio need not be extracted
+ from the source until the get image and audio methods are invoked.
+
+ In essence, the consumer pulls from what it's connected to - this means that
+ threading is typically in the domain of the consumer implementation and some
+ basic functionality is provided on the consumer class to ensure realtime
+ throughput.
+
+
+SECTION 2 - USAGE
+-----------------
+
+Hello World:
+
+ Before we go in to the specifics of the framework architecture, a working
+ example of usage is provided.
+
+ The following simply provides a media player:
+
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <framework/mlt.h>
+
+ int main( int argc, char *argv[] )
+ {
+ // Initialise the factory
+ if ( mlt_factory_init( NULL ) == 0 )
+ {
+ // Create the default consumer
+ mlt_consumer hello = mlt_factory_consumer( NULL, NULL );
+
+ // Create via the default producer
+ mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );
+
+ // Connect the producer to the consumer
+ mlt_consumer_connect( hello, mlt_producer_service( world ) );
+
+ // Start the consumer
+ mlt_consumer_start( hello );
+
+ // Wait for the consumer to terminate
+ while( !mlt_consumer_is_stopped( hello ) )
+ sleep( 1 );
+
+ // Close the consumer
+ mlt_consumer_close( hello );
+
+ // Close the producer
+ mlt_producer_close( world );
+
+ // Close the factory
+ mlt_factory_close( );
+ }
+ else
+ {
+ // Report an error during initialisation
+ fprintf( stderr, "Unable to locate factory modules\n" );
+ }
+
+ // End of program
+ return 0;
+ }
+
+ This is a simple example - it doesn't provide any seeking capabilities or
+ runtime configuration options.
+
+ The first step of any MLT application is the factory initialisation - this
+ ensures that the environment is configured and MLT can function. The factory
+ is covered in more detail below.
+
+ All services are instantiated via the factories, as shown by the
+ mlt_factory_consumer and mlt_factory_producer calls above. There are similar
+ factories for filters and transitions. There are details on all the standard
+ services in services.txt.
+
+ The defaults requested here are a special case - the NULL usage requests
+ that we use the default producers and consumers.
+
+ The default producer is "fezzik". This producer matches file names to
+ locate a service to use and attaches 'normalising filters' (such as scalers,
+ deinterlacers, resamplers and field normalisers) to the loaded content -
+ these filters ensure that the consumer gets what it asks for.
+
+ The default consumer is "sdl". The combination of fezzik and sdl will
+ provide a media player.
+
+ In this example, we connect the producer and then start the consumer. We
+ then wait until the consumer is stopped (in this case, by the action of the
+ user closing the SDL window) and finally close the consumer, producer and
+ factory before exiting the application.
+
+ Note that the consumer is threaded - waiting for an event of some sort is
+ always required after starting and before stopping or closing the consumer.
+
+ Also note, you can override the defaults as follows:
+
+ $ MLT_CONSUMER=westley ./hello file.avi
+
+ This will create a westley xml document on stdout.
+
+ $ MLT_CONSUMER=westley MLT_PRODUCER=avformat ./hello file.avi
+
+ This will play the video using the avformat producer directly, thus it will
+ bypass the normalising functions.
+
+ $ MLT_CONSUMER=libdv ./hello file.avi > /dev/dv1394
+
+ This might, if you're lucky, do on the fly, realtime conversions of file.avi
+ to DV and broadcast it to your DV device.
+
+
+Factories:
+
+ As shown in the 'Hello World' example, factories create service objects.
+
+ The framework itself provides no services - they are provided in the form of
+ a plugin structure. A plugin is organised in the form of a 'module' and a
+ module can provide many services of different types.
+
+ Once the factory is initialised, all the configured services are available
+ for use.
+
+ The complete set of methods associated to the factory are as follows:
+
+ int mlt_factory_init( char *prefix );
+ const char *mlt_factory_prefix( );
+ char *mlt_environment( char *name );
+ mlt_producer mlt_factory_producer( char *name, void *input );
+ mlt_filter mlt_factory_filter( char *name, void *input );
+ mlt_transition mlt_factory_transition( char *name, void *input );
+ mlt_consumer mlt_factory_consumer( char *name, void *input );
+ void mlt_factory_close( );
+
+ The mlt_factory_prefix returns the path to the location of the installed
+ modules directory. This can be specified in the mlt_factory_init call
+ itself, or it can be specified via the MLT_REPOSITORY environment variable,
+ or in the absence of either of those, it will default to the install
+ prefix/shared/mlt/modules.
+
+ The mlt_environment provides read only access to a collection of name=value
+ pairs as shown in the following table:
+
+ +------------------+------------------------------------+------------------+
+ |Name |Description |Values |
+ +------------------+------------------------------------+------------------+
+ |MLT_NORMALISATION |The normalisation of the system |PAL or NTSC |
+ +------------------+------------------------------------+------------------+
+ |MLT_PRODUCER |The default producer |"fezzik" or other |
+ +------------------+------------------------------------+------------------+
+ |MLT_CONSUMER |The default consumer |"sdl" or other |
+ +------------------+------------------------------------+------------------+
+ |MLT_TEST_CARD |The default test card producer |any producer |
+ +------------------+------------------------------------+------------------+
+
+ These values are initialised from the environment variables of the same
+ name.
+
+ As shown above, a producer can be created using the 'default normalising'
+ producer, and they can also be requested by name. Filters and transitions
+ are always requested by name - there is no concept of a 'default' for these.
+
+
+Service Properties:
+
+ As shown in the services.txt document, all services have their own set of
+ properties than can be manipulated to affect their behaviour.
+
+ In order to set properties on a service, we need to retrieve the properties
+ object associated to it. For producers, this is done by invoking:
+
+ mlt_properties properties = mlt_producer_properties( producer );
+
+ All services have a similar method associated to them.
+
+ Once retrieved, setting and getting properties can be done directly on this
+ object, for example:
+
+ mlt_properties_set( properties, "name", "value" );
+
+ A more complete description of the properties object is found below.
+
+
+Playlists:
+
+ So far, we've shown a simple producer/consumer configuration - the next
+ phase is to organise producers in playlists.
+
+ Let's assume that we're adapting the Hello World example, and wish to queue
+ a number of files for playout, ie:
+
+ hello *.avi
+
+ Instead of invoking mlt_factory_producer directly, we'll create a new
+ function called create_playlist. This function is responsible for creating
+ the playlist, creating each producer and appending to the playlist.
+
+ mlt_producer create_playlist( int argc, char **argv )
+ {
+ // We're creating a playlist here
+ mlt_playlist playlist = mlt_playlist_init( );
+
+ // We need the playlist properties to ensure clean up
+ mlt_properties properties = mlt_playlist_properties( playlist );
+
+ // Loop through each of the arguments
+ int i = 0;
+ for ( i = 1; i < argc; i ++ )
+ {
+ // Create the producer
+ mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );
+
+ // Add it to the playlist
+ mlt_playlist_append( playlist, producer );
+
+ // Close the producer (see below)
+ mlt_producer_close( producer );
+ }
+
+ // Return the playlist as a producer
+ return mlt_playlist_producer( playlist );
+ }
+
+ Notice that we close the producer after the append. Actually, what we're
+ doing is closing our reference to it - the playlist creates its own reference
+ to the producer on append and insert, and it will close its reference
+ when the playlist is destroyed[*].
+
+ Note also that if you append multiple instances of the same producer, it
+ will create multiple references to it.
+
+ Now all we need do is to replace these lines in the main function:
+
+ // Create a normalised producer
+ mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );
+
+ with:
+
+ // Create a playlist
+ mlt_producer world = create_playlist( argc, argv );
+
+ and we have a means to play multiple clips.
+
+ [*] This reference functionality was introduced in mlt 0.1.2 - it is 100%
+ compatable with the early mechanism of registering the reference and
+ destructor with the properties of the playlist object.
+
+
+Filters:
+
+ Inserting filters between the producer and consumer is just a case of
+ instantiating the filters, connecting the first to the producer, the next
+ to the previous filter and the last filter to the consumer.
+
+ For example:
+
+ // Create a producer from something
+ mlt_producer producer = mlt_factory_producer( ... );
+
+ // Create a consumer from something
+ mlt_consumer consumer = mlt_factory_consumer( ... );
+
+ // Create a greyscale filter
+ mlt_filter filter = mlt_factory_filter( "greyscale", NULL );
+
+ // Connect the filter to the producer
+ mlt_filter_connect( filter, mlt_producer_service( producer ), 0 );
+
+ // Connect the consumer to filter
+ mlt_consumer_connect( consumer, mlt_filter_service( filter ) );
+
+ As with producers and consumers, filters can be manipulated via their
+ properties object - the mlt_filter_properties method can be invoked and
+ properties can be set as needed.
+
+ The additional argument in the filter connection is an important one as it
+ dictates the 'track' on which the filter operates. For basic producers and
+ playlists, there's only one track (0), and as you will see in the next
+ section, even multiple tracks have a single track output.
+
+
+Attached Filters:
+
+ All services can have attached filters.
+
+ Consider the following example:
+
+ // Create a producer
+ mlt_producer producer = mlt_factory_producer( NULL, clip );
+
+ // Get the service object of the producer
+ mlt_producer service = mlt_producer_service( producer );
+
+ // Create a filter
+ mlt_filter filter = mlt_factory_filter( "greyscale" );
+
+ // Create a playlist
+ mlt_playlist playlist = mlt_playlist_init( );
+
+ // Attach the filter to the producer
+ mlt_service_attach( producer, filter );
+
+ // Construct a playlist with various cuts from the producer
+ mlt_playlist_append_io( producer, 0, 99 );
+ mlt_playlist_append_io( producer, 450, 499 );
+ mlt_playlist_append_io( producer, 200, 399 );
+
+ // We can close the producer and filter now
+ mlt_producer_close( producer );
+ mlt_filter_close( filter );
+
+ When this is played out, the greyscale filter will be executed for each frame
+ in the playlist which comes from that producer.
+
+ Further, each cut can have their own filters attached which are executed after
+ the producer's filters. As an example:
+
+ // Create a new filter
+ filter = mlt_factory_filter( "invert", NULL );
+
+ // Get the second 'clip' in the playlist
+ producer = mlt_playlist_get_clip( 1 );
+
+ // Get the service object of the clip
+ service = mlt_producer_service( producer );
+
+ // Attach the filter
+ mlt_service_attach( producer, filter );
+
+ // Close the filter
+ mlt_filter_close( filter );
+
+ Even the playlist itself can have an attached filter:
+
+ // Create a new filter
+ filter = mlt_factory_filter( "watermark", "+Hello.txt" );
+
+ // Get the service object of the playlist
+ service = mlt_playlist_service( playlist );
+
+ // Attach the filter
+ mlt_service_attach( service, filter );
+
+ // Close the filter
+ mlt_filter_close( filter );
+
+ And, of course, the playlist, being a producer, can be cut up and placed on
+ another playlist, and filters can be attached to those cuts or on the new
+ playlist itself and so on ad nauseum.
+
+ The main advantage of attached filters is that they remain attached and don't
+ suffer from the maintenance problems associated with items being inserted and
+ displacing calculated in/out points - this being a major issue if you
+ exclusively use the connect or insert detached filters in a multitrack field
+ (described below).
+
+
+Introducing the Mix:
+
+ The mix is the simplest way to introduce transitions between adjacent clips
+ on a playlist.
+
+ Consider the following playlist:
+
+ +-+----------------------+----------------------------+-+
+ |X|A |B |X|
+ +-+----------------------+----------------------------+-+
+
+ Let's assume that the 'X' is a 'black clip' of 50 frames long.
+
+ When you play this out, you'll get a 50 frames of black, abrupt cut into
+ A, followed by an abrupt cut into B, and finally into black again.
+
+ The intention is to convert this playlist into something like:
+
+ +-+---------------------+-+------------------------+-+
+ |X|A |A|B |B|
+ |A| |B| |X|
+ +-+---------------------+-+------------------------+-+
+
+ Where the clips which refer to 2 clips represent a transition. Notice that
+ the representation of the second playlist is shorter than the first - this is
+ to be expected - a single transition of 50 frames between two clips will
+ reduce the playtime of the result by 50 frames.
+
+ This is done via the use of the mlt_playlist_mix method. So, assuming you get
+ a playlist as shown in the original diagram, to do the first mix, you could do
+ something like:
+
+ // Create a transition
+ mlt_transition transition = mlt_factor_transition( "luma", NULL );
+
+ // Mix the first and second clips for 50
+ mlt_playlist_mix( playlist, 0, 50, transition );
+
+ // Close the transition
+ mlt_transition_close( transition );
+
+ This would give you the first transition, subsequently, you would apply a similar
+ technique to mix clips 1 and 2. Note that this would create a new clip on the
+ playlist, so the next mix would be between 3 and 4.
+
+ As a general hint, to simplify the requirement to know the next clip index,
+ you might find the following simpler:
+
+ // Get the number of clips on the playlist
+ int i = mlt_playlist_count( );
+
+ // Iterate through them in reverse order
+ while ( i -- )
+ {
+ // Create a transition
+ mlt_transition transition = mlt_factor_transition( "luma", NULL );
+
+ // Mix the first and second clips for 50
+ mlt_playlist_mix( playlist, i, 50, transition );
+
+ // Close the transition
+ mlt_transition_close( transition );
+ }
+
+ There are other techniques, like using the mlt_playlist_join between the
+ current clip and the newly created one (you can determine if a new clip was
+ created by comparing the playlist length before and after the mix call).
+
+ Internally, the mlt_playlist_mix call generates a tractor and multitrack as
+ described below. Like the attached filters, the mix makes life very simple
+ when you're inserting items into the playlist.
+
+ Also note that it allows a simpler user interface - instead of enforcing the
+ use of a complex multitrack object, you can do many operations on a single
+ track. Thus, additional tracks can be used to introduce audio dubs, mixes
+ or composites which are independently positioned and aren't affected by
+ manipulations on other tracks. But hey, if you want a bombastic, confusing
+ and ultimately frustrating traditional NLE experience, that functionality
+ is provided too ;-).
+
+
+Practicalities and Optimisations:
+
+ In the previous two sections I've introduced some powerful functionality
+ designed to simplify MLT usage. However, a general issue comes into this -
+ what happens when you introduce a transition between two cuts from the same
+ bit of video footage?
+
+ Anyone who is familiar with video compression will be aware that seeking
+ isn't always without consequence from a performance point of view. So if
+ you happen to require two frames from the same clip for a transition, the
+ processing is going to be excessive and the result will undoubtedly be very
+ unpleasant, especially if you're rendering in realtime...
+
+ So how do we get round this?
+
+ Actually, it's very simple - you invoke mlt_producer_optimise on the top
+ level object after a modification and MLT will determine how to handle it.
+ Internally, it determines the maximum number of overlapping instances
+ throughout the object and creates clones and assigns clone indexes as
+ required.
+
+ In the mix example above, you can simply call:
+
+ // Optimise the playlist
+ mlt_producer_optimise( mlt_playlist_producer( playlist ) );
+
+ after the mix calls have be done. Note that this is automatically applied
+ to deserialised westleys.
+
+
+Multiple Tracks and Transitions:
+
+ MLT's approach to multiple tracks is governed by two requirements:
+
+ 1) The need for a consumer and producer to communicate with one another via
+ a single frame;
+ 2) The desire to be able to serialise and manipulate a 'network' (or filter
+ graph if you prefer).
+
+ We can visualise a multitrack in the way that an NLE presents it:
+
+ +-----------------+ +-----------------------+
+ 0: |a1 | |a2 |
+ +---------------+-+--------------------------+-+---------------------+
+ 1: |b1 |
+ +------------------------------+
+
+ The overlapping areas of track 0 and 1 would (presumably) have some kind of
+ transition - without a transition, the frames from b1 and b2 would be shown
+ during the areas of overlap (ie: by default, the higher numbered track takes
+ precedence over the lower numbered track).
+
+ MLT has a multitrack object, but it is not a producer in the sense that it
+ can be connected directly to a consumer and everything will work correctly.
+ A consumer would treat it precisely as it would a normal producer, and, in
+ the case of the multitrack above, you would never see anything from track 1
+ other than the transitions between the clips - the gap between a1 and a2
+ would show test frames.
+
+ This happens because a consumer pulls one frame from the producer it's
+ connected to while a multitrack will provide one frame per track.
+ Something, somewhere, must ensure that all frames are pulled from the
+ multitrack and elect the correct frame to pass on.
+
+ Hence, MLT provides a wrapper for the multitrack, which is called a
+ 'tractor', and its the tractors task to ensure that all tracks are pulled
+ evenly, the correct frame is output and that we have 'producer like'
+ behaviour.
+
+ Thus, a multitrack is conceptually 'pulled' by a tractor as shown here:
+
+ +----------+
+ |multitrack|
+ | +------+ | +-------+
+ | |track0|-|--->|tractor|
+ | +------+ | |\ |
+ | | | \ |
+ | +------+ | | \ |
+ | |track1|-|--->|---o---|--->
+ | +------+ | | / |
+ | | | / |
+ | +------+ | |/ |
+ | |track2|-|--->| |
+ | +------+ | +-------+
+ +----------+
+
+ With a combination of the two, we can now connect multitracks to consumers.
+ The last non-test card will be retrieved and passed on.
+
+ The tracks can be producers, playlists, or even other tractors.
+
+ Now we wish to insert filters and transitions between the multitrack and the
+ tractor. We can do this directly by inserting filters directly between the
+ tractor and the multitrack, but this involves a lot of connecting and
+ reconnecting left and right producers and consumers, and it seemed only fair
+ that we should be able to automate that process.
+
+ So in keeping with our agricultural theme, the concept of the 'field' was
+ born. We 'plant' filters and transitions in the field and the tractor pulls
+ the multitrack (think of a combine harvester :-)) over the field and
+ produces a 'bail' (sorry - kidding - frame :-)).
+
+ Conceptually, we can see it like this:
+
+ +----------+
+ |multitrack|
+ | +------+ | +-------------+ +-------+
+ | |track0|-|--->|field |--->|tractor|
+ | +------+ | | | |\ |
+ | | | filters | | \ |
+ | +------+ | | and | | \ |
+ | |track1|-|--->| transitions |--->|---o---|--->
+ | +------+ | | | | / |
+ | | | | | / |
+ | +------+ | | | |/ |
+ | |track2|-|--->| |--->| |
+ | +------+ | +-------------+ +-------+
+ +----------+
+
+ So, we need to create the tractor first, and from that we obtain the
+ multitrack and field objects. We can populate these and finally
+ connect the tractor to a consumer.
+
+ In essence, this is how it looks to the consumer:
+
+ +-----------------------------------------------+
+ |tractor +--------------------------+ |
+ | +----------+ | +-+ +-+ +-+ +-+ | |
+ | |multitrack| | |f| |f| |t| |t| | |
+ | | +------+ | | |i| |i| |r| |r| | |
+ | | |track0|-|--->| |l|- ->|l|- ->|a|--->|a|\| |
+ | | +------+ | | |t| |t| |n| |n| | |
+ | | | | |e| |e| |s| |s| |\ |
+ | | +------+ | | |r| |r| |i| |i| | \|
+ | | |track1|-|- ->| |0|--->|1|--->|t|--->|t|-|--o--->
+ | | +------+ | | | | | | |i| |i| | /|
+ | | | | | | | | |o| |o| |/ |
+ | | +------+ | | | | | | |n| |n| | |
+ | | |track2|-|- ->| | |- ->| |--->|0|- ->|1|/| |
+ | | +------+ | | | | | | | | | | | |
+ | +----------+ | +-+ +-+ +-+ +-+ | |
+ | +--------------------------+ |
+ +-----------------------------------------------+
+
+ An example will hopefully clarify this.
+
+ Let's assume that we want to provide a 'watermark' to our hello world
+ example. We have already extended the example to play multiple clips,
+ and now we will place a text based watermark, reading 'Hello World' in
+ the top left hand corner:
+
+ mlt_producer create_tracks( int argc, char **argv )
+ {
+ // Create the tractor
+ mlt_tractor tractor = mlt_tractor_new( );
+
+ // Obtain the field
+ mlt_field field = mlt_tractor_field( tractor );
+
+ // Obtain the multitrack
+ mlt_multitrack multitrack = mlt_tractor_multitrack( tractor );
+
+ // Create a composite transition
+ mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" );
+
+ // Create track 0
+ mlt_producer track0 = create_playlist( argc, argv );
+
+ // Create the watermark track - note we NEED fezzik for scaling here
+ mlt_producer track1 = mlt_factory_producer( "fezzik", "pango" );
+
+ // Get the length of track0
+ mlt_position length = mlt_producer_get_playtime( track0 );
+
+ // Set the properties of track1
+ mlt_properties properties = mlt_producer_properties( track1 );
+ mlt_properties_set( properties, "text", "Hello\nWorld" );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", length - 1 );
+ mlt_properties_set_position( properties, "length", length );
+ mlt_properties_set_int( properties, "a_track", 0 );
+ mlt_properties_set_int( properties, "b_track", 1 );
+
+ // Now set the properties on the transition
+ properties = mlt_transition_properties( transition );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", length - 1 );
+
+ // Add our tracks to the multitrack
+ mlt_multitrack_connect( multitrack, track0, 0 );
+ mlt_multitrack_connect( multitrack, track1, 1 );
+
+ // Now plant the transition
+ mlt_field_plant_transition( field, transition, 0, 1 );
+
+ // Close our references
+ mlt_producer_close( track0 );
+ mlt_producer_close( track1 );
+ mlt_transition_close( transition );
+
+ // Return the tractor
+ return mlt_tractor_producer( tractor );
+ }
+
+ Now all we need do is to replace these lines in the main function:
+
+ // Create a playlist
+ mlt_producer world = create_playlist( argc, argv );
+
+ with:
+
+ // Create a watermarked playlist
+ mlt_producer world = create_tracks( argc, argv );
+
+ and we have a means to play multiple clips with a horribly obtrusive
+ watermark - just what the world needed, right? ;-)
+
+ Incidentally, the same thing could be achieved with the more trivial
+ watermark filter inserted between the producer and the consumer.
+
+
+SECTION 3 - STRUCTURE AND DESIGN
+--------------------------------
+
+Class Hierarchy:
+
+ The mlt framework consists of an OO class hierarchy which consists of the
+ following public classes and abstractions:
+
+ mlt_properties
+ mlt_frame
+ mlt_service
+ mlt_producer
+ mlt_playlist
+ mlt_tractor
+ mlt_filter
+ mlt_transition
+ mlt_consumer
+ mlt_deque
+ mlt_pool
+ mlt_factory
+
+ Each class defined above can be read as extending the classes above and to
+ the left.
+
+ The following sections describe the properties, stacking/queuing and memory
+ pooling functionality provided by the framework - these are key components
+ and a basic understanding of these is required for the remainder of the
+ documentation.
+
+
+mlt_properties:
+
+ The properties class is the base class for the frame and service classes.
+
+ It is designed to provide an efficient lookup table for various types of
+ information, such as strings, integers, floating points values and pointers
+ to data and data structures.
+
+ All properties are indexed by a unique string.
+
+ The most basic use of properties is as follows:
+
+ // 1. Create a new, empty properties set;
+ mlt_properties properties = mlt_properties_new( );
+
+ // 2. Assign the value "world" to the property "hello";
+ mlt_properties_set( properties, "hello", "world" );
+
+ // 3. Retrieve and print the value of "hello";
+ printf( "%s\n", mlt_properties_get( properties, "hello" ) );
+
+ // 4. Reassign "hello" to "world!";
+ mlt_properties_set( properties, "hello", "world!" );
+
+ // 5. Retrieve and print the value of "hello";
+ printf( "%s\n", mlt_properties_get( properties, "hello" ) );
+
+ // 6. Assign the value "0" to "int";
+ mlt_properties_set( properties, "int", "0" );
+
+ // 7. Retrieve and print the integer value of "int";
+ printf( "%d\n", mlt_properties_get_int( properties, "int" ) );
+
+ // 8. Assign the integer value 50 to "int2";
+ mlt_properties_set_int( properties, "int2", 50 );
+
+ // 9. Retrieve and print the double value of "int2";
+ printf( "%s\n", mlt_properties_get( properties, "int2" ) );
+
+ Steps 2 through 5 demonstrate that the "name" is unique - set operations on
+ an existing "name" change the value. They also free up memory associated to
+ the previous value. Note that it also possible to change type in this way
+ too.
+
+ Steps 6 and 7 demonstrate that the properties object handles deserialisation
+ from strings. The string value of "0" is set, the integer value of 0 is
+ retrieved.
+
+ Steps 8 and 9 demonstrate that the properties object handles serialisation
+ to strings.
+
+ To show all the name/value pairs in a properties, it is possible to iterate
+ through them:
+
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ printf( "%s = %s\n", mlt_properties_get_name( properties, i ),
+ mlt_properties_get_value( properties, i ) );
+
+ Note that properties are retrieved in the order in which they are set.
+
+ Properties are also used to hold pointers to memory. This is done via the
+ set_data call:
+
+ uint8_t *image = malloc( size );
+ mlt_properties_set_data( properties, "image", image, size, NULL, NULL );
+
+ In this example, we specify that the pointer can be retrieved from
+ properties by a subsequent request to get_data:
+
+ image = mlt_properties_get_data( properties, "image", &size );
+
+ or:
+
+ image = mlt_properties_get_data( properties, "image", NULL );
+
+ if we don't wish to retrieve the size.
+
+ Two points here:
+
+ 1) The allocated memory remains after the properties object is closed unless
+ you specify a destructor. In the case above, this can be done with:
+
+ mlt_properties_set_data( properties, "image", image, size, free, NULL );
+
+ When the properties are closed, or the value of "image" is changed, the
+ destructor is invoked.
+
+ 2) The string value returned by mlt_properties_get is NULL. Typically, you
+ wouldn't wish to serialise an image as a string, but other structures
+ might need such functionality - you can specify a serialiser as the last
+ argument if required (declaration is char *serialise( void * )).
+
+ Properties also provides some more advanced usage capabilities.
+
+ It has the ability to inherit all serialisable values from another properties
+ object:
+
+ mlt_properties_inherit( this, that );
+
+ It has the ability to mirror properties set on this on another set of
+ properties:
+
+ mlt_properties_mirror( this, that );
+
+ After this call, all serialisable values set on this are passed on to that.
+
+
+mlt_deque:
+
+ Stacks and queues are essential components in the MLT framework. Being of a
+ lazy disposition, we elected to implement a 'Double Ended Queue' (deque) -
+ this encapsulates the functionality of both.
+
+ The API of the deque is defined as follows:
+
+ mlt_deque mlt_deque_init( );
+ int mlt_deque_count( mlt_deque this );
+ int mlt_deque_push_back( mlt_deque this, void *item );
+ void *mlt_deque_pop_back( mlt_deque this );
+ int mlt_deque_push_front( mlt_deque this, void *item );
+ void *mlt_deque_pop_front( mlt_deque this );
+ void *mlt_deque_peek_back( mlt_deque this );
+ void *mlt_deque_peek_front( mlt_deque this );
+ void mlt_deque_close( mlt_deque this );
+
+ The stacking operations are used in a number of places:
+
+ * Reverse Polish Notation (RPN) image and audio operations
+ * memory pooling
+
+ The queuing operations are used in:
+
+ * the consumer base class;
+ * consumer implementations may require further queues.
+
+
+mlt_pool:
+
+ The MLT framework provides memory pooling capabilities through the mlt_pool
+ API. Once initilialised, these can be seen as a straightforward drop in
+ replacement for malloc/realloc/free functionality.
+
+ The background behind this API is that malloc/free operations are
+ notoriously inefficient, especially when dealing with large blocks of memory
+ (such as an image). On linux, malloc is optimised for memory allocations
+ less than 128k - memory blocks allocated of these sizes or less are retained
+ in the process heap for subsequent reuse, thus bypassing the kernel calls
+ for repeated allocation/frees for small blocks of memory. However, blocks of
+ memory larger than that require kernel calls and this has a detrimental
+ impact on performance.
+
+ The mlt_pool design is simply to hold a list of stacks - there is one stack
+ per 2^n bytes (where n is between 8 and 31). When an alloc is called, the
+ requested size is rounded to the next 2^n, the stack is retrieved for that
+ size, and an item is popped or created if the stack is empty.
+
+ Each item has a 'header', situated immediately before the returned address -
+ this holds the 'stack' to which the item belongs.
+
+ When an item is released, we retrieve the header, obtain the stack and push
+ it back.
+
+ Thus, from the programmers point of view, the API is the same as the
+ traditional malloc/realloc/free calls:
+
+ void *mlt_pool_alloc( int size );
+ void *mlt_pool_realloc( void *ptr, int size );
+ void mlt_pool_release( void *release );
+
+
+mlt_frame:
+
+ A frame object is essentially defined as:
+
+ +------------+
+ |frame |
+ +------------+
+ | properties |
+ | image stack|
+ | audio stack|
+ +------------+
+
+ The life cycle of a frame can be represented as follows:
+
+ +-----+----------------------+-----------------------+---------------------+
+ |Stage|Producer |Filter |Consumer |
+ +-----+----------------------+-----------------------+---------------------+
+ | 0.0 | | |Request frame |
+ +-----+----------------------+-----------------------+---------------------+
+ | 0.1 | |Receives request | |
+ | | |Request frame | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 0.2 |Receives request | | |
+ | |Generates frame for | | |
+ | |current position | | |
+ | |Increments position | | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 0.3 | |Receives frame | |
+ | | |Updates frame | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 0.4 | | |Receives frame |
+ +-----+----------------------+-----------------------+---------------------+
+
+ Note that neither the filter nor the consumer have any conception of
+ 'position' until they receive a frame. Speed and position are properties of
+ the producer, and they are assigned to the frame object when the producer
+ creates it.
+
+ Step 0.3 is a critical one here - if the filter determines that the frame is
+ of interest to it, then it should manipulate the image and/or audio stacks
+ and properties as required.
+
+ Assuming that the filter deals with both image and audio, then it should
+ push data and methods on to the stacks which will deal with the processing.
+ This can be done with the mlt_frame_push_image and audio methods. In order for
+ the filter to register interest in the frame, the stacks should hold:
+
+ image stack:
+ [ producer_get_image ] [ data1 ] [ data2 ] [ filter_get_image ]
+
+ audio stack:
+ [ producer_get_audio ] [ data ] [ filter_get_audio ]
+
+ The filter_get methods are invoked automatically when the consumer invokes a
+ get_image on the frame.
+
+ +-----+----------------------+-----------------------+---------------------+
+ |Stage|Producer |Filter |Consumer |
+ +-----+----------------------+-----------------------+---------------------+
+ | 1.0 | | |frame_get_image |
+ +-----+----------------------+-----------------------+---------------------+
+ | 1.1 | |filter_get_image: | |
+ | | | pop data2 and data1 | |
+ | | | frame_get_image | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 1.2 |producer_get_image | | |
+ | | Generates image | | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 1.3 | |Receives image | |
+ | | |Updates image | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 1.4 | | |Receives image |
+ +-----+----------------------+-----------------------+---------------------+
+
+ Obviously, if the filter isn't interested in the image, then it should leave
+ the stack alone, and then the consumer will retrieve its image directly from
+ the producer.
+
+ Similarly, audio is handled as follows:
+
+ +-----+----------------------+-----------------------+---------------------+
+ |Stage|Producer |Filter |Consumer |
+ +-----+----------------------+-----------------------+---------------------+
+ | 2.0 | | |frame_get_audio |
+ +-----+----------------------+-----------------------+---------------------+
+ | 2.1 | |filter_get_audio: | |
+ | | | pop data | |
+ | | | frame_get_audio | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 2.2 |producer_get_audio | | |
+ | | Generates audio | | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 2.3 | |Receives audio | |
+ | | |Updates audio | |
+ +-----+----------------------+-----------------------+---------------------+
+ | 2.4 | | |Receives audio |
+ +-----+----------------------+-----------------------+---------------------+
+
+ And finally, when the consumer is done with the frame, it should close it.
+
+ Note that a consumer may not evaluate both image and audio for any given
+ frame, especially in a realtime environment. See 'Realtime Considerations'
+ below.
+
+ By default, a frame has the following properties:
+
+ +------------------+------------------------------------+------------------+
+ |Name |Description |Values |
+ +------------------+------------------------------------+------------------+
+ |_position |The producers frame position |0 to n |
+ +------------------+------------------------------------+------------------+
+ |_speed |The producers speed |double |
+ +------------------+------------------------------------+------------------+
+ |image |The generated image |NULL or pointer |
+ +------------------+------------------------------------+------------------+
+ |alpha |The generated alpha mask |NULL or pointer |
+ +------------------+------------------------------------+------------------+
+ |width |The width of the image | |
+ +------------------+------------------------------------+------------------+
+ |height |The height of the image | |
+ +------------------+------------------------------------+------------------+
+ |normalised_width |The normalised width of the image |720 |
+ +------------------+------------------------------------+------------------+
+ |normalised_height |The normalised height of the image |576 or 480 |
+ +------------------+------------------------------------+------------------+
+ |progressive |Indicates progressive/interlaced |0 or 1 |
+ +------------------+------------------------------------+------------------+
+ |top_field_first |Indicates top field first |0 or 1 |
+ +------------------+------------------------------------+------------------+
+ |audio |The generated audio |NULL or pointer |
+ +------------------+------------------------------------+------------------+
+ |frequency |The frequency of the audio | |
+ +------------------+------------------------------------+------------------+
+ |channels |The channels of the audio | |
+ +------------------+------------------------------------+------------------+
+ |samples |The samples of the audio | |
+ +------------------+------------------------------------+------------------+
+ |aspect_ratio |The sample aspect ratio of the image|double |
+ +------------------+------------------------------------+------------------+
+ |test_image |Used to indicate no image available |0 or 1 |
+ +------------------+------------------------------------+------------------+
+ |test_audio |Used to indicate no audio available |0 or 1 |
+ +------------------+------------------------------------+------------------+
+
+ The consumer can attach the following properties which affect the default
+ behaviour of a frame:
+
+ +------------------+------------------------------------+------------------+
+ |test_card_producer|Synthesise test images from here |NULL or pointer |
+ +------------------+------------------------------------+------------------+
+ |consumer_aspect_ |Apply this aspect ratio to the test |double |
+ |ratio |card producer | |
+ +------------------+------------------------------------+------------------+
+ |rescale.interp |Use this scale method for test image|"string" |
+ +------------------+------------------------------------+------------------+
+
+ While most of these are mainly self explanatory, the normalised_width and
+ normalised_height values require a little explanation. These are required
+ to ensure that effects are consistently handled as PAL or NTSC, regardless
+ of the consumers or producers width/height image request.
+
+ The test_image and audio flags are used to determine when images and audio
+ should be synthesised.
+
+ Additional properties may be provided by the producer implementation, and
+ filters, transitions and consumers may add additional properties to
+ communicate specific requests. These are documented in modules.txt.
+
+ The complete API for the mlt frame is as follows:
+
+ mlt_frame mlt_frame_init( );
+ mlt_properties mlt_frame_properties( mlt_frame this );
+ int mlt_frame_is_test_card( mlt_frame this );
+ int mlt_frame_is_test_audio( mlt_frame this );
+ double mlt_frame_get_aspect_ratio( mlt_frame this );
+ int mlt_frame_set_aspect_ratio( mlt_frame this, double value );
+ mlt_position mlt_frame_get_position( mlt_frame this );
+ int mlt_frame_set_position( mlt_frame this, mlt_position value );
+ int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable );
+ uint8_t *mlt_frame_get_alpha_mask( mlt_frame this );
+ int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+ int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image );
+ mlt_get_image mlt_frame_pop_get_image( mlt_frame this );
+ int mlt_frame_push_frame( mlt_frame this, mlt_frame that );
+ mlt_frame mlt_frame_pop_frame( mlt_frame this );
+ int mlt_frame_push_service( mlt_frame this, void *that );
+ void *mlt_frame_pop_service( mlt_frame this );
+ int mlt_frame_push_audio( mlt_frame this, void *that );
+ void *mlt_frame_pop_audio( mlt_frame this );
+ void mlt_frame_close( mlt_frame this );
+
+
+mlt_service:
+
+ The service base class extends properties and allows 0 to m inputs and 0 to
+ n outputs and is represented as follows:
+
+ +-----------+
+ - ->| |- ->
+ - ->| Service |- ->
+ - ->| |
+ +-----------+
+ | properties|
+ +-----------+
+
+ Descendents of service impose restrictions on how inputs and outputs can be
+ connected and will provide a basic set of properties. Typically, the service
+ instance is encapsulated by the descendent in order for it to ensure that
+ its connection rules are followed.
+
+ A service does not define any properties when constructed. It should be
+ noted that producers, filters and transitions my be serialised (say, via the
+ westley consumer), and care should be taken to distinguish between
+ serialisable and transient properties. The convention used is to prefix
+ transient properties with an underscore.
+
+ The public interface is defined by the following functions:
+
+ int mlt_service_init( mlt_service this, void *child );
+ mlt_properties mlt_service_properties( mlt_service this );
+ int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index );
+ int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+ void mlt_service_close( mlt_service this );
+
+ Typically, only direct descendents of services need invoke these methods and
+ developers are encouraged to use those extensions when defining new services.
+
+
+mlt_producer:
+
+ A producer has 0 inputs and 1 output:
+
+ +-----------+
+ | |
+ | Producer |--->
+ | |
+ +-----------+
+ | service |
+ +-----------+
+
+ A producer provides an abstraction for file readers, pipes, streams or any
+ other image or audio input.
+
+ When instantiated, a producer has the following properties:
+
+ +------------------+------------------------------------+------------------+
+ |Name |Description |Values |
+ +------------------+------------------------------------+------------------+
+ |mlt_type |The producers type |mlt_producer |
+ +------------------+------------------------------------+------------------+
+ |_position |The producers frame position |0 to n |
+ +------------------+------------------------------------+------------------+
+ |_speed |The producers speed |double |
+ +------------------+------------------------------------+------------------+
+ |fps |The output frames per second |25 or 29.97 |
+ +------------------+------------------------------------+------------------+
+ |in |The in point in frames |0 to length - 1 |
+ +------------------+------------------------------------+------------------+
+ |out |The out point in frames |in to length - 1 |
+ +------------------+------------------------------------+------------------+
+ |length |The length of the input in frames |0 to n |
+ +------------------+------------------------------------+------------------+
+ |aspect_ratio |aspect_ratio of the source |0 to n |
+ +------------------+------------------------------------+------------------+
+ |eof |end of clip behaviour |"pause" or "loop" |
+ +------------------+------------------------------------+------------------+
+ |resource |Constructor argument (ie: file name)|"<resource>" |
+ +------------------+------------------------------------+------------------+
+
+ Additional properties may be provided by the producer implementation.
+
+ The public interface is defined by the following functions:
+
+ mlt_producer mlt_producer_new( );
+ int mlt_producer_init( mlt_producer this, void *child );
+ mlt_service mlt_producer_service( mlt_producer this );
+ mlt_properties mlt_producer_properties( mlt_producer this );
+ int mlt_producer_seek( mlt_producer this, mlt_position position );
+ mlt_position mlt_producer_position( mlt_producer this );
+ mlt_position mlt_producer_frame( mlt_producer this );
+ int mlt_producer_set_speed( mlt_producer this, double speed );
+ double mlt_producer_get_speed( mlt_producer this );
+ double mlt_producer_get_fps( mlt_producer this );
+ int mlt_producer_set_in_and_out( mlt_producer this, mlt_position in, mlt_position out );
+ mlt_position mlt_producer_get_in( mlt_producer this );
+ mlt_position mlt_producer_get_out( mlt_producer this );
+ mlt_position mlt_producer_get_playtime( mlt_producer this );
+ mlt_position mlt_producer_get_length( mlt_producer this );
+ void mlt_producer_prepare_next( mlt_producer this );
+ void mlt_producer_close( mlt_producer this );
+
+
+mlt_filter:
+
+ The public interface is defined by the following functions:
+
+ int mlt_filter_init( mlt_filter this, void *child );
+ mlt_filter mlt_filter_new( );
+ mlt_service mlt_filter_service( mlt_filter this );
+ mlt_properties mlt_filter_properties( mlt_filter this );
+ mlt_frame mlt_filter_process( mlt_filter this, mlt_frame that );
+ int mlt_filter_connect( mlt_filter this, mlt_service producer, int index );
+ void mlt_filter_set_in_and_out( mlt_filter this, mlt_position in, mlt_position out );
+ int mlt_filter_get_track( mlt_filter this );
+ mlt_position mlt_filter_get_in( mlt_filter this );
+ mlt_position mlt_filter_get_out( mlt_filter this );
+ void mlt_filter_close( mlt_filter );
+
+
+mlt_transition:
+
+ The public interface is defined by the following functions:
+
+ int mlt_transition_init( mlt_transition this, void *child );
+ mlt_transition mlt_transition_new( );
+ mlt_service mlt_transition_service( mlt_transition this );
+ mlt_properties mlt_transition_properties( mlt_transition this );
+ int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track );
+ void mlt_transition_set_in_and_out( mlt_transition this, mlt_position in, mlt_position out );
+ int mlt_transition_get_a_track( mlt_transition this );
+ int mlt_transition_get_b_track( mlt_transition this );
+ mlt_position mlt_transition_get_in( mlt_transition this );
+ mlt_position mlt_transition_get_out( mlt_transition this );
+ mlt_frame mlt_transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame );
+ void mlt_transition_close( mlt_transition this );
+
+
+mlt_consumer:
+
+ The public interface is defined by the following functions:
+
+ int mlt_consumer_init( mlt_consumer this, void *child );
+ mlt_service mlt_consumer_service( mlt_consumer this );
+ mlt_properties mlt_consumer_properties( mlt_consumer this );
+ int mlt_consumer_connect( mlt_consumer this, mlt_service producer );
+ int mlt_consumer_start( mlt_consumer this );
+ mlt_frame mlt_consumer_get_frame( mlt_consumer this );
+ mlt_frame mlt_consumer_rt_frame( mlt_consumer this );
+ int mlt_consumer_stop( mlt_consumer this );
+ int mlt_consumer_is_stopped( mlt_consumer this );
+ void mlt_consumer_close( mlt_consumer );
+
+
+Specialised Producers:
+
+ There are two major types of specialised producers - playlists and tractors.
+
+ The following sections describe these.
+
+
+mlt_playlist:
+
+ mlt_playlist mlt_playlist_init( );
+ mlt_producer mlt_playlist_producer( mlt_playlist this );
+ mlt_service mlt_playlist_service( mlt_playlist this );
+ mlt_properties mlt_playlist_properties( mlt_playlist this );
+ int mlt_playlist_count( mlt_playlist this );
+ int mlt_playlist_clear( mlt_playlist this );
+ int mlt_playlist_append( mlt_playlist this, mlt_producer producer );
+ int mlt_playlist_append_io( mlt_playlist this, mlt_producer producer, mlt_position in, mlt_position out );
+ int mlt_playlist_blank( mlt_playlist this, mlt_position length );
+ mlt_position mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index );
+ int mlt_playlist_current_clip( mlt_playlist this );
+ mlt_producer mlt_playlist_current( mlt_playlist this );
+ int mlt_playlist_get_clip_info( mlt_playlist this, mlt_playlist_clip_info *info, int index );
+ int mlt_playlist_insert( mlt_playlist this, mlt_producer producer, int where, mlt_position in, mlt_position out );
+ int mlt_playlist_remove( mlt_playlist this, int where );
+ int mlt_playlist_move( mlt_playlist this, int from, int to );
+ int mlt_playlist_resize_clip( mlt_playlist this, int clip, mlt_position in, mlt_position out );
+ void mlt_playlist_close( mlt_playlist this );
+
+mlt_tractor:
--- /dev/null
+Inigo Documentation
+
+Copyright (C) 2004-2009 Ushodaya Enterprised Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2009-01-21
+
+
+INIGO
+-----
+
+Preamble:
+
+ inigo was developed as a test tool for the MLT framework. It can be thought
+ of as a powerful, if somewhat obscure, multitrack command line oriented
+ video editor.
+
+ The following details the usage of the tool and as a result, provides a lot
+ of insight into the workings of the MLT framework.
+
+
+Usage:
+
+ inigo [options] [producer [name=value]* ]+
+ Options:
+ -attach filter[:arg] [name=value]* Attach a filter to the output
+ -attach-cut filter[:arg] [name=value]* Attach a filter to a cut
+ -attach-track filter[:arg] [name=value]* Attach a filter to a track
+ -attach-clip filter[:arg] [name=value]* Attach a filter to a producer
+ -audio-track | -hide-video Add an audio-only track
+ -blank frames Add blank silence to a track
+ -consumer id[:arg] [name=value]* Set the consumer (sink)
+ -debug Set the logging level to debug
+ -filter filter[:arg] [name=value]* Add a filter to the current track
+ -group [name=value]* Apply properties repeatedly
+ -help Show this message
+ -join clips Join multiple clips into one cut
+ -mix length Add a mix between the last two cuts
+ -mixer transition Add a transition to the mix
+ -null-track | -hide-track Add a hidden track
+ -profile name Set the processing settings
+ -progress Display progress along with the position
+ -remove Remove the most recent cut
+ -repeat times Repeat the last cut
+ -query List all of the registered services
+ -query "consumers" | "consumer"=id List consumers or show info about one
+ -query "filters" | "filter"=id List filters or show info about one
+ -query "producers" | "producer"=id List producers or show info about one
+ -query "transitions" | "transition"=id List transitions or show info about one
+ -serialise [filename] Write the commands to a text file
+ -silent Do not display position/transport help
+ -split relative-frame Split the last cut into two cuts
+ -swap Rearrange the last two cuts
+ -track Add a track
+ -transition id[:arg] [name=value]* Add a transition
+ -verbose Set the logging level to verbose
+ -version Show the version and copyright message
+ -video-track | -hide-audio Add a video-only track
+
+
+General rules:
+
+ 1. Order is incredibly important;
+
+ 2. Error checking on command line parsing is weak;
+
+ 3. Please refer to services.txt for details on services available;
+
+ 4. The MLT framework, from which inigo has inherited its naming convention,
+ is very mlt-centric. Producers produce MLT frame objects and consumers
+ consume MLT frame objects. The distinction is important - a DV producer
+ does not produce DV, it produces MLT frames from a DV source, and similarly
+ a DV consumer does not consume DV, it consumes MLT frames and produces DV
+ frames.
+
+
+Terminology:
+
+ 'Producers' typically refer to files but may also indicate devices (such as
+ dv1394 input or video4linux). Hence, the more generic term is used [the more
+ generic usage is out of scope for now...].
+
+ 'Filters' are frame modifiers - they always guarantee that for every frame
+ they receive, they output *precisely* one frame. Never more, never less,
+ ever. Nothing says that a filter cannot generate frames though
+
+ 'Transitions' collect frames from two tracks (a and b) and output 1
+ modified frame on their 'a track', and 1 unmodified frame on their 'b track'.
+ Never more, never less, ever.
+
+ 'Consumers' collect frames from a producer, do something with them and
+ destroy them.
+
+ Collectively, these are known as 'services'.
+
+ All services have 'properties' associated to them. These are typically
+ defaulted or evaluated and may be overriden on a case by case basis.
+
+ All services except consumers obey in and out properties.
+
+ Consumers have no say in the flow of frames [though they may give the
+ illusion that they do]. They get frames from a connected producer, use them,
+ destroy them and get more.
+
+
+Basics:
+
+ To play a file with the default SDL PAL consumer, usage is:
+
+ $ inigo file
+
+ Note that 'file' can be anything that inigo has a known 'producer' mapping
+ for (so this can be anything from .dv to .txt).
+
+ You can also specify the producer directly, for example:
+
+ $ inigo avformat:file.mpeg
+
+ Would force the direct use of avformat for loading the file.
+
+
+Properties:
+
+ Properties can be assigned to the producer by adding additional name=value
+ pairs after the producer:
+
+ $ inigo file in=50 out=100 something="something else"
+
+ Note that while some properties have meaning to all producers (for example:
+ in, out and length are guaranteed to be valid for all, though typically,
+ length is determined automatically), the validity of others are dependent on
+ the producer - however, properties will always be assigned and silently
+ ignored if they won't be used.
+
+
+Multiple Files:
+
+ Multiple files of different types can be used:
+
+ $ inigo a.dv b.mpg c.png
+
+ Properties can be assigned to each file:
+
+ $ inigo a.dv in=50 out=100 b.mpg out=500 c.png out=500
+
+ MLT will take care of 'normalising' the output of a producer to ensure
+ that the consumer gets what it needs. So, in the case above, the mlt
+ framework will ensure that images are rescaled and audio resampled to meet
+ the requirements of your configuration (which, by default, will be PAL).
+ See 'Appendix A: Normalisation Rules' below.
+
+
+Filters:
+
+ Filters are frame modifiers - they can change the contents of the audio or
+ the images associated to a frame.
+
+ $ inigo a.dv -filter greyscale
+
+ As with producers, properties may be specified on filters too.
+
+ Again, in and out properties are common to all, so to apply a filter to a
+ range of frames, you would use something like:
+
+ $ inigo a.dv -filter greyscale in=0 out=50
+
+ Again, filters have their own set of rules about properties and will
+ silently ignore properties that do not apply.
+
+
+Groups:
+
+ The -group switch is provided to force default properties on the following
+ 'services'. For example:
+
+ $ inigo -group in=0 out=49 clip*
+
+ would play the first 50 frames of all clips that match the wild card
+ pattern.
+
+ Note that the last -group settings also apply to the following filters,
+ transitions and consumers, so:
+
+ $ inigo -group in=0 out=49 clip* -filter greyscale
+
+ is *probably not* what you want (ie: the greyscale filter would only be
+ applied to the first 50 frames).
+
+ To shed the group properties, you can use any empty group:
+
+ $ inigo -group in=0 out=49 clip* -group -filter greyscale
+
+
+Attached Filters:
+
+ As described above, the -filter switch applies filters to an entire track. To
+ localise filters to a specific clip on a track, you have to know information
+ about the lengths of the clip and all clips leading up to it. In practise,
+ this is horrifically impractical, especially at a command line level (and not
+ even that practical from a programing point of view...).
+
+ The -attach family of switches simplify things enormously. By default, -attach
+ will attach a filter to the last service created, so:
+
+ $ inigo clip1.dv clip2.dv -attach greyscale clip3.dv
+
+ would only apply the filter to clip2.dv. You can further narrow down the area of
+ the effect by specifying in/out points on the attached filter.
+
+ This might seem simple so far, but there is a catch... consider the following:
+
+ $ ingo clip1.dv -attach watermark:+hello.txt -attach invert
+
+ The second attached filter is actually attached to the watermark. You might
+ think, yay, nice (and it is :-)), but, it might not be what you want. For example
+ you might want to attach both to clip1.dv. To do that, you can use:
+
+ $ ingo clip1.dv -attach-cut watermark:+hello.txt -attach-cut invert
+
+ As you shall see below, there are still another couple of gotchas associated to
+ -attach, and even another variant :-).
+
+
+Mixes:
+
+ The -mix switch provides the simplest means to introduce transitions between
+ adjacent clips.
+
+ For example:
+
+ $ inigo clip1.dv clip2.dv -mix 25 -mixer luma -mixer mix:-1
+
+ would provide both an audio and video transition between clip1 and clip2.
+
+ This functionality supercedes the enforced use of the -track and -transition
+ switches from earlier versions of inigo and makes life a lot easier :-).
+
+ These can be used in combination, so you can for example do a fade from black
+ and to black using the following:
+
+ $ inigo colour:black out=24 clip1.dv -mix 25 -mixer luma \
+ colour:black out=24 -mix 25 -mixer luma
+
+ while this may not be immediately obvious, consider what's happening as the
+ command line is being parsed from left to right:
+
+ Input: Track
+ ----------------------- -----------------------------------------------------
+ colour:black out=24 [black]
+ clip1.dv [black][clip1.dv]
+ -mix 25 [black+clip1.dv][clip1.dv]
+ -mixer luma [luma:black+clip1.dv][clip1.dv]
+ colour:black out=24 [luma:black+clip1.dv][clip1.dv][black]
+ -mix 25 [luma:black+clip1.dv][clip1.dv][clip1.dv+black]
+ -mixer luma [luma:black+clip1.dv][clip1.dv][luma:clip1.dv+black]
+
+ Obviously, the clip1.dv instances refer to different parts of the clip, but
+ hopefully that will demonstrate what happens as we construct the track.
+
+ You will find more details on the mix in the framework.txt.
+
+
+Mix and Attach:
+
+ As noted, -attach normally applies to the last created service - so, you can
+ attach a filter to the transition region using:
+
+ $ inigo clip1.dv clip2.dv -mix 25 -mixer luma -attach watermark:+Transition.txt
+
+ Again, nice, but take care - if you want the attached filter to be associated
+ to the region following the transition, use -attach-cut instead.
+
+
+Splits, Joins, Removes and Swaps:
+
+ COMPLEX - needs simplification....
+
+
+Introducing Tracks and Blanks:
+
+ So far, all of the examples have shown the definition of a single
+ playlist, or more accurately, track.
+
+ When multiple tracks exist, the consumer will receive a frame
+ from the 'highest numbered' track that is generating a non-blank
+ frame.
+
+ It is best to visualise a track arrangement, so we'll start with
+ an example:
+
+ $ inigo a.dv -track b.dv in=0 out=49
+
+ This can be visualised as follows:
+
+ +------------------+
+ |a |
+ +-------+----------+
+ |b |
+ +-------+
+
+ Playout will show the first 50 frames of b and the 51st frame shown will be
+ the 51st frame of a.
+
+ This rule also applies to audio only producers on the second track, for
+ example, the following would show the video from the a track, but the audio
+ would come from the second track:
+
+ $ inigo a.dv -track b.mp3 in=0 out=49
+
+ To have the 51st frame be the first frame of b, we can use the -blank switch:
+
+ $ inigo a.dv out=49 -track -blank 49 b.dv
+
+ Which we can visualise as:
+
+ +-------+
+ |a |
+ +-------+-------------------+
+ |b |
+ +-------------------+
+
+ Now playout will continue as though a and b clips are on the
+ same track (which on its own, is about as useful as reversing the
+ process of slicing bread).
+
+
+Transitions:
+
+ Where tracks become useful is in the placing of transitions.
+
+ Here we need tracks to overlap, so a useful multitrack
+ definition could be given as:
+
+ $ inigo a.dv out=49 \
+ -track \
+ -blank 24 b.dv \
+ -transition luma in=25 out=49 a_track=0 b_track=1
+
+ Now we're cooking - our visualisation would be something like:
+
+ +-------+
+ |a |
+ +---+---+--------------+
+ |b |
+ +------------------+
+
+ Playout will now show the first 25 frames of a and then a fade
+ transition for 25 frames between a and b, and will finally
+ playout the remainder of b.
+
+
+Reversing a Transition:
+
+ When we visualise a track definition, we also see situations
+ like:
+
+ +-------+ +----------+
+ |a1 | |a2 |
+ +---+---+--------------+----+-----+
+ |b |
+ +-----------------------+
+
+ In this case, we have two transitions, a1 to b and b to a2.
+
+ In this scenario, we define a command line as follows:
+
+ $ inigo a.dv out=49 -blank 49 a2.dv \
+ -track \
+ -blank 24 b.dv out=99 \
+ -transition luma in=25 out=49 a_track=0 b_track=1 \
+ -transition luma in=100 out=124 reverse=1 a_track=0 b_track=1
+
+
+Serialisation:
+
+ Inigo has a built in serialisation mechanism - you can build up
+ your command, test it via any consumer and then add a -serialise
+ file.inigo switch to save it.
+
+ The saved file can be subsequently used as a clip by either
+ miracle or inigo. Take care though - paths to files are saved as
+ provided on the command line....
+
+ A more expressive serialisation can be obtained with the westley consumer
+ - this will provide an xml document which can be used freely in inigo and
+ miracle.
+
+ See westley.txt for more information.
+
+
+Missing Features:
+
+ Some filters/transitions should be applied on the output frame regardless
+ of which track it comes from - for example, you might have a 3rd text
+ track or a watermark which you want composited on every frame, and of
+ course, there's the obscure filter....
+
+ inigo only supports this in two invocations - as a simple example:
+
+ $ inigo a.dv -track -blank 100 b.dv -consumer westley:basic.westley
+ $ inigo basic.westley -filter watermark:watermark.png
+
--- /dev/null
+Installation Documentation
+
+Copyright (C) 2004 Ushodaya Enterprises Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-04-13
+
+
+INSTALL
+-------
+
+ This document provides a description of the MLT project installation and
+ organisation.
+
+
+Directories
+-----------
+
+ The directory heirarchy is defined as follows:
+
+ + demo - A selection of samples to show off capabilities.
+ + docs - Location of all documentation
+ + src - All project source is provided here
+ + framework - The mlt media framework
+ + modules - All services are defined here
+ + avformat - libavformat dependent services
+ + bluefish - Bluefish dependent services (*)
+ + core - Independent MLT services
+ + dv - libdv dependent services
+ + fezzik - A giant (meta) service to load and normalise media
+ + gtk2 - pango and pixbuf dependent services
+ + mainconcept - mainconcept dependent services (*)
+ + normalize - audio normalisation functions (**)
+ + plus - throwaway silliness
+ + resample - libresample dependent services (**)
+ + sdl - SDL dependent services
+ + vorbis - vorbis dependenent services
+ + westley - Nice and clever XML services
+ + xine - Xine-derived sources (**)
+ + albino - A simple console (protocol level) example (**)
+ + inigo - A media playing test application (**)
+ + humperdink - A terminal-based example client (**)
+ + miracle - The server implementation (**)
+ + tests - Reserved for regression and unit tests
+ + valerie - Client API to access the server
+
+ Additional subdirectories may be nested below those shown and should be
+ documented in their parent.
+
+ (*) Not posted to CVS due to licensing issues.
+ (**) Contains GPL dependencies or code.
+
+
+Dependencies
+------------
+
+ The MLT core is dependent on:
+
+ * a C99 compliant C compiler
+ * posix threading
+ * standard posix libraries
+
+ The MLT applications and libraries provided are all dependent on the core.
+
+ The modules have the following dependencies:
+
+ ----------- ----------------------------------------------------------
+ MODULE DESCRIPTION
+ ----------- ----------------------------------------------------------
+ avformat Provided from ffmpeg CVS and compiled as a shared library.
+ URL: http://ffmpeg.sf.net
+ ----------- ----------------------------------------------------------
+ bluefish Bluefish hardware and software development kit
+ URL: http://www.bluefish444.com
+ ----------- ----------------------------------------------------------
+ dv libdv 0.102 or later.
+ URL: http://libdv.sf.net
+ ----------- ----------------------------------------------------------
+ gtk2 GTK2 and associated dependencies.
+ URL: http://www.gtk.org
+ ----------- ----------------------------------------------------------
+ mainconcept Mainconcept MPEG and DVCPRO Release SDKs.
+ URL: http://www.mainconcept.com
+ ----------- ----------------------------------------------------------
+ resample libsamplerate 0.15 or later
+ URL: http://www.mega-nerd.com/SRC/ (GPL)
+ ----------- ----------------------------------------------------------
+ sdl SDL 1.2 or later.
+ URL: http://www.libsdl.org
+ ----------- ----------------------------------------------------------
+ vorbis libvorbis 1.0.1 or later.
+ URL: http://www.vorbis.com/
+ ----------- ----------------------------------------------------------
+ westley libxml2 2.5 or later.
+ URL: http://www.xmlsoft.org/
+ ----------- ----------------------------------------------------------
+
+
+Configuration
+-------------
+
+ Configuration is triggered from the top level directory via a
+ ./configure script.
+
+ Each source bearing subdirectory shown above have their own configure
+ script which are called automatically from the top level.
+
+ Typically, new modules can be introduced without modification to the
+ configure script and arguments are accepted and passed through to all
+ subdirectories.
+
+ More information on usage is found by running:
+
+ ./configure --help
+
+ NB: This script must be run to register new services after a CVS checkout
+ or subsequent update.
+
+
+Compilation
+-----------
+
+ Makefiles are generated during configuration and these are based on
+ a per directory template which must be provided by the developer.
+
+
+Testing
+-------
+
+ To execute the mlt tools without installation, or to test a new version
+ on a system with an already installed mlt version, you should run:
+
+ . setenv
+
+ NB: This applies to your current shell only and it assumes sh or bash.
+
+
+Installation
+------------
+
+ The install is triggered by running make install from the top level
+ directory.
+
+ The framework produces a single shared object which is installed in
+ $prefix/lib/ and public header files which are installed in
+ $prefix/include/mlt/framework.
+
+ Valerie produces a single shared object which is installed in
+ $prefix/lib/ and public header which are installed in
+ $prefix/include/mlt/valerie.
+
+ Miracle produces a single exectuable which is installed in
+ $prefix/bin/, a library in $prefix/lib and associated header files in
+ $prefix/include.
+
+ The modules produce a shared object per module and update text files
+ containing a list of modules provided by this build. These are installed
+ in $prefix/share/mlt/modules. It is at the discretion of the module to
+ install additional support files.
+
+ To allow the development of external components, mlt-config and scripts
+ are generated and installed in $prefix/bin.
+
+ After install, only those modules listed are usable by the server. No
+ module is loaded unless explicitly requested via server configuration
+ or usage.
+
+ External modules are also placed in this $prefix/share/mlt/modules, and the
+ installation of those must modify the text file accordingly before they
+ will be considered at runtime.
+
+
+Development
+-----------
+
+ All compilation in the project has {top-level-dir}/src on the include path.
+ All headers are included as:
+
+ #include <framework/file.h>
+
+ All external modules have {prefix}/include/mlt on the include path. All
+ headers should also be included as:
+
+ #include <framework/file.h>
+
+ This allows migration of source between external and internal modules.
+ The configuration and Makefile template requirements will require
+ attention though.
--- /dev/null
+Open Source Development Policies and Procedures for MLT
+by Dan Dennedy
+
+Policies
+--------
+
+Any contribution to the "core" module must assign copyright to Ushodaya
+Enterprises Limited because they need license control over that module.
+
+The framework and valerie client libraries are covered under LGPL. Miracle,
+inigo, albino, and humperdink applications are covered under GPL. Modules
+should strive to be LGPL to make them available through the framework as LGPL.
+
+Comments in the framework and valerie header files must be C-style, not C++.
+
+Coding Style:
+There are not a lot of rules, but we prefer brackets on their own line,
+indents using tabs, liberal usage of spaces in statements and expressions, and
+no hard line length. The code in src/framework serves as a good example.
+
+Commit messages must be prefaced with the name of the changed files. This makes
+the Subversion log more useful as a ChangeLog. For example,
+ docs/policies.txt: added policy about commit message
+
+Increment the version number in ./configure AND mlt.h on the first commit after
+a release as well as just prior to a new release. This way we can track if
+someone is using an unreleased version from the source code repository.
+
+Do not write messages to stdout. stdout is reserved for writing streams that
+can be redirected: e.g. raw DV in consumer_libdv. I recommended to use the
+mlt_log API. For something quick and dirty, use stderr.
+
+
+Procedures
+----------
+
+Update services.txt when you add or update a service.
+
+Setting Copyright on Appropriated Code:
+You do not want to be accused of copying someone's code and changing copyright
+or license without permission. The license is straightforward: you must retain
+the original author's license unless you receive explicit permission. There are
+a few ways to approach the copyright declaration depending upon the
+intermingling and changes. If you heavily comingle original and new code or
+lightly modifiy the original code, you can retain the original's copyright
+including the years, and then add your copyright for the current year. If you
+can separate the MLT integration from the core subroutines, then you can put
+the core subroutines into a separate file with the original copyright and just
+copyright the MLT integration code as your own. However, if you have heavily
+modified the original code beyond nearly all recognition, you can copyright it
+as your own and attribute the original author as inspiration.
+
+Bug Reporting:
+First preference is to use the SourceForge tracker:
+http://sourceforge.net/tracker/?group_id=96039&atid=613414
+Second preference is in the mailing list:
+mlt-devel@lists.sourceforge.net
--- /dev/null
+Service Documentation
+
+Authors: Charles Yates <charles.yates@pandora.be>
+ Dan Dennedy <dan@dennedy.org>
+Last Revision: $Date$
+
+
+SERVICES
+--------
+
+ Services marked as "(Proprietary)" are not distributed with the LGPL
+ version of mlt.
+
+Producers
+---------
+
+ avformat
+
+ Description
+
+ ffmpeg libavformat based producer for video and audio.
+
+ Constructor Argument
+
+ 'file' - a filename specification or URL in the form:
+ [{protocol}|{format}]:{resource}[?{format-parameter}[&{format-parameter}...]]
+ For example, video4linux:/dev/video1?width:320&height:240
+ Note: on the bash command line, & must be escaped as '\&'.
+ Also, note the use of ':' instead of '=' for parameters.
+ Use 'ffmpeg -formats' to see a list of supported protocols
+ and formats.
+
+ Details
+
+ Format parameters only appear to be useful with 'video4linux' or
+ 'audio_device' formats. For 'video4linux' the parameters are
+ width, height, frame_rate, frame_rate_base, and standard (ntsc|pal).
+ For 'audio_device' the parameters are channels and sample_rate.
+
+ Initialisation Properties
+
+ int video_index - index of video stream to use (-1 is off)
+ int audio_index - index of audio stream to use (-1 is off)
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ string resource - file location
+ double source_fps - the framerate of the resource
+ double aspect_ratio - sample aspect ratio of the resource
+ - this is determined on every frame read
+
+ Dependencies
+
+ ffmpeg
+
+ Known Bugs
+
+ Audio sync discrepancy with some content.
+ Not all libavformat supported formats are seekable.
+ Fails to play beyond first frame of video of sources with PTS not
+ starting at 0 (video4linux).
+
+ fezzik
+
+ Description
+
+ A friendly giant that likes to rhyme and throw rocks
+
+ Constructor Argument
+
+ 'file' - a filename specification:
+ [{mlt-service}:]{resource} | {mlt-service}
+ - can also be the name of a producer service that can
+ accept the resource specified post construction.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ + all producer initialising properties
+
+ Read Only Properties
+
+ string resource - file location
+ + all producer read only properties
+
+ Details
+
+ This producer has two roles:
+
+ 1. it handles the mappings of all file names to the other
+ producers;
+ 2. it attaches normalising filters (rescale, resize and resample)
+ to the producers (when necessary).
+
+ This producer simplifies many aspects of use. Essentially, it
+ ensures that a consumer will receive images and audio precisely as
+ they request them.
+
+ Dependencies
+
+ all.
+
+ Known Bugs
+
+ None.
+
+
+ colour
+
+ Description
+
+ A simple colour generator.
+
+ Constructor Argument
+
+ colour - A colour value is a hexadecimal representation of RGB plus
+ alpha channel as 0xrrggbbaa.
+ - Also colours can be the words: white, black, red, green,
+ or blue.
+ - The default colour is black.
+
+ Initialisation Properties
+
+ none
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+
+ libdv
+
+ Description
+
+ libdv based decoder for video and audio.
+
+ Constructor Argument
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ string resource - file location
+ double fps - output frames per second
+ int length - duration of resource (in frames)
+
+ Mutable Properties
+
+ string quality - one of "best," "fast" or anything else chooses
+ medium.
+
+ Dependencies
+
+ libdv.
+
+ Known Bugs
+
+ DVCPRO is incorrectly identified as 16:9 aspect ratio. You must use
+ libdv from CVS or a post 0.101 release.
+
+ mcdv (Proprietary)
+
+ Description
+
+ MainConcept based dv decoder for video and audio.
+
+ Constructor Argument
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ string resource - file location
+ double fps - output frames per second
+ int length - duration of resource (in frames)
+
+ Dependencies
+
+ MainConcept DV or DVCPRO SDK, libdv.
+ "dv_sdk" installed parallel to mlt.
+
+ Known Bugs
+
+ None
+
+ mcmpeg (Proprietary)
+
+ Description
+
+ MainConcept based mpeg decoder for video and audio.
+
+ Constructor Argument
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ string resource - file location
+ double fps - output frames per second
+ double aspect_ratio - sample aspect ratio of video
+ int length - duration of resource (in frames)
+
+ Dependencies
+
+ MainConcept MPEG SDK.
+ "mpeg_sdk_release" installed parallel to mlt.
+
+ Known Bugs
+
+ None.
+
+ noise
+
+ Description
+
+ White noise producer
+
+ Constructor Argument
+
+ none
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ string resource - file location
+ double fps - output frames per second
+ double aspect_ratio - sample aspect ratio of video
+ int length - duration of resource (in frames)
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+ pango
+
+ Description
+
+ A title generator that uses the Pango international text layout
+ and Freetype2 font renderer.
+
+ Constructor Argument
+
+ string file - a text file containing Pango markup, see:
+ http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html
+ - requires xml-like encoding special chars from:
+ <, >, & -to- <, >, &
+
+ Details
+
+ Supplying a filename with extension ".txt" causes the Fezzik
+ producer to load with pango. If the filename begins with "+" the
+ pango producer interprets the filename as pango text. This is a
+ shortcut to embed titles in inigo commands. For westley, it is
+ recommended that you embed the title text in the property value.
+
+ Pango has builtin scaling. It will rescale the originally rendered
+ title to whatever the consumer requests. Therefore, it will lose
+ its aspect ratio if so requested, and it is up to the consumer to
+ request a proper width and height that maintains the image aspect.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ string markup - a string containing Pango markup see:
+ http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html
+ - requires xml-like encoding special chars from:
+ <, >, & -to- <, >, &
+ string fgcolour - an RGBA colour specification of the text
+ (i.e. 0xrrggbbaa)
+ string bgcolour - an RGBA colour of the background rectangle
+ string align - paragraph alignment: left, centre, right
+ - also, numbers 0, 1 and 2 can be used respectively.
+ int pad - the number of pixels to pad the background rectangle
+ beyond edges of text. default 0.
+ string markup - see constructor argument
+ string text - non-markup string in UTF-8 encoding (can contain
+ markup chars un-encoded)
+ string font - the default typeface to use when not using markup.
+ default "Sans 48". FreeType2 renders at 72 dpi.
+ string encoding - the text encoding type of the input if not UTF-8.
+ - see 'iconv --list' for a list of possible inputs.
+ int weight - the weight of the font (default is 400)
+
+ Read Only Properties
+
+ string resource - the text/markup file or "pango" if no file.
+ int real_width - the original, unscaled width of the rendered title.
+ int real_height - the original, unscaled height of the title.
+ int width - the last requested scaled image width.
+ int height - the last requested scaled image height.
+
+ Dependencies
+
+ libpango-1.0, libpangoft2-1.0, libfreetype, libgdk_pixbuf-2.0,
+ libglib-2.0, libgobject-2.0, libgmodule-2.0, libfontconfig.
+
+ Known Bugs
+
+ The foreground and background Pango markup span attributes are not
+ supported.
+ Word wrapping is not supported.
+
+ pixbuf
+
+ Description
+
+ A still graphics to video generator using gdk-pixbuf
+
+ Constructor Argument
+
+ 'file' - The name of a graphics file loadable by
+ a gdk-pixbuf loader. see /usr/lib/gdk-pixbuf/loaders
+ definitely png, jpeg, tiff, pnm, and xpm
+ - If "%" in filename, the filename is used with sprintf
+ generate a filename from a counter for multi-file/flipbook
+ animation. The file sequence ends when numeric
+ discontinuity >100.
+ - If filename contains "/.all.", suffix with an extension to
+ load all pictures with matching extension from a directory.
+ - If filename contains the string "<svg", then pixbuf tries
+ to load the filename as inline SVG XML, which is convenient
+ for inigo commands.
+
+ Details
+
+ Pixbuf has builtin scaling. It will rescale the originally rendered
+ title to whatever the consumer requests. Therefore, it will lose
+ its aspect ratio if so requested, and it is up to the consumer to
+ request a proper width and height that maintains the image aspect.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ int begin - when using an image sequence, this sets the starting
+ number.
+
+ Mutable Properties
+
+ int ttl - how long (in frames) to repeat each picture in file
+ sequences. default is 25.
+
+ Read Only Properties
+
+ string resource - file location. See Constructor Argument above.
+ int real_width - the original, unscaled width of the rendered title.
+ int real_height - the original, unscaled height of the title.
+ int width - the last requested scaled image width.
+ int height - the last requested scaled image height.
+
+ Dependencies
+
+ libgdk_pixbuf-2.0, libglib-2.0, libgobject-2.0, libgmodule-2.0
+
+ Known Bugs
+
+ XXX: in/out settings are incorrectly handled.
+
+ ppm
+
+ Description
+
+ Reads a stream of contiguous PPM images.
+
+ Constructor Argument
+
+ command - a shell command to run something that produces ppm
+ streams on stdout.
+
+ Initialisation Properties
+
+ none
+
+ Read Only Properties
+
+ string resource - the command
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Since it uses pipes, it is not compatible with bluefish.
+
+ westley
+
+ Description
+
+ Construct a service network from an XML description.
+ See docs/westley.txt.
+
+ Constructor Argument
+
+ URL - an XML text file containing westley XML (schema/DTD pending)
+ - Since westley files can be parameterised, the URL syntax is:
+ {file-name}[?{param-name}{'='|':'}{param-value}[&{param-name}{'='|':'}{param-value}...]]
+ A colon is allowed instead of an equal sign to pacify inigo,
+ who tokenises anything with an equal sign as a property
+ setting. Also, when running inigo from the shell, beware of
+ the '?' and shell filename expansion. You can surround the URL
+ with single quotations to prevent expansion. Finally, fezzik
+ will fail to match the filename when you use parameters, so
+ preface the url with 'westley:' to force fezzik to load with
+ the westley service.
+
+ Read Only Properties
+
+ string resource - file location
+
+ Dependencies
+
+ libxml2
+
+ Known Bugs
+
+ Non-referenced producers and playlists are not destroyed until the
+ network is destroyed.
+ A referenced producer or playlist must appear before the reference.
+ A filter that occurs before a producer has been defined causes a
+ segfault.
+
+ vorbis
+
+ Description
+
+ OGG Vorbis file reader.
+
+ Constructor Argument
+
+ 'file' - file to use (only .ogg supported at the moment)
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ double fps - this is fixed at 25 for PAL currently
+
+ Dependencies
+
+ libvorbisfile
+
+ Known Bugs
+
+ Fixed frame size (PAL audio chunks).
+ Doesn't cover ogg files with multiple, differing sections.
+
+Filters
+-------
+
+ brightness
+
+ Description
+
+ Shift the luma component using a constant value.
+
+ Constructor Argument
+
+ start - the constant floating point numeric value to apply.
+ - the default is 0.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ double start - see Constructor Argument above.
+ double end - the ending adjustment value. the filter interpolates
+ between the start and end adjustments over the
+ duration of the effect.
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Does not go completely to black or white.
+
+
+ channelcopy
+
+ Description
+
+ Copy audio from one channel to another channel.
+
+ Constructor Argument
+
+ to - the 0-indexed channel to copy into, default is 1.
+
+ Mutable Properties
+
+ int to - see above
+ int from - the channel from which to copy, default is 0.
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+
+
+ deinterlace
+
+ Description
+
+ Deinterlace a frame consisting of two fields using bob, weave,
+ greedy, one-field, and linear blend methods. This code is
+ appropriated from the Xine XV video output plugin.
+
+ Constructor Argument
+
+ method - a string containing the deinterlace method: bob, weave,
+ greedy, onefield, or linearblend. The default is
+ linearblend.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+ string method
+
+ Details
+
+ If the frame properties "progressive" or "consumer_progressive"
+ are non-zero, then the filter is not applied. Also, if applied,
+ this sets the frame property "progressive" to 1.
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Not a bug, but it only provides fair quality.
+
+
+ gamma
+
+ Description
+
+ Adjust image luma using a non-linear power-law curve
+
+ Constructor Argument
+
+ gamma - a floating point value. The default is 1.0, or none.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ double gamma - the exponential factor of the power-law curve
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+
+ greyscale
+
+ Description
+
+ Convert colour image to greyscale
+
+ Constructor Argument
+
+ none
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+ gtkrescale
+
+ Description
+
+ Scale the producer video frame size to match the consumer.
+ This filter is designed for use as a Fezzik normaliser.
+
+ Constructor Argument
+
+ interpolation - the rescaling method, one of:
+ nearest (lowest quality, fastest),
+ tiles,
+ bilinear (default; good quality, moderate speed),
+ hyper (best quality, slowest).
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ string interpolation - see constructor argument above
+
+ If a property "consumer_aspect_ratio" exists on the frame, then
+ rescaler normalises the producer's aspect ratio and maximises the
+ size of the frame, but may not produce the consumer's requested
+ dimension. Therefore, this option works best in conjunction with the
+ resize filter. This behavior can be disabled by another service by
+ either removing the property, setting it to zero, or setting
+ frame property "distort" to 1.
+
+ Dependencies
+
+ libgdk_pixbuf-2.0, libglib-2.0, libgobject-2.0, libgmodule-2.0
+
+ Known Bugs
+
+ none
+
+ jackrack
+
+ Description
+
+ Creates Jack ports and runs a JackRack project to process audio
+ through a stack of LADSPA filters.
+
+ Constructor Argument
+
+ src - a JackRack file
+
+ Details
+
+ If you are using a consumer that uses ALSA, then you should start
+ jackd with the dummy driver: jackd -d dummy.
+ I also recommend using a period size of 2048: -p 2048.
+
+ jackd -ddummy -r48000 -p2048
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ The following can be used without a rack file in order to connect
+ filter_jackrack to a running instance of JackRack:
+ string out_1 - Jack port to connect MLT's output port (JackRack's input)
+ string out_2 - Jack port to connect MLT's output port (JackRack's input)
+ string in_1 - Jack port to connect MLT's input port (JackRack's output)
+ string in_2 - Jack port to connect MLT's input port (JackRack's output)
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+ none
+
+ Dependencies
+
+ Jack, LADSPA, glib-2.0, libxml2
+
+ Known Bugs
+
+ Currently, due to timing and synchronisation issues, the audio
+ is distorted with repeated samples and latency clicks.
+ no encapsulated resampling and jack runs at a fixed frequency
+
+ ladspa
+
+ Description
+
+ Runs a JackRack project to process audio through a stack of
+ LADSPA filters without using Jack.
+
+ Constructor Argument
+
+ src - a JackRack file
+
+ Details
+
+ Due to audio integrity issues with the jackrack filter, this
+ filter is better for runtime, while jackrack is more suitable
+ for prototyping a rack file using the Jack Rack GUI.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+ none
+
+ Dependencies
+
+ Jack, LADSPA, glib-2.0, libxml2
+ Jack is still required because this coexists with the jackrack
+ filter.
+
+ Known Bugs
+
+ no encapsulated resampling and jack runs at a fixed frequency
+
+ luma
+
+ Description
+
+ Applies a luma transition between the current and next frames.
+ Useful for transitions from a slideshow created using producer
+ pixbuf.
+
+ Constructor Argument
+
+ file - a luma wipe
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+ int period - how long to wait between success iterations of the
+ transition. For best results set this to a multiple
+ of ttl used in pixbuf. The default is 24.
+
+ luma. - all properties beginning with "luma." are passed to the
+ encapsulated luma transition. For example, luma.out controls
+ the duration of the wipe.
+
+ Dependencies
+
+ transition_luma and its dependencies
+
+ Known Bugs
+
+ none
+
+ mcrescale
+
+ Description
+
+ Scale the producer video frame size to match the consumer.
+ This filter is designed for use as a Fezzik normaliser.
+
+ Constructor Argument
+
+ interpolation - the rescaling method, one of:
+ nearest (lowest quality, fastest),
+ bilinear (default; good quality, moderate speed),
+ hyper (best quality, slowest).
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ string interpolation - see constructor argument above
+
+ If a property "consumer_aspect_ratio" exists on the frame, then
+ rescaler normalises the producer's aspect ratio and maximises the
+ size of the frame, but may not produce the consumer's requested
+ dimension. Therefore, this option works best in conjunction with the
+ resize filter. This behavior can be disabled by another service by
+ either removing the property, setting it to zero, or setting
+ frame property "distort" to 1.
+
+ Dependencies
+
+ the mainconcept rescaling sdk.
+
+ Known Bugs
+
+ none
+
+ mirror
+
+ Description
+
+ Provides various mirror and image reversing effects.
+
+ Constructor Argument
+
+ mirror - horizontal, vertical, diagonal, flip, flop
+
+ Initialisation Properties
+
+ int reverse - being 0 or 1
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+
+ mono
+
+ Description
+
+ Mix all channels of audio into a mono signal and output it as
+ N channels.
+
+ Constructor Argument
+
+ channels - the number of output channels (default 2)
+
+ Initialisation Properties
+
+ none
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+
+ obscure
+
+ Description
+
+ Obscuring filter.
+
+ Constructor Argument
+
+ none
+
+ Initialisation Properties
+
+ string start - in the format X,Y:WxH[:PWxPY]
+ - PWxPY is the size of the averaging region in pixels.
+ string end - in the format X,Y:WxH[:PWxPY]
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+ region
+
+ Description
+
+ Apply one or more filters to a region of the video image. The region
+ can be shaped as well using the alpha channel of another producer.
+
+ Constructor Argument
+
+ resource - A file whose alpha channel will "shape" the region.
+ - The string "circle" is a shortcut but it requires pixbuf with
+ the librsvg loader. The circle is automatically stretched
+ to the region to create an ellipse.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ plus and region transition properties
+
+ Read Only Properties
+
+ see the region transition
+
+ Dependencies
+
+ transition_region
+
+ Known Bugs
+
+ "circle" is unpredictable in the absence of the librsvg pixbuf loader.
+
+
+ resample
+
+ Description
+
+ Adjust an audio stream's sampling rate, and duplicate channels if
+ producer provides less than consumer requested.
+
+ This filter is automatically invoked by Fezzik for the sake of
+ normalisation over inputs and with the consumer.
+
+ Constructor Argument
+
+ frequency - a numeric value for the new sample rate
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ int frequency - the target sample rate
+
+ Dependencies
+
+ libresample
+
+ Known Bugs
+
+ Assumes 2 channels during libsamplerate initialisation. Untested
+ with >2 channels.
+
+ rescale
+
+ Description
+
+ Scale the producer video frame size to match the consumer.
+ This filter is designed for use as a Fezzik normaliser.
+
+ Constructor Argument
+
+ None.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ If a property "consumer_aspect_ratio" exists on the frame, then
+ rescaler normalises the producer's aspect ratio and maximises the
+ size of the frame, but may not produce the consumer's requested
+ dimension. Therefore, this option works best in conjunction with the
+ resize filter. This behavior can be disabled by another service by
+ either removing the property, setting it to zero, or setting
+ frame property "distort" to 1.
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none but... it only implements a nearest neighbour scaling - it is
+ used as the base class for the gtkrescale and mcrescale filters.
+
+ resize
+
+ Description
+
+ Image scaling and padding and field order adjustment.
+
+ Details
+
+ Normally resize is used to pad the producer's
+ output to what the consumer has requested after an upstream rescale
+ filter first scales the image to maximise usage of the image area.
+ This filter also adjusts the field order to lower field first
+ if the frame property "top_field_first" has been set to 1.
+ Therefore, when done, it sets the top_field_first to 0.
+ This filter is automatically invoked by Fezzik as part of image
+ sample aspect ratio normalisation.
+
+ Constructor Argument
+
+ scale - "affine" to use affine transform scaling, otherwise
+ center and pad.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Assumes lower field first output.
+
+ volume
+
+ Description
+
+ Adjust an audio stream's volume level
+ - based on the 'normalize' utility
+
+ Constructor Argument
+
+ gain - a string containing one of:
+ - a floating point value of the gain adjustment
+ - a numeric value with the suffix "dB" to adjust in terms of decibels
+ - "normalise" to normalise the volume to the target amplitude -12dBFS
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ int window - the number of video frames over which to smooth normalisation.
+ defaults to 75.
+
+ Mutable Properties
+
+ string gain - same as constructor argument above
+
+ string normalise - normalise the volume to the amplitude:
+ - a numeric value with the suffix "dB" to set amplitude in decibels
+ - a floating point value of the relative volume
+ - an unspecified value uses the default -12dBFS
+
+ string limiter - limit all samples above:
+ - a numeric value with the suffix "dB"
+ - a floating point value ( dB = 20 * log10(x) )
+ - an unspecified value uses the default -6dBFS
+
+ double max_gain - a floating point or decibel value of the maximum gain that
+ can be applied during normalisation.
+ - an unspecified value uses the default 20dB
+
+ string end - a gain value just like the gain property above.
+ This causes the gain to be interpolated from 'gain' to 'end'
+ over the duration.
+
+ int window - the size of the normalising smoothing buffer in video frame units.
+ - the smoothing buffer prevents erratic gain changes.
+ - the default value is 75 video frames.
+
+ gain can be applied as a factor to the normalise amplitude!
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ none
+
+ watermark
+
+ Description
+
+ Add a watermark to the frames.
+
+ Constructor Argument
+
+ resource - the producer to use (ie: a .png)
+
+ Initialisation Properties
+
+ string resource - the producer to use
+ string factory - producer required for the resource ('fezzik')
+ string geometry - composite geometry
+ string distort - control scaling
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ none
+
+ Dependencies
+
+ mlt core modules and optionally, fezzik
+
+ Known Bugs
+
+ none
+
+Transitions
+-----------
+
+ composite
+
+ Description
+
+ A key-framable alpha-channel compositor for two frames.
+
+ Details
+
+ Performs dissolves and luma wipes in addition to alpha compositing.
+
+ By default, the aspect ratio of the B frame is respected and the
+ size portion of the geometry specification simply defines a
+ bounding rectangle.
+
+ This performs field-based rendering unless the A frame property
+ "progressive" or "consumer_progressive" or the transition property
+ "progressive" is set to 1.
+
+ Constructor Argument
+
+ none[*]
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ string factory - The name of a factory service used as a non-PGM
+ producer loader. The default is fezzik.
+
+ Read Only Properties
+
+ none
+
+ Mutable Properties
+
+
+ string geometry - key frame specification
+ - this is a ; delimited form of the deprecated start,
+ key[n], end properties
+ int progressive - set to 1 to disable field-based rendering.
+ string distort - when set, causes the B frame image to fill the WxH
+ completely with no regard to B's aspect ratio.
+ string halign - when not distorting, set the horizontal alignment
+ of B within the geometry rectangle.
+ - one of: left (0), centre (1), or right (2).
+ - the default is left.
+ string valign - when not distorting, set the vertical alignment of
+ B within the geometry rectangle.
+ - one of: top (0), middle (1), or bottom (2).
+ - the default is top.
+ string luma - the luma map file name. If not supplied, a dissolve.
+ double softness - only when using a luma map, how soft to make the
+ edges between A and B. 0.0 = no softness. 1.0 =
+ too soft.
+ Any property starting with "luma." is passed to the non-PGM luma
+ producer.
+
+ Deprecated Properties
+
+ string start - a geometry specification as X,Y:WxH[!][:mix]
+ - X, Y, W, H are assumed to pixel units unless they
+ have the suffix '%'
+ - '!' is a shortcut to specify distort, see below.
+ - mix is always a 2 digit percentage, defaults to 100.
+ - default is "85%,5%:10%x10%"
+ string end - the ending size and position.
+ string key[F] - X,Y:WxH[:mix] - set a key frame for geometry between
+ the in and out. F is a frame number and can be
+ negative to make it relative to the out point.
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Assumes lower field first during field rendering.
+
+ luma
+
+ Description
+
+ A generic dissolve and wipe transition processor.
+
+ Details
+
+ luma gets its name
+ from how it uses a grayscale "map" file. As the luma value varies
+ over time, a threshold filter is applied to the map to determine
+ what parts of frame A vs. frame B to show. It reads PGM files
+ up to 16 bits! Alternatively, it can use the first frame from any
+ producer that outputs yuv, but it will be limited to the luma
+ gamut of 220 values.
+ This performs field-based rendering unless the A frame property
+ "progressive" or "consumer_progressive" or the transition property
+ "progressive" is set to 1.
+
+ Constructor Argument
+
+ string resource - the luma map file name - either PGM or any other
+ producable video.
+ - If not supplied, a dissolve.
+
+ Initialisation Properties
+
+ int in - in point
+ int out - out point
+ string factory - The name of a factory service used as a non-PGM
+ producer loader. The default is Fezzik.
+
+ Mutable Properties
+
+ string resource - same as above
+ double softness - only when using a luma map, how soft to make the
+ edges between A and B. 0.0 = no softness. 1.0 =
+ too soft.
+ int reverse - reverse the direction of the transition.
+ Any property starting with "producer." is passed to the non-PGM luma
+ producer.
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Assumes lower field first output.
+
+ mix
+
+ Description
+
+ An two stream audio mixer.
+
+ Constructor Argument
+
+ start - see below
+
+ Initalisation Properties
+
+ int in - in point
+ int out - out point
+
+ Mutable Properties
+
+ double start - the mix level to apply to the second frame.
+ - any negative value causes an automatic crossfade from 0 to 1.
+ double end - the ending value of the mix level. mix level will be interpolated
+ from start to end over the in-out range.
+ int reverse - set to 1 to reverse the direction of the mix.
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ none
+
+ Known Bugs
+
+ Samples from the longer of the two frames are discarded.
+
+
+ region
+
+ Description
+
+ Apply zero or more filters to B frame as it is composited onto
+ a region of the A frame. The "shape" of the region can be defined
+ by the alpha channel of a third producer.
+
+ Constructor Argument
+
+ resource - a shape producer
+
+ Initialisation Properties
+
+ string resource - nothing is rectangle, "circle" is a pixbuf-
+ generated SVG circle, anything else is loaded by the factory.
+ string factory - the service that creates the shape producer.
+ - the default is fezzik.
+ string filter[N] - one or more filters to apply. All filter
+ properties are passed using the same filter "key"
+ Any property starting with "composite." is passed to the
+ encapsulated composite transition.
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ transition_composite
+
+ Known Bugs
+
+ none
+
+
+Consumers
+---------
+
+ avformat
+
+ Description
+
+ Multiformat transcoding consumer.
+
+ Constructor Argument
+
+ string target - the filename to write to, e.g. test.mpeg.
+
+ Initialisation Properties
+
+ int buffer - the number of frames to buffer, minimum 1, default 25.
+ string rescale - a rescale method, see the Filters/rescale.
+ int progressive - indicates whether to use progressive or field-
+ based rendering, default 0 (off).
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ libavformat
+
+ Known Bugs
+
+ Plenty.
+
+ bluefish (Proprietary)
+
+ Description
+
+ BlueFish444 audio and video output module.
+
+ Constructor Argument
+
+ card - a numeric card id starting at 1, default is 1.
+
+ Initialisation Properties
+
+ string standard - "PAL" (default) or "NTSC"
+ - default is based upon MLT_NORMALISATION
+ environment variable, which defaults to PAL.
+ int frames - the number of DMA video frames. default is 8.
+ minimum is 2. maximum on my system is 11.
+ int buffer - the number of frames to buffer within MLT, minimum 1,
+ default 25.
+ string rescale - a rescale method, see the Filters/rescale.
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ BlueVelvet SDK installed parallel to mlt in "bluefish."
+
+ Known Bugs
+
+ Does not work with any service that uses pipes!
+
+ If mlt crashes, you might need to reload the BlueDriver kernel
+ module due to unreleased DMA buffers.
+
+ Only supports 2 channel audio at the moment.
+
+ libdv
+
+ Description
+
+ libdv dv producer.
+
+ Constructor Argument
+
+ string target - the filename to write to, e.g. /dev/dv1394.
+
+ Initialisation Properties
+
+ int buffer - the number of frames to buffer, minimum 1, default 25.
+ string rescale - a rescale method, see the Filters/rescale.
+
+ Mutable Properties
+
+ int progressive - indicates whether to use progressive or field-
+ based rendering, default 0 (off).
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ libdv
+
+ Known Bugs
+
+ none
+
+ mcmpeg
+
+ Description
+
+ Mainconcept MPEG encoder.
+
+ Constructor Argument
+
+ string target - the filename to write to.
+
+ Initialisation Properties
+
+ int buffer - the number of frames to buffer, minimum 1, default 25.
+ string rescale - a rescale method, see the Filters/rescale.
+ string format - vcd [default], svcd or dvd provide base settings
+ int motion_search_type - 0 to 16 - reduces quality/cpu usage
+ int gop - group of picture size (default: format dependent)
+
+ Mutable Properties
+
+ int progressive - indicates whether to use progressive or field-
+ based rendering, default 0 (off).
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ Mainconcept MPEG SDK
+
+ Known Bugs
+
+ none
+
+ sdl
+
+ Description
+
+ Simple DirectMedia Layer audio and video output module.
+
+ Constructor Argument
+
+ string video_standard - "PAL" (default), "NTSC", or "WxH"
+
+ Initialisation Properties
+
+ int buffer - the number of frames to buffer, minimum 1, default 25.
+ string rescale - a rescale method, see the Filters/rescale.
+ - Hint: "none" works very good with SDL output.
+
+ Mutable Properties
+
+ double volume - audio level factor
+ int video_off - if 1, disable video output
+ int audio_off - if 1, disable audio output
+ int resize - TODO
+ int progressive - indicates whether to use progressive or field-
+ based rendering, default 0 (off).
+ int audio_buffer - size of the sdl audio buffer (default: 1024)
+
+ Read Only Properties
+
+ none
+
+ Dependencies
+
+ libSDL-1.2, libasound, libaudio, libXt, libX11, libXext, libSM, libICE
+
+ Known Bugs
+
+ none
+
+ westley
+
+ Description
+
+ Serialise the service network to XML.
+ See docs/westley.txt for more information.
+
+ Constructor Argument
+
+ resource - the name of a file in which to store the XML.
+ - stdout is default.
+
+ Initialisation Properties
+
+ string resource - same as above.
+
+ Dependencies
+
+ libxml2
+
+ Known Bugs
+
+ Untested arbitrary nesting of multitracks and playlists.
+ Property "id" is generated as service type followed by number if
+ no property named "id" exists, but it fails to guarantee uniqueness.
--- /dev/null
+On 1/10/2004, Dan Dennedy ran the testing.txt against mlt albino and miracle.
+
+
+NOTE: Discrepancies cited here may have impact on related functionality.
+
+
+General
+------------------------------------------------------------------------------
+Server side error checks and related response error codes are not stringently enforced.
+
+
+Not Implemented
+------------------------------------------------------------------------------
+NLS
+USET points=ignore
+USET eof=terminate
+
+
+Incorrect Behaviour
+------------------------------------------------------------------------------
+
+
+Different Intentional Behaviour
+------------------------------------------------------------------------------
+
+Different forced Behaviour
+------------------------------------------------------------------------------
+killall miracle does not work when the SDL consumer is in use. requires killall -HUP
+
+MLT Bugs
+------------------------------------------------------------------------------
+Please check the services.txt doc for known bugs related to MLT components.
+
+
+
--- /dev/null
+Miracle Test Procedure
+
+Copyright (C) 2003 Ushodaya Enterprised Limited
+Author: Dan Dennedy <dan@dennedy.org>
+Last Revision: 2004-03-20
+
+
+NOTE: THIS DOCUMENT REQUIRES REVISION TO NEW, EXPECTED BEHAVIOR FROM MIRACLE.
+
+Tests are divided into 9 sections:
+
+ 1. Command Line Usage
+ 2. Unit Management
+ 3. Server Configuration
+ 4. Simple Playback
+ 5. Multi-unit Playback
+ 6. Unit Configuration
+ 7. Advanced Playback
+ 8. Bus Reset
+ 9. Server Side Queuing
+
+Each section contains many tests which I've divided into a minimum of two lines:
+
+n.m action to carry out
+--> expected result
+
+Further lines may appear to show the actual results when they deviate from what
+I expected or if there are special cases to consider.
+
+Sequential tests are indicated as:
+
+n.m.o action to carry out
+--> expected result
+
+It is suggested that you run top during the testing and note cpu hikes
+or any excessive memory usage related to an operation.
+
+
+0. Introduction
+---------------
+
+The tests following are by no means exhaustive, but they should cover typical
+use cases - creativity is encouraged with more cases being added where necessary.
+This document should also be maintained to dictate actual state, especially with
+regard to a final release. Unit test cases are encouraged, but are excluded from
+this document.
+
+It is important to carry out the full test cycle when preparing a final release.
+In this situation, please resist the temptation to bug fix a given test case and
+resume the tests from that point onward - it is better to repeat from the
+beginning (but you can of course employ common sense in this situation).
+
+Before starting the final tests, please delete/backup your current
+/etc/dv139d.conf file. This (more or less) ensures that tests are carried out
+for a virgin install.
+
+
+1. Command Line Usage
+---------------------
+
+Run these from the top level project directory
+
+1.1.0 Start miracle in interactive mode: src/miracle/miracle -test
+--> miracle starts interactively and reports:
+(5) Starting server on 5250.
+(5) miracle version 0.0.1 listening on port 5250
+
+1.1.1 Stop the server by pressing Ctrl-C
+--> miracle returns the following and returns control to the console:
+(5) miracle version 0.0.1 server terminated.
+
+1.2.2 Start miracle as a daemon: src/miracle/miracle
+--> control returns to the console
+
+1.2.3 Verify miracle is running: ps ax
+--> several miracle processes are running
+
+1.2.4 Verify successful miracle startup using syslog: sudo tail /var/log/syslog
+--> miracle: miracle version 0.0.1 listening on port 5250
+
+1.2.5 Verify connectivity on port 5250: telnet localhost 5250
+--> 100 VTR Ready
+
+1.2.6 Test clean disconnect: BYE
+--> Connection closed by foreign host.
+
+1.2.7 Stop the daemon: killall miracle
+--> no errors
+
+1.2.8 Verify a clean server shutdown: sudo tail /var/log/syslog
+--> miracle: shutdown complete.
+
+1.3.0 Start miracle on a different port: src/miracle/miracle -port 5260
+
+1.3.1 Verify successful miracle startup using syslog: sudo tail /var/log/syslog
+--> miracle: miracle version 0.0.1 listening on port 5260
+
+1.3.2 Verify connectivity on port 5260: telnet localhost 5260
+--> 100 VTR Ready
+
+1.3.3 Test clean disconnect: BYE
+--> Connection closed by foreign host.
+
+1.3.4 Stop the daemon: killall miracle
+--> no errors
+
+
+2. Unit Management
+------------------
+
+Start the miracle server and connect to it with telnet or a protocol-
+level client (albino).
+
+2.1 List the AV/C devices on the bus: NLS
+--> 201 OK
+--> one or more lines listing devices with GUID in second column
+
+2.2 Add a device as a miracle unit: UADD {sdl, bluefish}
+--> 201 OK
+--> U0
+
+2.3 List the units: ULS
+--> 201 OK
+--> U0 ?? {sdl, bluefish} 1
+--> It is important that the last column is '1' to indicate it is online.
+
+2.4 List the units: ULS
+--> 201 OK
+--> U0 ?? {sdl, bluefish} 1
+
+2.5 Attempt unit commands for a unit that does not exist: LOAD U6 foo
+--> 403 Unit not found
+
+2.6 Attempt unit commands without specifying a unit: PLAY
+--> 402 Argument missing
+
+2.7 Attempt unit commands for a unit: PLAY U0
+--> 200 OK
+
+2.8.0 Load a clip into an unit: LOAD U0 test.dv
+--> 200 OK
+
+2.7.1 Verify the status of the unit: USTA U0
+--> 202 OK
+--> 0 online "test.dv" 0 1000 25.00 0 ...
+--> only the first 3 columns are relevant in this test
+
+
+3. Server Configuration
+-----------------------
+
+Start miracle if not already started from a previous test.
+
+3.1 Get the hard-coded default root property value: GET root
+--> 202 OK
+--> /
+
+3.2 List the files and subdirectories at the root: CLS /
+--> 201 OK
+--> "bin/"
+--> ...
+
+3.3 Change the server root to a place where clips are stored: e.g.,
+ SET root=/tmp
+--> 200 OK
+
+3.4 Get the new value of the root property value: GET root
+--> 202 OK
+--> /tmp/
+--> Notice that if you did not use a trailing slash in step 2.3, one is
+ added for you and appears in this step. This is normal and correct.
+
+3.5 List the files and subdirectories at the root: CLS /
+--> 201 OK
+--> zero or more lines listing subdirectories followed by files.
+
+3.6 Try to set a property that does not exist: SET foo=bar
+--> 405 Argument value out of range
+
+3.7 Try to set no property or value: SET
+--> 402 Argument missing
+
+3.8 Attempt a bogus command: FOO
+--> 400 Unknown command
+
+XXX 3.9 Attempt the incorrect case for a command: get root
+XXX --> 400 Unknown command
+
+3.10 Attempt case insensitivity of property key: GET Root
+--> 202 OK
+--> /tmp/
+
+
+4. Simple Playback
+-------------------
+
+Start miracle or restart if already started.
+Add an online unit.
+Set the server root property if desired.
+
+4.1.0 Load a clip into the unit: LOAD U0 test.dv
+--> 200 OK
+
+4.1.1 Check the unit status: USTA U0
+--> 202 OK
+--> 0 stopped "test.dv" 0 1000 25.00 0 ...
+--> Only the first 3 columns are relevant in this test.
+--> The remaining columns are only relevant to the tester.
+
+4.2.0 Play the clip: PLAY U0
+--> 200 OK
+--> Verify audio and video output
+
+4.2.1 Check the unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 1739 1000 25.00 0 ...
+--> Only the first 3 columns are relevant in this test.
+--> The remaining columns are only relevant to the tester.
+
+4.3.0 Pause playback: PAUSE U0
+--> 200 OK
+--> Verify video continues, but audio is muted.
+
+4.3.1 Check the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 1739 0 25.00 0 ...
+--> The fifth column --------^ should be 0; it indicates speed.
+
+4.3.2 Stop playback: STOP U0
+--> 200 OK
+--> The analog video output stops
+
+4.3.3 Pause playback: PAUSE U0
+--> 200 OK
+--> Analog video starts again, but it is held on the same frame
+ paused in 4.3.0.
+
+4.3.4 Stop playback: STOP U0
+--> 200 OK
+--> The analog video signal ceases.
+
+4.3.5 Rewind the unit: REW U0
+--> 200 OK
+
+4.3.6 Play the unit: PLAY U0
+--> 200 OK
+--> Analog audio and video are produced from the beginning of the file.
+
+4.4 Stop the server during playback and ensure clean shutdown.
+
+
+5. Multi-unit Playback
+-----------------------
+
+Start miracle or restart if already started.
+Add *2* online units.
+Set the server root property if desired.
+
+5.1.0 Load a clip into one unit: LOAD U0 test.dv
+--> 200 OK
+
+5.1.1 Load a clip into the other unit: LOAD U1 test.dv
+--> 200 OK
+
+5.1.2 Start playing one unit: PLAY U0
+--> 200 OK
+--> Verify audio and video output
+
+5.1.3 Start playing the other unit: PLAY U1
+--> 200 OK
+--> Verify audio and video output of both units
+
+5.2 Verify independence of units by pausing one of them: PAUSE U0
+--> 200 OK
+--> Verify video continues, but audio is muted on the first unit only.
+
+5.3 Stop the server during multi-unit playback and ensure clean shutdown.
+
+
+6. Advanced Playback
+--------------------
+
+Start miracle or restart if already started.
+Add *2* online units.
+Set the server root property if desired.
+
+Trick play modes:
+
+6.1.0 Load a clip: LOAD U0 test.dv
+--> 200 OK
+
+6.1.1 Start playback by pausing on the first frame: PAUSE U0
+--> 200 OK
+--> analog video starts, but audio is muted.
+
+6.1.2 Play fast forward: FF U0
+--> 200 OK
+--> verify video is playing fast in the forward direction.
+
+6.1.3 Get unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 219 2000 25.00 0 ...
+--> The important column is --^, indicates speed
+
+6.1.4 Play fast reverse: REW U0
+--> 200 OK
+--> verify analog video output is fast in the reverse direction.
+
+6.1.5 Get unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 4621 -2000 25.00 0 ...
+--> The important column is ---^, negative mean reverse
+
+6.1.6 Play slow forward: PLAY U0 500
+--> 200 OK
+--> Verify the analog video output is slow in the forward direction.
+
+6.1.7 Play reverse normal speed: PLAY U0 -1000
+--> 200 OK
+--> Verify the analog video output is at a normal speed in the reverse direction.
+--> Audio output is reverse, but not the field order of video
+
+Loading while playing:
+
+6.2.0 Stop the unit (might be playing): STOP U0
+--> 200 OK
+
+6.2.1 Rewing the unit: REW U0
+--> 200 OK
+
+6.2.2 Start playing: PLAY U0
+--> 200 OK
+--> verify analog audio and video output
+
+6.2.3 Load another clip: LOAD test002.dv
+--> 200 OK
+--> playback seamlessly switches to the new clip
+--> verify the analog appearance of the video makes a clean switch
+
+6.2.4 Load another clip, this time with in and out points:
+ LOAD test.dv 100 500 (whatever works for your test footage)
+--> 200 OK
+--> verify the analog appearance of the video makes a clean switch
+
+6.2.5 Get unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 403 1000 25.00 100 ...
+--> verify position -----^ is beyond --^ in point, last column is the out
+ point specified in the previous step.
+
+Edit points:
+
+6.3.0 Load a clip: LOAD U0 test.dv
+--> 200 OK
+
+6.3.1 Pause the playback unit: PAUSE U0
+--> 200 OK
+
+6.3.2 Set the in point: SIN U0 100
+--> 200 OK
+--> verify the frame displayed in analog video out changes
+
+6.3.4 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 100 0 25.00 100 ...
+--> verify position ---^ and in ---^
+
+6.3.5 Change the mode of the unit to not restrict playback to the edit
+ region: USET U0 points=ignore
+--> 200 OK
+
+6.3.6 Jump to a frame before the in frame: GOTO U0 50
+--> 200 OK
+
+6.3.7 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 50 0 25.00 100 ...
+--> position ----------^ preceeds -^ (in)
+
+6.3.8 Set the unit mode to restrict playback to the edit region: USET U0 points=use
+--> 200 OK
+--> verify frame on analog video output changes
+
+6.3.9 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 100 0 25.00 100 ...
+--> verify position ---^ and in ---^
+
+6.3.10 Clear the in point: SIN U0 -1
+--> 200 OK
+
+6.3.11 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 100 0 25.00 0 ...
+--> verify the in point is reset --^
+
+The above sequence should be repeated in a similar manner for the out point
+using the SOUT command.
+
+Transfer:
+
+6.4.0 Load a clip into the first unit: LOAD U0 test.dv
+--> 200 OK
+
+6.4.1 Load a clip into the second unit: LOAD U1 test002.dv
+--> 200 OK
+
+6.4.2 Start playing the first unit: PLAY U0
+--> 200 OK
+--> verify audio and video analog output
+
+6.4.3 Set an in point on the clip in the second unit: SIN U1 100
+--> 200 OK
+
+6.4.4 Play the second unit: PLAY U1
+--> 200 OK
+--> note the beginning footage
+
+6.4.5 Transfer the clip from the second to the first unit: XFER U1 U0
+--> 200 OK
+--> verify a clean switch on the analog audio and video output of the first unit.
+--> upon transfer it should play the same footage previewed in step 6.4.4
+
+6.4.5 Get the first unit's status: USTA U0
+--> 202 OK
+--> 0 playing "test002.dv" 963 1000 29.97 100 2502
+--> note the in point set from U1 ---------^
+
+
+7. Unit Configuration
+---------------------
+
+Start miracle or restart if already started.
+Add an online unit.
+Set the server root property if desired.
+
+7.1.0 Load a short clip: LOAD U0 test.dv
+--> 200 OK
+
+7.1.1 Play a clip: PLAY U0
+--> 200 OK
+--> Wait until it gets to the end, and it should pause on the last frame.
+
+7.1.2 Make the clip start looping: USET U0 eof=loop
+--> 200 OK
+--> verify the clip starts playing from the beginning and loops
+
+7.2.0 Set the in point: SIN U0 10
+--> 200 OK
+--> playback pauses at in point (verify with USTA U0)
+
+7.2.1 Set the out point: SOUT U0 200
+--> 200 OK
+--> playback pauses at in point (verify with USTA U0)
+
+7.2.2 Start playing again: PLAY U0
+--> 200 OK
+--> verify playback loops between in and out points
+
+7.3 Tell the unit to ignore the edit points: USET U0 points=ignore
+--> 200 OK
+--> verify playback loops over entire video file
+
+7.4 Get the current value of the points property: UGET U0 points
+--> 202 OK
+--> ignore
+
+
+9. Server Side Queuing
+----------------------
+
+Only one unit is used for these test cases, and
+users are encouraged to test with multiple units online. It is assumed that a
+number of dv files are available for use in the servers ROOT directory - this
+document assumes that they are named test001.dv and up.
+
+9.1.0 Start miracle in interactive mode and add a unit (all tests will assume U0)
+--> server started with unit 0 available
+
+9.1.1 Obtain a miracle shell (via telnet or albino).
+--> 100 VTR (if reported by the client)
+
+9.1.2 Load a clip with LOAD U0 test001.dv and PAUSE U0
+--> 200 OK
+
+9.1.3 List the clips with LIST U0
+--> 201 OK
+--> 1
+--> 0 "test001.dv" 0 6999 7000 7000 25.00
+--> The 1 on the second line denotes the number of times the list has been changed
+ via user commands (known as the 'generation' number).
+--> The third line and beyond reports the clip index (from 0 to n), file loaded, in point,
+ out point, real size of the file and the calculated size (out - in + 1 ).
+
+9.1.4 Check the unit status with USTA U0
+--> 202 OK
+--> 0 paused "test001.dv" 0 0 25.00 0 6999 7000 "test001.dv" 0 0 6999 7000 1 1 0
+--> The last two fields indicate the generation number and current clip resp.
+
+9.1.5 Append a clip with APND U0 test002.dv followed by LIST U0
+--> 201 OK
+--> 2
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 2 and current clip of 0
+
+9.1.6 Move clip 1 to clip 0 with MOVE U0 1 0 followed by LIST U0
+--> 201 OK
+--> 3
+--> 0 "test002.dv" 0 6999 7000 7000
+--> 1 "test001.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 3 and current clip of 1
+
+9.1.7 Move clip 0 to clip 1 with MOVE U0 0 1 followed by LIST U0
+--> 201 OK
+--> 4
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 4 and current clip of 0
+--> Note that the order in which you run 9.1.6 and 9.1.7 shouldn't matter as the
+ result will be identical
+
+9.1.8 Change the position to the next clip with GOTO U0 0 +1
+--> 200 OK
+--> Check that USTA U0 reports a generation of 4 and current clip of 1
+
+9.1.9 Remove all but the playing clip with CLEAN U0 followed by LIST U0
+--> 201 OK
+--> 5
+--> 0 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 5 and current clip of 0
+
+9.1.10 Insert test001.dv back into the list using INSERT U0 test001.dv and run LIST U0
+--> 201 OK
+--> 6
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 6 and current clip of 1
+
+9.1.11 Insert test003.dv at position 2 using INSERT U0 test001.dv 3 and run LIST U0
+--> 201 OK
+--> 7
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> 2 "test003.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 7 and current clip of 1
+
+9.1.12 Change the in point of the current clip using SIN U0 5000 and run LIST U0
+--> 201 OK
+--> 8
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 8 and current clip of 1
+
+9.1.13 Change the out point of the following clip using SOUT U0 5000 +1 and run LIST U0
+--> 201 OK
+--> 9
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 0 5000 7000 5001
+--> Check that USTA U0 reports a generation of 9 and current clip of 2
+
+9.1.14 Change the in point of the current clip to 1000 using SIN U0 1000 and run LIST U0
+--> 201 OK
+--> 10
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 1000 5000 7000 4001
+--> Check that USTA U0 reports a generation of 10 and current clip of 2
+
+9.1.15 Ignore the in/out points by running USET U0 points=ignore and run LIST U0
+--> 201 OK
+--> 11
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 7000
+--> 2 "test003.dv" 1000 5000 7000 7000
+--> Check that USTA U0 reports a generation of 11 and current clip of 2
+
+9.1.16 Turn the in/out points on again by running USET U0 points=use and run LIST U0
+--> 201 OK
+--> 12
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 1000 5000 7000 4001
+--> Check that USTA U0 reports a generation of 12 and current clip of 2
+
+9.1.17 Remove the current clip using REMOVE U0 and run LIST U0
+--> 201 OK
+--> 13
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> Check that USTA U0 reports a generation of 13 and current clip of 0
+
+9.1.17 Remove the next clip using REMOVE U0 +1 and run LIST U0
+--> 201 OK
+--> 14
+--> 0 "test001.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 14 and current clip of 0
--- /dev/null
+Valerie API Documentation
+
+Copyright (C) 2004 Ushodaya Enterprised Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-03-20
+
+
+TABLE OF CONTENTS
+-----------------
+
+ 0. Overview
+ 0.1. Intended Audience
+ 0.2. Terminology
+ 1. Definition of a Parser
+ 1.1. Construction of a Local Parser
+ 1.2. Construction of a Remote Parser
+ 1.3. Using the Parser
+ 1.4. Closing the Parser
+ 2. The High Level Parser Wrapper
+ 2.1. Connecting
+ 2.2. valerie_error_code
+ 2.3. Using the High Level Wrapper
+ 2.4. Obtaining Directory Contents
+ 2.5. Obtaining the Node List
+ 2.6. Obtaining the Unit List
+ 2.7. Unit Status Information
+ 2.8. Server Side Queuing APIs
+ 2.9. Accessing the Low Level Parser Directly
+ 2.10. Cleaning up
+ 2.11. Examples
+ 3. The Low Level Parser API
+ 3.1. Executing a Command
+ 3.2. Interpreting valerie_response
+ 3.3. Accessing Unit Status
+ APPENDIX A - COMPILATION AND LINKING
+ APPENDIX B - COMPLETE HIGH LEVEL PARSER WRAPPER API LISTING
+ APPENDIX C - COMPLETE LOW LEVEL PARSER API LISTING
+ APPENDIX D - REFERENCES
+
+
+0. Overview
+-----------
+
+ This document details how applications interface to DVCP functionality.
+
+
+0.1. Intended Audience
+----------------------
+
+ This document draws heavily upon the DVCP design (1) and assumes a basic
+ knowledge of the functionality provided by the DVCP core.
+
+ It is aimed at developers who wish to use or maintain the API.
+
+
+0.2. Terminology
+----------------
+
+ The API is designed to allow client applications the ability to communicate
+ to a standalone miracle server or entirely embed the DVCP core in an
+ instance of a client application.
+
+ The distinction between the two is defined by the construction of the
+ 'parser'.
+
+ This 'parser' can be used to issue DVCP commands and receive responses and
+ a 'high level parser wrapper' is provided to simplify the usage and
+ decouple the application from the DVCP command set.
+
+
+1. Definition of a Parser
+-------------------------
+
+ The parser provides a low level API which allows text DVCP commands to be
+ executed with responses being returned to the caller. Commands and
+ responses are ASCII formatted text.
+
+ Two parsers are provided - local and remote.
+
+ The local parser is the physical implementation which takes commands and
+ executes them.
+
+ The remote parser is a network abstraction that forwards commands to a
+ miracle instance that hosts a local parser.
+
+
+1.1. Construction of a Local Parser
+-----------------------------------
+
+ To construct a local parser you must have:
+
+ #include <miracle/miracle_local.h>
+
+ and code to initialise the parser is as follows:
+
+ valerie_parser parser = miracle_parser_init_local( );
+
+ See Appendix A for compilation and linking details.
+
+
+1.2. Construction of a Remote Parser
+------------------------------------
+
+ To construct a remote parser you must have:
+
+ #include <valerie/valerie_remote.h>
+
+ and code to initialise the parser is as follows:
+
+ valerie_parser parser = valerie_parser_init_remote( "server", port );
+
+ See Appendix A for compilation and linking details.
+
+
+1.3. Using the Parser
+---------------------
+
+ Although the parser can be used directly to send commands and receive
+ responses, this low level usage puts the onus on the developer to parse the
+ responses in a meaningful way.
+
+ Although this usage is not strictly forbidden by applications, it is
+ discouraged as construction of commands and meaningful parsing of responses
+ leads to the clients being unnecessarily dependent on the servers input and
+ output.
+
+ As a result, a higher level Parser Wrapper API is provided - this API
+ encapsulates the command construction and response parsing.
+
+ The following 2 sections provide details on these modes of access.
+
+
+1.4. Closing the Parser
+-----------------------
+
+ Regardless of use, it is the constructors responsibility to close the
+ parser before it goes out of scope. This is done via:
+
+ valerie_parser_close( parser );
+
+
+2. The High Level Parser Wrapper
+--------------------------------
+
+ The recommended way to access the parser, is via the valerie API. To use
+ this API, you must have:
+
+ #include <valerie/valerie.h>
+
+ and code to construct the wrapper is:
+
+ valerie dv = valerie_init( parser );
+
+ Note that either remote or local parsers can be used here and there is no
+ difference in usage, though some error returns will not be applicable to
+ both.
+
+ It is recommended that applications honour and deal with the error returns
+ of both as this allows applications to interchange parsers.
+
+ Also note that valerie is not threadsafe, so you should not use the same
+ structure in multiple threads. The correct solution to this is to create a
+ valerie per thread - you may safely use the same parser for each thread ie:
+
+ /* valerie for the application */
+ valerie dv = valerie_init( parser );
+ /* valerie for the status handling thread. */
+ valerie dv_status = valerie_init( parser );
+
+ For the purposes of simplification, the remainder of this section assumes
+ that a remote parser is in use.
+
+
+2.1. Connecting
+---------------
+
+ Once constructed, the next thing to do is 'connect':
+
+ valerie_error_code error = valerie_connect( dv );
+
+ This function call initialises the parser (ie: if it's remote, it
+ establishes a connection to the server, or if it's local, it initialises
+ the state of the units and supporting objects).
+
+ Note that if you have multiple valerie instances on the same parser you
+ should only connect one of the instances.
+
+
+2.2. valerie_error_code
+----------------------
+
+ All but a couple of the functions that make up the valerie API return a
+ valerie_error_code.
+
+ These are defined as follows:
+
+ valerie_ok = 0,
+ valerie_malloc_failed,
+ valerie_unknown_error,
+ valerie_no_response,
+ valerie_invalid_command,
+ valerie_server_timeout,
+ valerie_missing_argument,
+ valerie_server_unavailable,
+ valerie_unit_creation_failed,
+ valerie_unit_unavailable,
+ valerie_invalid_file,
+ valerie_invalid_position
+
+ In most cases, it is sufficient to check on a return of valerie_ok.
+
+ To obtain a textual description of a particular error, you can use:
+
+ char *valerie_error_description( valerie_error_code );
+
+
+2.3. Using the High Level Wrapper
+---------------------------------
+
+ The following code snippet assumes that dv is an initialised and connected
+ valerie structure:
+
+ valerie_error_code error = valerie_unit_play( dv, 0 );
+ if ( error == valerie_ok )
+ fprintf( stderr, "Unit 0 is now playing\n" );
+ else
+ fprintf( stderr, "Play on unit 0 failed: %s\n",
+ valerie_error_description( error ) );
+
+ The complete interface to valerie is listed in Appendix B of this document.
+
+
+2.4. Obtaining Directory Contents
+--------------------------------
+
+ To obtain a list of files and subdirectories in a given directory relative
+ to the ROOT property of the server, DVCP provides the CLS command.
+
+ A valid execution of CLS would be something like:
+
+ CLS "/Stuff"
+
+ would provide a response formatted as follows:
+
+ 201 OK
+ "More Stuff/"
+ "file0001.dv" 15552000
+ "file0002.dv" 15552000
+
+ with a trailing empty line.
+
+ The first line indicates the error value, the second line shows an example
+ of a subdirectory and the 3rd and 4th line lists two files that happen to
+ exist in the directory.
+
+ valerie provides a high level view on this which automatically parses the
+ response from the server correctly via the valerie_dir structures and
+ related functions.
+
+ An example of use is as follows:
+
+ valerie_dir dir = valerie_dir_init( dv, "/Stuff" );
+ valerie_error_code error = valerie_dir_get_error_code( dir );
+ if ( error == valerie_ok )
+ {
+ if ( valerie_dir_count( dir ) > 0 )
+ {
+ valerie_dir_entry_t entry;
+ int index = 0;
+ for ( index = 0; index < valerie_dir_count( dir ); index ++ )
+ {
+ valerie_dir_get( dir, index, &entry );
+ if ( entry.dir )
+ printf( "<%s>\n", entry.name );
+ else
+ printf( "%30s %8d", entry.name, entry.size );
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Directory is empty\n" );
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Directory listing failed: %s\n",
+ valerie_error_description( error ) );
+ }
+ valerie_dir_close( dir );
+
+ Note that entry.name provides the name of the file or directory without the
+ directory prefix. As a convenience, entry.full provides the prefixed name,
+ so you could subsequently use:
+
+ error = valerie_unit_load( dv, 0, entry.full );
+
+ to load unit 0 with an entry.
+
+
+2.5. Obtaining the Node List
+----------------------------
+
+ Currently not defined by miracle.
+
+2.6. Obtaining the Unit List
+----------------------------
+
+ To obtain a list of defined units, DVCP provides the ULS command.
+
+ A valid execution of ULS would be:
+
+ ULS
+
+ and would provide a response formatted as follows:
+
+ 201 OK
+ U0 00 sdl:360x288 1
+
+ with a trailing empty line.
+
+ The fields of each record in the response dictate unit, node, mlt consumer and
+ online status respectively.
+
+ valerie provides a high level view on this which automatically parses the
+ response from the server correctly via the valerie_units structures and
+ related functions.
+
+ An example of use is as follows:
+
+ valerie_units units = valerie_units_init( dv );
+ valerie_error_code error = valerie_units_get_error_code( units );
+ if ( error == valerie_ok )
+ {
+ if ( valerie_units_count( units ) > 0 )
+ {
+ valerie_unit_entry_t entry;
+ int index = 0;
+ for ( index = 0; index < valerie_units_count( units ); index ++ )
+ {
+ valerie_units_get( units, index, &entry );
+ printf( "U%d %02d %s %s\n",
+ entry.unit,
+ entry.node,
+ entry.guid,
+ entry.online ? "online" : "offline" );
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Unit list is empty\n" );
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Unit listing failed: %s\n",
+ valerie_error_description( error ) );
+ }
+ valerie_units_close( units );
+
+
+2.7. Unit Status Information
+----------------------------
+
+ There are two methods for a client to obtain unit status information.
+
+ The first is via the DVCP USTA command, which would normally be accessed
+ via:
+
+ USTA U0
+
+ and would provide a response formated as follows:
+
+ 202 OK
+ 0 playing "a.dv" 58 1000 25.00 0 6999 7000 "a.dv" 157 0 6999 7000 1 4 0
+
+ with no trailing empty line.
+
+ The entries in the record are:
+
+ * Unit
+ * State (undefined, offline, not_loaded, stopped, playing,
+ paused, disconnected [when server dies])
+ * Name of Clip
+ * Position in clip
+ * Speed * 1000
+ * Frames per second
+ * Start of clip (in point)
+ * End of clip (out point)
+ * Length of clip
+ * Read ahead clip
+ * Read ahead position
+ * Read ahead clip in
+ * Read ahead clip out
+ * Read ahead clip length
+ * Seekable flag
+ * Playlist generation
+ * Clip index
+
+ Again, valerie provides a high level means for obtaining this via the
+ valerie_unit_status function and valerie_status structures:
+
+ valerie_status_t status;
+ valerie_error_code error = valerie_unit_status( dv, 0, &status );
+ if ( error == valerie_ok )
+ {
+ switch( status.status )
+ {
+ case unit_offline:
+ printf( "offline " );
+ break;
+ case unit_undefined:
+ printf( "undefined " );
+ break;
+ case unit_not_loaded:
+ printf( "unloaded " );
+ break;
+ case unit_stopped:
+ printf( "stopped " );
+ break;
+ case unit_playing:
+ printf( "playing " );
+ break;
+ default:
+ printf( "unknown " );
+ break;
+ }
+
+ printf( "%06lld %06lld %06lld %s\n", status.in,
+ status.position,
+ status.out,
+ status.clip );
+ }
+ else
+ {
+ fprintf( stderr, "Unit status failed: %s\n",
+ valerie_error_description( error ) );
+ }
+
+ The second approach for obtaining a units status is via automatic
+ notification.
+
+ This is done via the valerie_notifier API. To obtain the notifier from the
+ high level API, you can use:
+
+ valerie_notifier notifier = valerie_get_notifier( dv );
+
+ To obtain the last status associated to a unit, you can use:
+
+ int unit = 1;
+ valerie_status_t status;
+ valerie_notifier_get( notifier, &status, unit );
+
+ To wait for the next status from any unit, you can use:
+
+ valerie_notifier_wait( notifier, &status );
+
+ If you wish to trigger the action associated to your applications wait
+ handling of a particular unit, you can use:
+
+ valerie_notifier_get( notifier, &status, unit );
+ valerie_notifier_put( notifier, &status );
+
+ See Examples below for details on this.
+
+ The complete list of fields in the status structure are:
+
+ int unit;
+ unit_status status;
+ char clip[ 2048 ];
+ int64_t position;
+ int speed;
+ double fps;
+ int64_t in;
+ int64_t out;
+ int64_t length;
+ char tail_clip[ 2048 ];
+ int64_t tail_position;
+ int64_t tail_in;
+ int64_t tail_out;
+ int64_t tail_length;
+ int seekable;
+ int generation;
+ int clip_index;
+
+ You will always receive a status record for every frame output.
+
+ The read ahead information is provided for client side queuing. Client side
+ queuing assumes that uset eof=pause is applied to the unit. A client can
+ detect when the previously scheduled clip is played out by using the read
+ ahead information and schedule the next clip. While this mode of operation
+ is still supported, it is recommended that new clients use the server side
+ queuing mechanism which is described in the following section.
+
+
+2.8. Server Side Queueing APIs
+------------------------------
+
+ This section describes the APIs available to provide server side queueing.
+
+ The concept is that each unit maintains its own playlist, containing multiple
+ clips. Associated to the playlist is a generation number which is incremented
+ on each modification to the playlist. The current playlist generation is
+ provided in the status record in order for a client to know when to refresh
+ its presentation of the list. The status record also indicates which clip is
+ currently active.
+
+ Actions that can be carried out on the playlist are summarised as:
+
+ * list - list all the clips and associated in/out points and size
+ * loading a clip - a load will wipe the current list and replace it with the
+ specified clip
+ * appending a clip - append will always place the specified clip at the end
+ of the playlist
+ * inserting a clip - insert will place a new clip at the specified position
+ in the playlist
+ * moving a clip - move will allow clips can be moved in the playlist
+ * removing a clip - remove will remove the specified clip from the playlist
+ * clean - clean will remove all but the playing clip from the playlist
+
+ Additionally, the following existing actions are clip aware:
+
+ * goto allows you to move the current play position to a specific clip position
+ * set in/out points allows you to modify clip in and out points
+
+ Backward compatability has been maintained by the addition of a clip-aware
+ family of APIs which have the naming convention of valerie_unit_clip_*.
+
+ These are listed in Appendix B.
+
+ The following shows an example of obtaining the clips queued on unit 0:
+
+ valerie_list list = valerie_list_init( dv, 0 );
+ valerie_list_entry_t entry;
+ int index;
+
+ printf( "Generation = %d\n", list->generation );
+ for ( index = 0; index < valerie_list_count( list ); index ++ )
+ {
+ valerie_list_get( list, index, &entry );
+ printf( "%d %s %d %d %d %d\n",
+ entry.clip,
+ entry.full,
+ entry.in,
+ entry.out,
+ entry.max,
+ entry.size );
+ }
+ valerie_list_close( list );
+
+ To load a clip on unit 0:
+
+ valerie_unit_load( dv, 0, "/path/clip.dv" );
+
+ To append a clip on unit 0:
+
+ valerie_unit_append( dv, 0, "/path/clip.dv", -1, -1 );
+
+ Note that the last two arguments specify the in and out points of the clip
+ with -1 denoting dfaults of the entirety of the file.
+
+ To insert a clip at position 0 on unit 0, we can use the following:
+
+ valerie_unit_clip_insert( dv, 0, clip_absolute, 0, "/path/clip.dv", -1, -1 );
+
+ The 3rd and 4th arguments here are common to all the valerie_unit_clip functions.
+ They take the form of either [clip_absolute, n] to indicate an absolute clip
+ index, or [clip_relative, n] to indicate a clip index relative to the
+ currently playing clip.
+
+ So, to insert a clip immediately before the currently playing clip, we can
+ use:
+
+ valerie_unit_clip_insert( dv, 0, clip_relative, -1, "/path/clip.dv", -1, -1 );
+
+ To move the current clip to the next position in the list:
+
+ valerie_unit_clip_move( dv, 0, clip_relative, 0, clip_relative, 1 );
+
+ To remove a specific clip:
+
+ valerie_unit_clip_remove( dv, 0, clip_absolute, index );
+
+ To remove all but the currently playing clip:
+
+ valerie_unit_clean( dv, 0 );
+
+ To goto the first frame in the first clip, you can use:
+
+ valerie_unit_clip_goto( dv, 0, clip_absolute, 0, 0 );
+
+ To set the in and out points on the current clip:
+
+ valerie_unit_clip_set_in( dv, 0, clip_relative, 0, 0 );
+ valerie_unit_clip_set_out( dv, 0, clip_relative, 0, 1000 );
+
+ A more complete example of use of the server side can queuing can be found
+ at:
+
+ http://users.pandora.be/acp/rugen
+
+ The demo client provided with valerie is used for retaining backward
+ compatability with the client side queuing API.
+
+
+2.9. Accessing the Low Level Parser Directly
+--------------------------------------------
+
+ The low level parser and its associated structures can be accessed directly
+ from the high level API, but is very occasionally actually needed.
+
+ The methods are provided via a pair of high level methods:
+
+ valerie_error_code error = valerie_execute( dv, 1024, "USTA U%d", unit );
+ valerie_response response = valerie_get_last_response( dv );
+ int index = 0;
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ printf( "%d: %s\n", index, valerie_response_get_line( response,index ) );
+
+ More details on the valerie_response structure can be found in section 3 of this
+ document.
+
+
+2.10. Cleaning up
+-----------------
+
+ Before the valerie and parser go out of scope, you need to run:
+
+ valerie_close( dv );
+ valerie_parser_close( parser );
+
+ Note that you should close all valerie instances before closing the parser.
+
+
+2.11. Examples
+--------------
+
+ Please refer to albino and humperdink source for examples provided with
+ the project. Additional examples can be found via google with gdv1394 and
+ poldo.
+
+
+3. The Low Level Parser API
+---------------------------
+
+ The low level parser API provides a very simple mechanism for constructing
+ commands and receiving responses.
+
+ As described in section 2, a parser is constructed as local or remote and
+ this is sufficient for constructing the low level parser.
+
+
+3.1. Executing a Command
+------------------------
+
+ All commands can be executed via the single variable argument function
+ valerie_parser_executef and this function returns a valerie_response, ie:
+
+ valerie_response response = valerie_parser_executef( parser, "CLS \"%s\"", dir );
+
+ Note that no carriage return/line feed is required (adding this is
+ erroneous).
+
+ It is the receiver of the response who is responsible for closing it.
+
+ valerie_response_close( response );
+
+
+3.2. Interpreting valerie_response
+-----------------------------
+
+ The response received can be NULL, but it is safe to call:
+
+ int error = valerie_response_get_error_code( response );
+
+ which will return:
+
+ * -1 if response is NULL,
+ * -2 if there is no content to the response,
+ * 0 if the responses first line does not correspond to a valid DVCP response
+ * or the DVCP protocol error code returned on the first line of the response
+
+ A simple use of a valerie_response structure is as follows:
+
+ valerie_response response = valerie_parser_executef( parser, "CLS \"%s\"", dir );
+ int error = valerie_response_get_error_code( response );
+ if ( error >= 0 )
+ {
+ int index = 0;
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ printf( "%3d: %s\n", index, valerie_response_get_line( response, index ) );
+ }
+ else
+ {
+ /* interpret error */
+ }
+ valerie_response_close( response );
+
+ Note that it is safe to call valerie_response_close regardless of the error
+ condition indicated.
+
+
+3.3. Accessing Unit Status
+--------------------------
+
+ As with the high level parser, there are two alternatives to obtain unit
+ status information - either via the USTA DVCP command or via the
+ valerie1394_notifier.
+
+ The latter is the recommended way for any applications which wish to extract
+ meaningful information from the status while avoiding the requirement to
+ duplicate the parsing process in a specific client.
+
+ The notifier can be obtained by:
+
+ valerie_notifier notifier = valerie_parser_get_notifier( parser );
+
+ The use of the notifier with the low level parser is identical to that
+ dictated in Section 2 - to obtain the last status associated to a unit,
+ you can use:
+
+ int unit = 1;
+ valerie_status_t status;
+ valerie_notifier_get( notifier, &status, unit );
+
+ To wait for the next status from any unit, you can use:
+
+ valerie_notifier_wait( notifier, &status );
+
+
+APPENDIX A - COMPILATION AND LINKING
+------------------------------------
+
+ Compilation flags are:
+
+ -I <prefix>/include
+
+ where prefix defaults to /usr/local.
+
+ Linking flags for a client are:
+
+ -L <prefix>/lib/ -lvalerie
+
+ Or for a local parser:
+
+ -L <prefix>/lib/ -lmiracle
+
+ Note that you never need both libs.
+
+
+APPENDIX B - COMPLETE HIGH LEVEL PARSER WRAPPER API LISTING
+-----------------------------------------------------------
+
+ valerie valerie_init( valerie_parser );
+
+ valerie_error_code valerie_connect( valerie );
+
+ valerie_error_code valerie_set( valerie, char *, char * );
+ valerie_error_code valerie_get( valerie, char *, char *, int );
+
+ valerie_error_code valerie_unit_add( valerie, char * );
+ valerie_error_code valerie_unit_load( valerie, int, char * );
+ valerie_error_code valerie_unit_load_clipped( valerie,int,char *,long,long );
+ valerie_error_code valerie_unit_load_back( valerie, int, char * );
+ valerie_error_code valerie_unit_load_back_clipped(valerie,int,char *,long,long)
+ valerie_error_code valerie_unit_play( valerie, int );
+ valerie_error_code valerie_unit_play_at_speed( valerie, int, int );
+ valerie_error_code valerie_unit_stop( valerie, int );
+ valerie_error_code valerie_unit_pause( valerie, int );
+ valerie_error_code valerie_unit_rewind( valerie, int );
+ valerie_error_code valerie_unit_fast_forward( valerie, int );
+ valerie_error_code valerie_unit_step( valerie, int, int );
+ valerie_error_code valerie_unit_goto( valerie, int, int );
+ valerie_error_code valerie_unit_set_in( valerie, int, int );
+ valerie_error_code valerie_unit_set_out( valerie, int, int );
+ valerie_error_code valerie_unit_clear_in( valerie, int );
+ valerie_error_code valerie_unit_clear_out( valerie, int );
+ valerie_error_code valerie_unit_clear_in_out( valerie, int );
+ valerie_error_code valerie_unit_set( valerie, int, char *, char * );
+ valerie_error_code valerie_unit_get( valerie, int, char * );
+
+ valerie_error_code valerie_unit_status( valerie, int, valerie_status );
+ valerie_notifier valerie_get_notifier( valerie );
+
+ valerie_dir valerie_dir_init( valerie, char * );
+ valerie_error_code valerie_dir_get( valerie_dir, int, valerie_dir_entry );
+ int valerie_dir_count( valerie_dir );
+ void valerie_dir_close( valerie_dir );
+
+ valerie_nodes valerie_nodes_init( valerie );
+ valerie_error_code valerie_nodes_get(valerie_nodes,int,valerie_node_entry);
+ int valerie_nodes_count( valerie_nodes );
+ void valerie_nodes_close( valerie_nodes );
+
+ valerie_units valerie_units_init( valerie );
+ valerie_error_code valerie_units_get(valerie_units,int,valerie_unit_entry);
+ int valerie_units_count( valerie_units );
+ void valerie_units_close( valerie_units );
+
+ valerie_response valerie_get_last_response( valerie );
+
+ valerie_error_code valerie_execute( valerie, size_t, char *, ... );
+
+ void valerie_close( valerie );
+
+ Notifier Functions
+ ------------------
+
+ void valerie_notifier_get( valerie_notifier, valerie_status, int );
+ void valerie_notifier_put( valerie_notifier, valerie_status );
+ int valerie_notifier_wait( valerie_notifier, valerie_status );
+ void valerie_notifier_close( valerie_notifier );
+
+ Server Side Queuing
+ -------------------
+
+ valerie_list valerie_list_init( valerie, int )
+ valerie_error_code valerie_list_get_error_code( valerie_list )
+ valerie_error_code valerie_list_get( valerie_list, int, valerie_list_entry )
+ int valerie_list_count( valerie_list )
+ void valerie_list_close( valerie_list )
+
+ valerie_error_code valerie_unit_clean( valerie dv, int unit )
+ valerie_error_code valerie_unit_append( valerie dv, int unit, char *file, int in, int out )
+ valerie_error_code valerie_unit_remove_current_clip( valerie dv, int unit )
+
+ valerie_error_code valerie_unit_clip_goto( valerie dv, int unit, valerie_clip_offset offset, int clip, int position )
+ valerie_error_code valerie_unit_clip_set_in( valerie dv, int unit, valerie_clip_offset offset, int clip, int in )
+ valerie_error_code valerie_unit_clip_set_out( valerie dv, int unit, valerie_clip_offset offset, int clip, int in )
+ valerie_error_code valerie_unit_clip_move( valerie dv, int unit, valerie_clip_offset offset, int src, valerie_clip_offset offset, int dest )
+ valerie_error_code valerie_unit_clip_remove( valerie dv, int unit, valerie_clip_offset offset, int clip )
+ valerie_error_code valerie_unit_clip_insert( valerie dv, int unit, valerie_clip_offset offset, int clip, char *file, int in, int out )
+
+
+
+APPENDIX C - COMPLETE LOW LEVEL PARSER API LISTING
+--------------------------------------------------
+
+ valerie_response valerie_parser_connect( valerie_parser );
+ valerie_response valerie_parser_execute( valerie_parser, char * );
+ valerie_response valerie_parser_executef( valerie_parser, char *, ... );
+ valerie_response valerie_parser_run( valerie_parser, char * );
+ valerie_notifier valerie_parser_get_notifier( valerie_parser );
+ void valerie_parser_close( valerie_parser );
+
+ valerie_response valerie_response_init( );
+ valerie_response valerie_response_clone( valerie_response );
+ int valerie_response_get_error_code( valerie_response );
+ char *valerie_response_get_error_string( valerie_response );
+ char *valerie_response_get_line( valerie_response, int );
+ int valerie_response_count( valerie_response );
+ void valerie_response_set_error( valerie_response, int, char * );
+ int valerie_response_printf( valerie_response, size_t, char *, ... );
+ int valerie_response_write( valerie_response, char *, int );
+ void valerie_response_close( valerie_response );
+
+
+APPENDIX D - REFERENCES
+-----------------------
+
+ (1) doc/dvcp.txt - DVCP protocol
+ (2) doc/testing.txt - Test procedures
--- /dev/null
+Westley Documentation
+
+Copyright (C) 2004 Ushodaya Enterprised Limited
+Authors: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-03-20
+
+
+WESTLEY
+-------
+
+Preamble:
+
+ Westley is the MLT projects XML serialisation/deserialisation format -
+ as such, it closely mirrors the internal structure of the MLT API.
+
+ If you just want to go straight to the DTD, then see
+ mlt/src/modules/westley/westley.dtd, which gets installed at
+ $(prefix)/share/mlt/modules/westley.dtd. Currently, the westley parser is
+ non-validating.
+
+
+Introduction:
+
+ A westley document is essentially a list of 'producers' - a producer is
+ an mlt object which generates mlt frames (images and associated audio
+ samples).
+
+ There are 3 types of producer:
+
+ * Basic Producers - these are typically file or device oriented feeds;
+ * Playlists - these are arrangements of multiple producers;
+ * Multitracks - these are the fx encapsulators.
+
+ In the mlt model, producers are created and attached to 'consumers' -
+ consumers are software playback components (such as SDL), or wrappers for
+ hardware drivers (such as bluefish) or even the westley serialising
+ consumer itself (the latter doesn't receive frames - it merely
+ interrogates the connected producer for its configuration).
+
+ Although westley was defined as a serialisation mechanism for instantiated
+ MLT components, this document will concentrate on the hand authoring of
+ westley documents.
+
+
+Rules:
+
+ As shall become apparent through the remainder of this document, the basic
+ tenet of westley authoring is to organise the document in the following
+ manner:
+
+ 1) create producer elements for each unique media clip in the project;
+ 2) create playlists for each track;
+ 3) create a multitrack and specify filters and transitions;
+ 4) adding global filters.
+
+ While other uses of westley exist, the approach taken here is to maximise
+ efficiency for complex projects.
+
+
+Basic Producers:
+
+ The simplest westley document is:
+
+ <westley>
+ <producer id="producer0">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ </westley>
+
+ The westley wrapping is of course superfluous here - loading this document
+ with MLT is identical to loading the clip directly.
+
+ Of course, you can specify additional properties. For example, consider an
+ MPEG file with multiple soundtracks - you could define a westley document to
+ ensure that the second audio track is loaded:
+
+ <westley>
+ <producer id="producer0">
+ <property name="resource">clip1.mpeg</property>
+ <property name="audio_track">1</property>
+ </producer>
+ </westley>
+
+ NB: This relies on the mpeg being handled by the avformat producer, rather
+ than the mcmpeg one. See services.txt for more details.
+
+ A more useful example comes with the pango producer for a text producer.
+
+ TODO: pango example...
+
+ Notes:
+
+ 1) It is better not to specify in/out points when defining basic producers
+ as these can be specified in the playlists. The reasoning is that in/out
+ restricts the amount of the clip available, and could lead to the same clip
+ being loaded multiple times if you need different regions of the clip
+ elsewhere;
+ 2) A westley can be specified as a resource, so westleys can naturally
+ encapsulate other westleys.
+
+
+Playlists:
+
+ Playlists provide a 'collection' structure for producers. These can be used
+ to define 'tracks' in the multitrack approach, or simple playlists for
+ sequential, single track playout.
+
+ As an example, the following defines two basic producers and a playlist with 3
+ items:
+
+ <westley>
+ <producer id="producer0">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ <producer id="producer1">
+ <property name="resource">clip2.dv</property>
+ </producer>
+ <playlist id="playlist0">
+ <entry producer="producer0" in="0" out="2999"/>
+ <entry producer="producer1" in="0" out="999"/>
+ <entry producer="producer0" in="3000" out="6999"/>
+ </playlist>
+ </westley>
+
+ Here we see how the playlist defines the in/out points of the basic
+ producers.
+
+ Notes:
+
+ 1) All in/out points are absolute frame positions relative to the producer
+ being appended to the playlist;
+ 2) Westley documents are currently authored for a specific normalisation;
+ 3) The last 'producer' in the document is the default for play out;
+ 4) Playlists can reference the same producer multiple times. In/out regions
+ do not need to be contiguous - duplication and skipping is acceptable.
+
+
+Interlude - Introducing Multitracks:
+
+ So far we've defined basic producers and playlists/tracks - the tractor is
+ the element that allows us to arrange our tracks and specify filters and
+ transitions. Similarly to a playlist, a tractor is a container.
+
+ Note that MLT doesn't see a filter or a transition as a producer in the
+ normal sense - filters and transitions are passive when it comes to seeking.
+ Internally, seeks are carried out on the producers. This is an important
+ point - MLT does not follow a traditional graph oriented model.
+
+ Visualising an MLT tractor and it's interaction with the consumer will
+ assist here:
+
+ +----------------------------------------------+
+ |tractor |
+ | +----------+ +-+ +-+ +-+ +-+ |
+ | |multitrack| |f| |f| |t| |t| |
+ | | +------+ | |i| |i| |r| |r| |
+ | | |track0|-|--->|l|- ->|l|- ->|a|--->|a|\ |
+ | | +------+ | |t| |t| |n| |n| \ |
+ | | | |e| |e| |s| |s| \ |
+ | | +------+ | |r| |r| |i| |i| \ | +--------+
+ | | |track1|-|- ->|0|--->|1|--->|t|--->|t|-----|--->|consumer|
+ | | +------+ | | | | | |i| |i| / | +--------+
+ | | | | | | | |o| |o| / | ^
+ | | +------+ | | | | | |n| |n| / | |
+ | | |track2|-|- ->| |- ->| |--->|0|- ->|1|/ | |
+ | | +------+ | | | | | | | | | | |
+ | +----------+ +-+ +-+ +-+ +-+ | |
+ +----------------------------------------------+ |
+ ^ |
+ | |
+ +-----------+ |
+ |APPLICATION|--------------------------------------------+
+ +-----------+
+
+ Internally, all frames from all tracks pass through all the filters and
+ transitions - these are told which tracks to deal and which regions of the
+ tracks to work on.
+
+ Note that the application communicates with the producer - it can alter
+ playback speed, position, or even which producer is connected to which
+ consumer.
+
+ The consumer receives the first non-blank frame (see below). It has no say
+ in the order in which gets them (the sdl consumer when used with inigo might
+ appear to be an exception - it isn't - it simply has a route back to the
+ application to allow the application to interpret key presses).
+
+
+Tractors:
+
+ To create a multitrack westley, we can use two playlists and introduce a
+ tractor. For the purposes of demonstration, I'll add a filter here too:
+
+ <westley>
+ <producer id="producer0">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ <producer id="producer1">
+ <property name="resource">clip2.dv</property>
+ </producer>
+ <playlist id="playlist0">
+ <entry producer="producer0" in="0" out="2999"/>
+ <blank length="1000"/>
+ <entry producer="producer0" in="3000" out="6999"/>
+ <playlist id="playlist1">
+ <blank length="3000"/>
+ <entry producer="producer1" in="0" out="999"/>
+ </playlist>
+ <tractor id="tractor0">
+ <multitrack>
+ <track producer="playlist0"/>
+ <track producer="playlist1"/>
+ </multitrack>
+ <filter>
+ <property name="track">0</property>
+ <property name="mlt_service">greyscale</property>
+ </filter>
+ </tractor>
+ </westley>
+
+ Here we see that blank frames are inserted into the first playlist and a
+ blank is provided at the beginning of the second - this can be visualised in
+ the traditional timeline widget as follows:
+
+ +-------+ +-------------+
+ |a | |a |
+ +-------+---+-------------+
+ |b |
+ +---+
+
+ Adding the filter on the top track, gives us:
+
+ +-------+ +-------------+
+ |a | |a |
+ +-------+---+-------------+
+ |greyscale |
+ --------+---+-------------+
+ |b |
+ +---+
+
+ Note that it's only applied to the visible parts of the top track.
+
+ The requirement to apply a filter to the output, as opposed to a specific
+ track leads us to the final item in the Rules section above. As an example,
+ let's assume we wish to watermark all output, then we could use the
+ following:
+
+ <westley>
+ <producer id="producer0">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ <producer id="producer1">
+ <property name="resource">clip2.dv</property>
+ </producer>
+ <playlist id="playlist0">
+ <entry producer="producer0" in="0" out="2999"/>
+ <blank length="1000"/>
+ <entry producer="producer0" in="3000" out="6999"/>
+ <playlist id="playlist1">
+ <blank length="3000"/>
+ <entry producer="producer1" in="0" out="999"/>
+ </playlist>
+ <tractor id="tractor0">
+ <multitrack>
+ <track producer="playlist0"/>
+ <track producer="playlist1"/>
+ </multitrack>
+ <filter>
+ <property name="track">0</property>
+ <property name="mlt_service">greyscale</property>
+ </filter>
+ </tractor>
+ <tractor id="tractor1">
+ <multitrack>
+ <track producer="tractor0"/>
+ </multitrack>
+ <filter>
+ <property name="mlt_service">watermark</property>
+ <property name="resource">watermark1.png</property>
+ </filter>
+ </tractor>
+ </westley>
+
+ Here we employ another tractor and we define a single track (being the
+ tractor we previously defined) and apply a watermarking filter there.
+
+ This is simply provided as an example - the watermarking functionality could
+ be better handled at the playout stage itself (ie: as a filter automatically
+ placed between all producers and the consumer).
+
+ Tracks act like "layers" in an image processing program like the GIMP. The
+ bottom-most track takes highest priority and higher layers are overlays
+ and do not appear unless there are gaps in the lower layers or unless
+ a transition is applied that merges the tracks on the specifed region.
+ Practically speaking, for A/B video editing it does not mean too much,
+ and it will work as expected; however, as a general rule apply any CGI
+ (graphic overlays with pixbuf or titles with pango) on tracks higher than
+ your video tracks. Also, this means that any audio-only tracks that are
+ lower than your video tracks will play rather than the audio from the video
+ clip. Remember, nothing is affected like mixing or compositing until one
+ applies a transition or appropriate filter.
+
+ <westley>
+ <producer id="producer0">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ <playlist id="playlist0">
+ <entry producer="producer0"/>
+ </playlist>
+ <producer id="producer1">
+ <property name="resource">clip2.mpeg</property>
+ </producer>
+ <playlist id="playlist1">
+ <blank length="50"/>
+ <entry producer="producer1"/>
+ </playlist>
+ <tractor id="tractor0" in="0" out="315">
+ <multitrack id="multitrack0">
+ <track producer="playlist0"/>
+ <track producer="playlist1"/>
+ </multitrack>
+ <transition id="transition0" in="50" out="74">
+ <property name="a_track">0</property>
+ <property name="b_track">1</property>
+ <property name="mlt_service">luma</property>
+ </transition>
+ <transition id="transition1" in="50" out="74">
+ <property name="a_track">0</property>
+ <property name="b_track">1</property>
+ <property name="mlt_service">mix</property>
+ <property name="start">0.0</property>
+ <property name="end">1.0</property>
+ </transition>
+ </tractor>
+ </westley>
+
+ A "luma" transition is a video wipe processor that takes a greyscale bitmap
+ for the wipe definition. When one does not specify a bitmap, luma performs
+ a dissolve. The "mix" transition does an audio mix, but it interpolates
+ between the gain scaling factors between the start and end properties -
+ in this example, from 0.0 (none of track B) to 1.0 (all of track B).
+ Because the bottom track starts out with a gap specified using the <blank>
+ element, the upper track appears during the blank segment. See the demos and
+ services.txt to get an idea of the capabilities of the included transitions.
+
+Flexibility:
+
+ The information presented above is considered the MLT Westley "normal"
+ form. This is the output generated by the westley consumer, for example,
+ when used with inigo. It is the output generated when you use the
+ "Westley to File" consumer in the demo script, which beginners will find
+ most useful for learning to use westley XML. This section describes
+ alternative forms the westley producer accepts.
+
+ First of all, the normal form is more of a linear format with producers
+ and playlists defined prior to their usage in a multitrack. Westley
+ also accepts a hierarchical format with producers as children of tracks
+ or playlist entries and with playlists as children of tracks:
+
+ <westley>
+ <tractor>
+ <multitrack>
+ <track>
+ <playlist>
+ <entry>
+ <producer>
+ <property name="resource">clip1.dv</property>
+ </producer>
+ </entry>
+ </playlist>
+ </track>
+ </multitrack>
+ </tractor>
+ </westley>
+
+ Obviously, this example is meant to demonstrate hierarchy and not effective
+ use of playlist or multitrack!
+
+ Secondly, as part of error handling, westley is forgiving if you fail to
+ supply <tractor>, <track>, and <entry> where one can be understood. This
+ affords an abbreviated syntax that is less verbose and perhaps less
+ intimidating for a human to read and understand. One can simplify the
+ above example as:
+
+ <westley>
+ <multitrack>
+ <playlist>
+ <producer>
+ <property name="resource">clip1.dv</property>
+ </producer>
+ </playlist>
+ </multitrack>
+ </westley>
+
+ Yes, filters and transitions can be added to the above example after the
+ closing multitrack tag (</multitrack>) because it is still enclosed within
+ the westley body tags.
+
+ If you specify in and out on a producer and it has been enclosed within
+ an <entry> or <playlist>, then the edit points apply to the playlist
+ entry and not to the producer itself. This facilitates re-use of media:
+
+ <playlist>
+ <producer id="clip1" in="25" out="78">
+ <property name="resource">clip1.dv</property>
+ </producer>
+ <entry producer="clip1" in="119" out="347"/>
+ </playlist>
+
+ In the above example, the producer attribute of the entry element is
+ a reference to the preceding producer. All references must follow the
+ definition. The edit points supplied on the producer above will not affect
+ the entry that references it below because westley knows the clip is a
+ playlist entry and optimises this situation. The advantage is that one
+ does not need to determine every clip to be included ahead of time
+ and specify them outside the context of the mutlitrack timeline.
+
+ This form of authoring will be easier for many to visualise as a non-linear
+ editor's timeline. Here is a more complex example:
+
+ <westley>
+ <multitrack>
+ <playlist>
+ <producer id="foo" in="100" out="149">
+ <property name="resource">clip2.mpeg</property>
+ </producer>
+ <blank length="25"/>
+ <entry producer="foo" in="10" out="59"/>
+ </playlist>
+ <playlist>
+ <blank length="25"/>
+ <producer id="bar" in="100" out="199">
+ <property name="resource">clip3.mpeg</property>
+ </producer>
+ <entry out="99" producer="bar"/>
+ </playlist>
+ </multitrack>
+ <filter mlt_service="greyscale" track="0"/>
+ <transition mlt_service="luma" in="25" out="49" a_track="0" b_track="1"/>
+ <transition mlt_service="luma" in="75" out="99" a_track="0" b_track="1">
+ <property name="reverse" value="1"/>
+ </transition>
+ </westley>
+
+ Did you notice something different in the last example? Properties can be
+ expressed using XML attributes on the element as well. However, only
+ non-service-specific properties are supported in this way. For example,
+ "mlt_service" is available to any producer, filter, or transition. However,
+ "resource" is actually service-specific. Notice the syntax of the last
+ property, on the last transition. Westley accepts property values using
+ the "value" attribute as well as using element text.
+
+ We have seen a few different ways of expressing property values. There are
+ a couple more for properties that can accept XML data. For example, the
+ GDK pixbuf producer with librsvg can handle embedded SVG, and the Pango
+ producer can handle embedded Pango markup. You can enclose the embedded XML
+ using a CDATA section:
+
+ <property name="resource"><![CDATA[ <svg>...</svg> ]]></property>
+
+ Please ensure the opening CDATA tag immediately follows the opening
+ property tag and that the section closing tag immediately precedes the
+ closing property tag.
+
+ However, westley can also accept inline embedded XML:
+
+ <property name="resource">
+ <svg>
+ </svg>
+ </property>
+
+ Currently, there is no namespace handling so a conflict will occur only on
+ any embedded XML that contains an element named "property" because
+ westley collects embedded XML until it reaches a closing property tag.
+
+
+Entities and Parameterisation:
+
+ The westley producer parser supports XML entities. An example:
+
+ <?xml version="1.0"?>
+ <!DOCTYPE westley [
+ <!ENTITY msg "Hello world!">
+ ]>
+ <westley>
+ <producer id="producer0">
+ <property name="mlt_service">pango</property>
+ <property name="text">&msg;</property>
+ </producer>
+ </westley>
+
+ If you are embedding another XML document into a property value not using
+ a CNODE section, then any DOCTYPE section must be relocated before any of
+ the xml elements to be well-formed. See demo/svg.westley for an example.
+
+ Entities can be used to parameterise westley! Using the above example, the
+ entity declared serves as the default value for &msg;. The entity content
+ can be overridden from the resource property supplied to the westley
+ producer. The syntax is the familiar, url-encoded query string used with
+ HTTP, e.g.: file?name=value&name=value...
+
+ There are a couple of rules of usage. The Miracle LOAD command and inigo
+ command line tool require you to preface the URL with "westley:" because
+ the query string destroys the filename extension matching peformed by
+ Fezzik. Also, inigo looks for '=' to tokenise property settings. Therefore,
+ one uses ':' between name and value instead of '='. Finally, since inigo
+ is run from the shell, one must enclose the URL within single quotes to
+ prevent shell filename expansion, or similar.
+
+ Needless to say, the ability to parameterise westley XML compositions is
+ an extremely powerful tool. An example for you to play with is available in
+ demo/entity.westley. Try overriding the name from inigo:
+ inigo 'westley:entity.westley?name:Charlie'
+
+ Technically, the entity declaration is not needed in the head of the XML
+ document if you always supply the parameter. However, you run the risk
+ of unpredictable behviour without one. Therefore, it is safest and a best
+ practice to always supply an entity declaration. It is improves the
+ readability as one does not need to search for the entity references to
+ see what parameters are available.
+
+
+Tips and Technique:
+
+ If one finds the above hierarchical, abbreviated format intuitive,
+ start with a simple template and fill and extend as needed:
+ <westley>
+ <multitrack>
+ <playlist>
+ </playlist>
+ ...add a playlist for each track...
+ </multitrack>
+ ...add filters and transitions...
+ </westley>
+
+ By using a playlist for each track, it is easier to iteratively add new
+ clips and blank regions as you develop the project. You will not have to
+ use <track> or later add <playlist> when necessary.
+
+ A more advanced template that allows sequencing multitracks is:
+ <playlist>
+ <entry>
+ <multitrack>
+ <playlist>
+ </playlist>
+ ...add a playlist for each track...
+ </multitrack>
+ ...add filters and transitions...
+ </entry>
+
+ <entry>
+ <multitrack>
+ <playlist>
+ </playlist>
+ ...add a playlist for each track...
+ </multitrack>
+ ...add filters and transitions...
+ </entry>
+ </playlist>
+
+ If you end up making a collection of templates for various situations, then
+ consider using XML Entities to make the template more effective by moving
+ anything that should parameterised into an entity.
+
+ If you want to have a silent, black background for audio and video fades,
+ then make the top track simply <producer mlt_service="colour"/>. Then,
+ use composite and volume effects. See the "Fade from/to black/silence"
+ demo for an example (demo/mlt_fade_black).
+
+ If you apply the reverse=1 property to a transition like "luma," then
+ be careful because it also inherently swaps the roles of A and B tracks.
+ Therefore, you need to might need to swap the a_track and b_track values
+ if it did not turn out the way you expected. See the "Clock in and out"
+ for an example (demo/mlt_clock_in_and_out).
--- /dev/null
+export package=framework
+export field=0
+
+while [ "$1" != "" ]
+do
+ case $1 in
+ --help ) field=0 ;;
+ --version ) field=-1 ;;
+ --prefix ) field=-2 ;;
+ --prefix=* ) prefix="${i#--prefix=}" ;;
+ --cflags ) field=2 ;;
+ --libs ) field=3 ;;
+ --list ) field=1; package="" ;;
+ * ) package=$1 ;;
+ esac
+ shift
+done
+
+if [ "$field" = "0" ]
+then echo "Usage: mlt-config [ --version ] | [ --prefix=dir ] [ [ package ] [ --cflags ] [ --libs ] ]"
+elif [ "$field" = "-1" ]
+then echo $version
+elif [ "$field" = "-2" ]
+then config=`which mlt-config`
+ dir=`dirname $config`
+ dir=`dirname $dir`
+ echo $dir
+elif [ -f "$prefix/share/mlt/packages.dat" ]
+then grep "^$package" $prefix/share/mlt/packages.dat | cut -f $field
+else echo mlt-config cannot find package $package.
+fi
+echo >&2 "mlt-config is deprecated. Please use pkg-config instead."
--- /dev/null
+
+Name: mlt-framework
+Description: MLT multimedia framework
+Version: ${version}
+Requires:
+Libs: -L${libdir} ${libs}
+Cflags: ${cflags}
--- /dev/null
+
+Name: mlt-miracle
+Description: MLT Miracle server API
+Version: ${version}
+Requires:
+Libs: -L${libdir} ${libs}
+Cflags: ${cflags}
--- /dev/null
+
+Name: mlt-valerie
+Description: MLT Valerie client API
+Version: ${version}
+Requires:
+Libs: -L${libdir} ${libs}
+Cflags: ${cflags}
--- /dev/null
+include ../config.mak
+
+all:
+
+depend:
+
+distclean:
+
+clean:
+
+install: all uninstall
+ install -d "$(DESTDIR)$(prefix)/share/mlt/profiles"
+ install -m 644 * "$(DESTDIR)$(prefix)/share/mlt/profiles"
+ rm -f "$(DESTDIR)$(prefix)/share/mlt/profiles/"*~
+ rm -f "$(DESTDIR)$(prefix)/share/mlt/profiles/Makefile"
+
+uninstall:
+ rm -rf "$(DESTDIR)$(prefix)/share/mlt/profiles"
--- /dev/null
+description=ATSC 1080i 50Hz
+frame_rate_num=25
+frame_rate_den=1
+width=1920
+height=1080
+progressive=0
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 1080i 60Hz
+frame_rate_num=30000
+frame_rate_den=1001
+width=1920
+height=1080
+progressive=0
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 1080p 23.98Hz
+frame_rate_num=24000
+frame_rate_den=1001
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 1080p 24Hz
+frame_rate_num=24
+frame_rate_den=1
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 1080p 25Hz
+frame_rate_num=25
+frame_rate_den=1
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 1080p 29.97Hz
+frame_rate_num=30000
+frame_rate_den=1001
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 1080p 30Hz
+frame_rate_num=30
+frame_rate_den=1
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=ATSC 720p 30Hz
+frame_rate_num=30000
+frame_rate_den=1001
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=CIF 15 fps
+frame_rate_num=15
+frame_rate_den=1
+width=352
+height=288
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=CIF NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=352
+height=288
+progressive=1
+sample_aspect_num=10
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=CIF PAL
+frame_rate_num=25
+frame_rate_den=1
+width=352
+height=288
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=CVD NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=352
+height=480
+progressive=0
+sample_aspect_num=20
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=CVD PAL
+frame_rate_num=25
+frame_rate_den=1
+width=352
+height=576
+progressive=0
+sample_aspect_num=59
+sample_aspect_den=27
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=DV NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=720
+height=480
+progressive=0
+sample_aspect_num=8
+sample_aspect_den=9
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=DV NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=720
+height=480
+progressive=0
+sample_aspect_num=32
+sample_aspect_den=27
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=DV PAL
+frame_rate_num=25
+frame_rate_den=1
+width=720
+height=576
+progressive=0
+sample_aspect_num=16
+sample_aspect_den=15
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=DV PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=720
+height=576
+progressive=0
+sample_aspect_num=64
+sample_aspect_den=45
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 1080 25p
+frame_rate_num=25
+frame_rate_den=1
+width=1440
+height=1080
+progressive=1
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 1080 30p
+frame_rate_num=30000
+frame_rate_den=1001
+width=1440
+height=1080
+progressive=1
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 1080 50i
+frame_rate_num=25
+frame_rate_den=1
+width=1440
+height=1080
+progressive=0
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 1080 60i
+frame_rate_num=30000
+frame_rate_den=1001
+width=1440
+height=1080
+progressive=0
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 720 25p
+frame_rate_num=25
+frame_rate_den=1
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 720 30p
+frame_rate_num=30000
+frame_rate_den=1001
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 720 50p
+frame_rate_num=50
+frame_rate_den=1
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=HDV 720 60p
+frame_rate_num=60000
+frame_rate_den=1001
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=QCIF 15fps
+frame_rate_num=15
+frame_rate_den=1
+width=176
+height=144
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=QCIF NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=176
+height=144
+progressive=1
+sample_aspect_num=10
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=QCIF PAL
+frame_rate_num=25
+frame_rate_den=1
+width=176
+height=144
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=QVGA 15fps
+frame_rate_num=15
+frame_rate_den=1
+width=320
+height=240
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=Quarter Square NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=320
+height=240
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=Quarter Square NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=426
+height=240
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=Quarter Square PAL
+frame_rate_num=25
+frame_rate_den=1
+width=384
+height=288
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=Quarter Square PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=512
+height=288
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=Square NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=640
+height=480
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=Square NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=854
+height=480
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=Square PAL
+frame_rate_num=25
+frame_rate_den=1
+width=768
+height=576
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=Square PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=1024
+height=576
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=SVCD NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=480
+height=480
+progressive=0
+sample_aspect_num=15
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=SVCD NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=480
+height=480
+progressive=0
+sample_aspect_num=20
+sample_aspect_den=11
+display_aspect_num=16
+display_aspect_den=9
--- /dev/null
+description=SVCD PAL
+frame_rate_num=25
+frame_rate_den=1
+width=480
+height=576
+progressive=0
+sample_aspect_num=59
+sample_aspect_den=36
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=SVCD PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=480
+height=576
+progressive=0
+sample_aspect_num=59
+sample_aspect_den=27
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=VCD NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=352
+height=240
+progressive=1
+sample_aspect_num=10
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+description=VCD PAL
+frame_rate_num=25
+frame_rate_den=1
+width=352
+height=288
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
--- /dev/null
+
+# Environment variable settings to allow execution without install
+
+export MLT_REPOSITORY=`pwd`/src/modules
+export MLT_DATA=`pwd`/src/modules
+export MLT_PROFILES_PATH=`pwd`/profiles
+
+export LD_LIBRARY_PATH=\
+`pwd`/src/framework:\
+`pwd`/src/valerie:\
+`pwd`/src/miracle:\
+`pwd`/src/modules/bluefish:\
+`pwd`/../BlueLinuxDriver/install/lib:\
+`pwd`/../mpeg_sdk_release/bin:\
+`pwd`/../dvcpro_sdk_release/lib:\
+`pwd`/../sr_sdk_release:\
+$LD_LIBRARY_PATH
+
+[ $(uname -s) = Darwin ] && export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
+
+export PATH=\
+`pwd`/src/albino:\
+`pwd`/src/inigo:\
+`pwd`/src/humperdink:\
+`pwd`/src/miracle:\
+$PATH
--- /dev/null
+
+# Environment variable settings to allow execution without install
+
+export LD_LIBRARY_PATH=\
+`pwd`/../mpeg_sdk_release/bin:\
+`pwd`/../dvcpro_sdk_release/lib:\
+`pwd`/../sr_sdk_release/lib:\
+$LD_LIBRARY_PATH
+
--- /dev/null
+include ../../config.mak
+
+TARGET = albino
+
+OBJS = albino.o
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../miracle -lmiracle
+LDFLAGS += -L../valerie -lvalerie
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -d "$(DESTDIR)$(bindir)"
+ install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * albino.c -- Local Valerie/Miracle Test Utility
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+
+#ifdef __DARWIN__
+#include <SDL.h>
+#endif
+
+/* Application header files */
+#include <miracle/miracle_local.h>
+#include <valerie/valerie_remote.h>
+#include <valerie/valerie_util.h>
+
+char *prompt( char *command, int length )
+{
+ printf( "> " );
+ return fgets( command, length, stdin );
+}
+
+void report( valerie_response response )
+{
+ int index = 0;
+ if ( response != NULL )
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ printf( "%4d: %s\n", index, valerie_response_get_line( response, index ) );
+}
+
+int main( int argc, char **argv )
+{
+ valerie_parser parser = NULL;
+ valerie_response response = NULL;
+ char temp[ 1024 ];
+ int index = 1;
+
+ if ( argc > 2 && !strcmp( argv[ 1 ], "-s" ) )
+ {
+ printf( "Miracle Client Instance\n" );
+ parser = valerie_parser_init_remote( argv[ 2 ], 5250 );
+ response = valerie_parser_connect( parser );
+ index = 3;
+ }
+ else
+ {
+ printf( "Miracle Standalone Instance\n" );
+ parser = miracle_parser_init_local( );
+ response = valerie_parser_connect( parser );
+ }
+
+ if ( response != NULL )
+ {
+ /* process files on command lines before going into console mode */
+ for ( ; index < argc; index ++ )
+ {
+ valerie_response_close( response );
+ response = valerie_parser_run( parser, argv[ index ] );
+ report( response );
+ }
+
+ while ( response != NULL && prompt( temp, 1024 ) )
+ {
+ valerie_util_trim( valerie_util_chomp( temp ) );
+ if ( !strcasecmp( temp, "BYE" ) )
+ {
+ break;
+ }
+ else if ( strcmp( temp, "" ) )
+ {
+ valerie_response_close( response );
+ response = valerie_parser_execute( parser, temp );
+ report( response );
+ }
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Unable to connect to a Miracle instance.\n" );
+ }
+
+ printf( "\n" );
+ valerie_parser_close( parser );
+
+ return 0;
+}
--- /dev/null
+include ../../config.mak
+
+NAME = libmlt$(LIBSUF)
+TARGET = $(NAME).$(version)
+
+ifneq ($(targetos), Darwin)
+NAME = libmlt$(LIBSUF)
+TARGET = $(NAME).$(version)
+SONAME = $(NAME).$(soversion)
+SHFLAGS += -Wl,-soname,$(SONAME)
+else
+NAME = libmlt$(LIBSUF)
+TARGET = libmlt.$(version)$(LIBSUF)
+SONAME = libmlt.$(soversion)$(LIBSUF)
+SHFLAGS += -install_name $(libdir)/$(SONAME) -current_version $(version) -compatibility_version $(soversion)
+endif
+
+OBJS = mlt_frame.o \
+ mlt_geometry.o \
+ mlt_deque.o \
+ mlt_property.o \
+ mlt_properties.o \
+ mlt_events.o \
+ mlt_parser.o \
+ mlt_service.o \
+ mlt_producer.o \
+ mlt_multitrack.o \
+ mlt_playlist.o \
+ mlt_consumer.o \
+ mlt_filter.o \
+ mlt_transition.o \
+ mlt_field.o \
+ mlt_tractor.o \
+ mlt_factory.o \
+ mlt_repository.o \
+ mlt_pool.o \
+ mlt_tokeniser.o \
+ mlt_profile.o \
+ mlt_log.o \
+ mlt_cache.o
+
+INCS = mlt_consumer.h \
+ mlt_factory.h \
+ mlt_filter.h \
+ mlt.h \
+ mlt_multitrack.h \
+ mlt_pool.h \
+ mlt_properties.h \
+ mlt_events.h \
+ mlt_parser.h \
+ mlt_repository.h \
+ mlt_tractor.h \
+ mlt_types.h \
+ mlt_deque.h \
+ mlt_field.h \
+ mlt_frame.h \
+ mlt_geometry.h \
+ mlt_playlist.h \
+ mlt_producer.h \
+ mlt_property.h \
+ mlt_service.h \
+ mlt_transition.h \
+ mlt_tokeniser.h \
+ mlt_profile.h \
+ mlt_log.h \
+ mlt_cache.h
+
+SRCS := $(OBJS:.o=.c)
+
+CFLAGS += $(RDYNAMIC) -DPREFIX="\"$(prefix)\"" -DLIBDIR="\"$(libdir)\""
+
+LDFLAGS += -lm $(LIBDL) -lpthread
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+ ln -sf $(TARGET) $(NAME)
+ ln -sf $(TARGET) $(SONAME)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET) $(NAME)
+
+install:
+ install -d $(DESTDIR)$(libdir)
+ install -m 755 $(TARGET) $(DESTDIR)$(libdir)
+ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(SONAME)
+ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(NAME)
+ install -d "$(DESTDIR)$(prefix)/include/mlt/framework"
+ install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/framework"
+ install -d "$(DESTDIR)$(prefix)/share/mlt"
+ install -m 644 metaschema.yaml "$(DESTDIR)$(prefix)/share/mlt/"
+
+uninstall:
+ rm -f "$(DESTDIR)$(libdir)/$(TARGET)"
+ rm -f "$(DESTDIR)$(libdir)/$(SONAME)"
+ rm -f "$(DESTDIR)$(libdir)/$(NAME)"
+ rm -rf "$(DESTDIR)$(prefix)/include/mlt/framework"
+ rm "$(DESTDIR)$(prefix)/share/mlt/metaschema.yaml"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+echo "framework -I$prefix/include -I$prefix/include/mlt -D_REENTRANT -L$libdir -lmlt" >> ../../packages.dat
--- /dev/null
+--- # A metadata schema in Kwalify: http://www.kuwata-lab.com/kwalify/
+# Version: 0.1
+type: map
+mapping:
+ "schema_version": # This should match the version comment above
+ type: float
+ required: yes
+ "type": # A service type
+ type: str
+ required: yes
+ enum: [consumer, filter, producer, transition]
+ "identifier": # The same value used to register and create the service
+ type: str
+ required: yes
+ unique: yes
+ "title": # The UI can use this for a field label
+ type: str
+ "copyright": # Who owns the rights to the module and/or service?
+ type: str
+ "version": # The version of the service implementation
+ type: text
+ "license": # The software license for the service implementation
+ type: str
+ "language": # A 2 character ISO 639-1 language code
+ type: str
+ required: yes
+ "url": # A hyperlink to a related website
+ type: str
+ "creator": # The name and/or e-mail address of the original author
+ type: str
+ "contributor": # The name and/or e-mail of all source code contributors
+ type: seq
+ sequence:
+ - type: str
+ "tags": # A set of categories, this might become an enum
+ type: seq
+ sequence:
+ - type: str
+ "description": # A slightly longer description than title
+ type: str
+ "icon": # A graphical representation of the effect
+ type: map
+ mapping:
+ "filename":
+ type: str
+ "content-type":
+ type: str
+ "content-encoding":
+ type: str
+ "content":
+ type: str
+ "notes": # Details about the usage and/or implementation - can be long
+ type: str
+ "bugs": # A list of known problems that users can try to avoid
+ type: seq
+ sequence:
+ - type: str # Can be a sentence or paragraph, preferably not a hyperlink
+ "parameters": # A list of all of the options for the service
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "identifier": # The key that must be used to set the mlt_property
+ type: str
+ required: yes
+ "type": # An mlt_property_type
+ type: str
+ enum:
+ - float
+ - geometry
+ - integer
+ - properties # for passing options to encapsulated services
+ - string
+ - time # currently, mlt_position (frame), soon to be a time value
+ "service-name": # for type: properties, a reference to another service
+ type: str # format: type.service, e.g. transition.composite
+ "title": # A UI can use this for a field label
+ type: str
+ "description": # A UI can use this for a tool tip or what's-this
+ type: str
+ "readonly": # If you set this property, it will be ignored
+ type: bool
+ default: no
+ "required": # Is this property required?
+ type: bool
+ default: no
+ "mutable": # The service will change behavior if this is set after
+ # processing the first frame
+ type: bool
+ default: no
+ "widget": # A hint to the UI about how to let the user set this
+ type: str
+ enum:
+ - checkbox
+ - color
+ - combo
+ - curve
+ - directory
+ - fileopen
+ - filesave
+ - font
+ - knob
+ - listbox
+ - dropdown # aka HTML select or GtkOptionMenu
+ - radio
+ - rectangle # for use with type: geometry
+ - slider
+ - spinner
+ - text
+ - textbox # multi-line
+ - timecode
+ "minimum": # For numeric types, the minimal value
+ type: number
+ "maximum": # For numeric types, the maximal value
+ type: number
+ "default": # The default value to be used in a UI
+ type: scalar # If not specified, the UI might be able to display
+ # this as blank
+ "unit": # A UI can display this as a label after the widget (e.g. %)
+ type: str
+ "scale": # the number of digits after decimal point when type: float
+ type: int
+ "format": # A hint about a custom string encoding, possibly scanf
+ "values": # A list of acceptable string values
+ type: seq # A UI can allow something outside the list with
+ # widget: combo or if "other" is in this sequence
+ sequence:
+ - type: scalar
+
--- /dev/null
+/**
+ * \file mlt.h
+ * \brief header file for lazy client and implementation code :-)
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_H_
+#define _MLT_H_
+
+#define LIBMLT_VERSION_INT ((0<<16)+(3<<8)+9)
+#define LIBMLT_VERSION 0.3.9
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "mlt_factory.h"
+#include "mlt_frame.h"
+#include "mlt_deque.h"
+#include "mlt_multitrack.h"
+#include "mlt_producer.h"
+#include "mlt_transition.h"
+#include "mlt_consumer.h"
+#include "mlt_filter.h"
+#include "mlt_playlist.h"
+#include "mlt_properties.h"
+#include "mlt_field.h"
+#include "mlt_tractor.h"
+#include "mlt_tokeniser.h"
+#include "mlt_parser.h"
+#include "mlt_geometry.h"
+#include "mlt_profile.h"
+#include "mlt_repository.h"
+#include "mlt_log.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_profile.c
+ * \brief least recently used cache
+ * \see mlt_profile_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_types.h"
+#include "mlt_log.h"
+#include "mlt_properties.h"
+#include "mlt_cache.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+
+/** the maximum number of data objects to cache per line */
+#define CACHE_SIZE (10)
+
+/** \brief Cache item class
+ *
+ * A cache item is a structure holding information about a data object including
+ * a reference count that is used to control its lifetime. When you get a
+ * a cache item from the cache, you hold a reference that prevents the data
+ * from being released when the cache is full and something new is added.
+ * When you close the cache item, the reference count is decremented.
+ * The data object is destroyed when all cache items are closed and the cache
+ * releases its reference.
+ */
+
+typedef struct mlt_cache_item_s
+{
+ mlt_cache cache; /**< a reference to the cache to which this belongs */
+ void *object; /**< a parent object to the cache data that uniquely identifies this cached item */
+ void *data; /**< the opaque pointer to the cached data */
+ int size; /**< the size of the cached data */
+ int refcount; /**< a reference counter to control when destructor is called */
+ mlt_destructor destructor; /**< a function to release or destroy the cached data */
+} mlt_cache_item_s;
+
+/** \brief Cache class
+ *
+ * This is a utility class for implementing a Least Recently Used (LRU) cache
+ * of data blobs indexed by the address of some other object (e.g., a service).
+ * Instead of sorting and manipulating linked lists, it tries to be simple and
+ * elegant by copying pointers between two arrays of fixed size to shuffle the
+ * order of elements.
+ *
+ * This class is useful if you have a service that wants to cache something
+ * somewhat large, but will not scale if there are many instances of the service.
+ * Of course, the service will need to know how to recreate the cached element
+ * if it gets flushed from the cache,
+ *
+ * The most obvious examples are the pixbuf and qimage producers that cache their
+ * respective objects representing a picture read from a file. If the picture
+ * is no longer in the cache, it can simply re-read it from file. However, a
+ * picture is often repeated over many frames and makes sense to cache instead
+ * of continually reading, parsing, and decoding. On the other hand, you might
+ * want to load hundreds of pictures as individual producers, which would use
+ * a lot of memory if every picture is held in memory!
+ */
+
+struct mlt_cache_s
+{
+ int count; /**< the number of items currently in the cache */
+ void* *current; /**< pointer to the current array of pointers */
+ void* A[ CACHE_SIZE ];
+ void* B[ CACHE_SIZE ];
+ pthread_mutex_t mutex; /**< a mutex to prevent multi-threaded race conditions */
+ mlt_properties active; /**< a list of cache items some of which may no longer
+ be in \p current but to which there are
+ outstanding references */
+ mlt_properties garbage;/**< a list cache items pending release. A cache item
+ is copied to this list when it is updated but there
+ are outstanding references to the old data object. */
+};
+
+/** Get the data pointer from the cache item.
+ *
+ * \public \memberof mlt_cache_s
+ * \param item a cache item
+ * \param[out] size the number of bytes pointed at, if supplied when putting the data into the cache
+ * \return the data pointer
+ */
+
+void *mlt_cache_item_data( mlt_cache_item item, int *size )
+{
+ if ( size && item )
+ *size = item->size;
+ return item? item->data : NULL;
+}
+
+/** Close a cache item given its parent object pointer.
+ *
+ * \private \memberof mlt_cache_s
+ * \param cache a cache
+ * \param object the object to which the data object belongs
+ * \param data the data object, which might be in the garbage list (optional)
+ */
+
+static void cache_object_close( mlt_cache cache, void *object, void* data )
+{
+ char key[19];
+
+ // Fetch the cache item from the active list by its owner's address
+ sprintf( key, "%p", object );
+ pthread_mutex_lock( &cache->mutex );
+ mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL );
+ if ( item )
+ {
+ mlt_log( NULL, MLT_LOG_DEBUG, "%s: item %p object %p data %p refcount %d\n", __FUNCTION__,
+ item, item->object, item->data, item->refcount );
+ if ( item->destructor && --item->refcount <= 0 )
+ {
+ // Destroy the data object
+ item->destructor( item->data );
+ item->data = NULL;
+ item->destructor = NULL;
+ // Do not dispose of the cache item because it could likely be used
+ // again.
+ }
+ }
+
+ // Fetch the cache item from the garbage collection by its data address
+ if ( data )
+ {
+ sprintf( key, "%p", data );
+ item = mlt_properties_get_data( cache->garbage, key, NULL );
+ if ( item )
+ {
+ mlt_log( NULL, MLT_LOG_DEBUG, "collecting garbage item %p object %p data %p refcount %d\n",
+ item, item->object, item->data, item->refcount );
+ if ( item->destructor && --item->refcount <= 0 )
+ {
+ item->destructor( item->data );
+ item->data = NULL;
+ item->destructor = NULL;
+ // We do not need the garbage-collected cache item
+ mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL );
+ }
+ }
+ }
+ pthread_mutex_unlock( &cache->mutex );
+}
+
+/** Close a cache item.
+ *
+ * Release a reference and call the destructor on the data object when all
+ * references are released.
+ *
+ * \public \memberof mlt_cache_item_s
+ * \param item a cache item
+ */
+
+void mlt_cache_item_close( mlt_cache_item item )
+{
+ if ( item )
+ cache_object_close( item->cache, item->object, item->data );
+}
+
+/** Create a new cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \return a new cache or NULL if there was an error
+ */
+
+mlt_cache mlt_cache_init()
+{
+ mlt_cache result = calloc( 1, sizeof( struct mlt_cache_s ) );
+ if ( result )
+ {
+ result->current = result->A;
+ pthread_mutex_init( &result->mutex, NULL );
+ result->active = mlt_properties_new();
+ result->garbage = mlt_properties_new();
+ }
+ return result;
+}
+
+/** Destroy a cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache the cache to detroy
+ */
+
+void mlt_cache_close( mlt_cache cache )
+{
+ if ( cache )
+ {
+ while ( cache->count-- )
+ {
+ void *object = cache->current[ cache->count ];
+ mlt_log( NULL, MLT_LOG_DEBUG, "%s: %d = %p\n", __FUNCTION__, cache->count, object );
+ cache_object_close( cache, object, NULL );
+ }
+ mlt_properties_close( cache->active );
+ mlt_properties_close( cache->garbage );
+ pthread_mutex_destroy( &cache->mutex );
+ free( cache );
+ }
+}
+
+/** Remove cache entries for an object.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache a cache
+ * \param object the object that owns the cached data
+ */
+
+void mlt_cache_purge( mlt_cache cache, void *object )
+{
+ pthread_mutex_lock( &cache->mutex );
+ if ( cache && object )
+ {
+ int i, j;
+ void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+ for ( i = 0, j = 0; i < cache->count; i++ )
+ {
+ void *o = cache->current[ i ];
+
+ if ( o == object )
+ {
+ pthread_mutex_unlock( &cache->mutex );
+ cache_object_close( cache, o, NULL );
+ pthread_mutex_lock( &cache->mutex );
+ }
+ else
+ {
+ alt[ j++ ] = o;
+ }
+ }
+ cache->count = j;
+ cache->current = alt;
+
+ // Remove the object's data from the active list regardless of refcount
+ char key[19];
+ sprintf( key, "%p", object );
+ mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL );
+ if ( item && item->destructor )
+ {
+ item->destructor( item->data );
+ item->data = NULL;
+ item->destructor = NULL;
+ mlt_properties_set_data( cache->active, key, NULL, 0, NULL, NULL );
+ }
+
+ // Remove the object's items from the garbage collection regardless of refcount
+ i = mlt_properties_count( cache->garbage );
+ while ( i-- )
+ {
+ item = mlt_properties_get_data_at( cache->garbage, i, NULL );
+ if ( object == item->object && item->destructor )
+ {
+ sprintf( key, "%p", item->data );
+ item->destructor( item->data );
+ item->data = NULL;
+ item->destructor = NULL;
+ mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL );
+ }
+ }
+ }
+ pthread_mutex_unlock( &cache->mutex );
+}
+
+/** Shuffle the cache entries between the two arrays and return the cache entry for an object.
+ *
+ * \private \memberof mlt_cache_s
+ * \param cache a cache object
+ * \param object the object that owns the cached data
+ * \return a cache entry if there was a hit or NULL for a miss
+ */
+
+static void** shuffle_get_hit( mlt_cache cache, void *object )
+{
+ int i = cache->count;
+ int j = cache->count - 1;
+ void **hit = NULL;
+ void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+ if ( cache->count > 0 && cache->count < CACHE_SIZE )
+ {
+ // first determine if we have a hit
+ while ( i-- && !hit )
+ {
+ void **o = &cache->current[ i ];
+ if ( *o == object )
+ hit = o;
+ }
+ // if there was no hit, we will not be shuffling out an entry
+ // and are still filling the cache
+ if ( !hit )
+ ++j;
+ // reset these
+ i = cache->count;
+ hit = NULL;
+ }
+
+ // shuffle the existing entries to the alternate array
+ while ( i-- )
+ {
+ void **o = &cache->current[ i ];
+
+ if ( !hit && *o == object )
+ {
+ hit = o;
+ }
+ else if ( j > 0 )
+ {
+ alt[ --j ] = *o;
+// mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] );
+ }
+ }
+ return hit;
+}
+
+/** Put a chunk of data in the cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache a cache object
+ * \param object the object to which this data belongs
+ * \param data an opaque pointer to the data to cache
+ * \param size the size of the data in bytes
+ * \param destructor a pointer to a function that can destroy or release a reference to the data.
+ */
+
+void mlt_cache_put( mlt_cache cache, void *object, void* data, int size, mlt_destructor destructor )
+{
+ pthread_mutex_lock( &cache->mutex );
+ void **hit = shuffle_get_hit( cache, object );
+ void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+ // add the object to the cache
+ if ( hit )
+ {
+ // release the old data
+ pthread_mutex_unlock( &cache->mutex );
+ cache_object_close( cache, *hit, NULL );
+ pthread_mutex_lock( &cache->mutex );
+ // the MRU end gets the updated data
+ hit = &alt[ cache->count - 1 ];
+ }
+ else if ( cache->count < CACHE_SIZE )
+ {
+ // more room in cache, add it to MRU end
+ hit = &alt[ cache->count++ ];
+ }
+ else
+ {
+ // release the entry at the LRU end
+ pthread_mutex_unlock( &cache->mutex );
+ cache_object_close( cache, cache->current[0], NULL );
+ pthread_mutex_lock( &cache->mutex );
+
+ // The MRU end gets the new item
+ hit = &alt[ cache->count - 1 ];
+ }
+ *hit = object;
+ mlt_log( NULL, MLT_LOG_DEBUG, "%s: put %d = %p, %p\n", __FUNCTION__, cache->count - 1, object, data );
+
+ // Fetch the cache item
+ char key[19];
+ sprintf( key, "%p", object );
+ mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL );
+ if ( !item )
+ {
+ item = calloc( 1, sizeof( mlt_cache_item_s ) );
+ if ( item )
+ mlt_properties_set_data( cache->active, key, item, 0, free, NULL );
+ }
+ if ( item )
+ {
+ // If updating the cache item but not all references are released
+ // copy the item to the garbage collection.
+ if ( item->refcount > 0 && item->data )
+ {
+ mlt_cache_item orphan = calloc( 1, sizeof( mlt_cache_item_s ) );
+ if ( orphan )
+ {
+ mlt_log( NULL, MLT_LOG_DEBUG, "adding to garbage collection object %p data %p\n", item->object, item->data );
+ *orphan = *item;
+ sprintf( key, "%p", orphan->data );
+ // We store in the garbage collection by data address, not the owner's!
+ mlt_properties_set_data( cache->garbage, key, orphan, 0, free, NULL );
+ }
+ }
+
+ // Set/update the cache item
+ item->cache = cache;
+ item->object = object;
+ item->data = data;
+ item->size = size;
+ item->destructor = destructor;
+ item->refcount = 1;
+ }
+
+ // swap the current array
+ cache->current = alt;
+ pthread_mutex_unlock( &cache->mutex );
+}
+
+/** Get a chunk of data from the cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache a cache object
+ * \param object the object for which you are trying to locate the data
+ * \return a mlt_cache_item if found or NULL if not found or has been flushed from the cache
+ */
+
+mlt_cache_item mlt_cache_get( mlt_cache cache, void *object )
+{
+ mlt_cache_item result = NULL;
+ pthread_mutex_lock( &cache->mutex );
+ void **hit = shuffle_get_hit( cache, object );
+ void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+ if ( hit )
+ {
+ // copy the hit to the MRU end
+ alt[ cache->count - 1 ] = *hit;
+ hit = &alt[ cache->count - 1 ];
+
+ char key[19];
+ sprintf( key, "%p", *hit );
+ result = mlt_properties_get_data( cache->active, key, NULL );
+ if ( result && result->data )
+ result->refcount++;
+ mlt_log( NULL, MLT_LOG_DEBUG, "%s: get %d = %p, %p\n", __FUNCTION__, cache->count - 1, *hit, result->data );
+
+ // swap the current array
+ cache->current = alt;
+ }
+ pthread_mutex_unlock( &cache->mutex );
+
+ return result;
+}
--- /dev/null
+/**
+ * \file mlt_cache.h
+ * \brief least recently used cache
+ * \see mlt_cache_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_CACHE_H
+#define _MLT_CACHE_H
+
+#include "mlt_types.h"
+
+extern void *mlt_cache_item_data( mlt_cache_item item, int *size );
+extern void mlt_cache_item_close( mlt_cache_item item );
+
+extern mlt_cache mlt_cache_init();
+extern void mlt_cache_close( mlt_cache cache );
+extern void mlt_cache_purge( mlt_cache cache, void *object );
+extern void mlt_cache_put( mlt_cache cache, void *object, void* data, int size, mlt_destructor destructor );
+extern mlt_cache_item mlt_cache_get( mlt_cache cache, void *object );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_consumer.c
+ * \brief abstraction for all consumer services
+ * \see mlt_consumer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_consumer.h"
+#include "mlt_factory.h"
+#include "mlt_producer.h"
+#include "mlt_frame.h"
+#include "mlt_profile.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+/** Define this if you want an automatic deinterlace (if necessary) when the
+ * consumer's producer is not running at normal speed.
+ */
+#undef DEINTERLACE_ON_NOT_NORMAL_SPEED
+
+static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name );
+static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties );
+
+/** Initialize a consumer service.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this the consumer to initialize
+ * \param child a pointer to the object for the subclass
+ * \param profile the \p mlt_profile_s to use (optional but recommended,
+ * uses the environment variable MLT if this is NULL)
+ * \return true if there was an error
+ */
+
+int mlt_consumer_init( mlt_consumer this, void *child, mlt_profile profile )
+{
+ int error = 0;
+ memset( this, 0, sizeof( struct mlt_consumer_s ) );
+ this->child = child;
+ error = mlt_service_init( &this->parent, this );
+ if ( error == 0 )
+ {
+ // Get the properties from the service
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+
+ // Apply profile to properties
+ if ( profile == NULL )
+ {
+ // Normally the application creates the profile and controls its lifetime
+ // This is the fallback exception handling
+ profile = mlt_profile_init( NULL );
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_properties_set_data( properties, "_profile", profile, 0, (mlt_destructor)mlt_profile_close, NULL );
+ }
+ apply_profile_properties( this, profile, properties );
+
+ // Default rescaler for all consumers
+ mlt_properties_set( properties, "rescale", "bilinear" );
+
+ // Default read ahead buffer size
+ mlt_properties_set_int( properties, "buffer", 25 );
+
+ // Default audio frequency and channels
+ mlt_properties_set_int( properties, "frequency", 48000 );
+ mlt_properties_set_int( properties, "channels", 2 );
+
+ // Default of all consumers is real time
+ mlt_properties_set_int( properties, "real_time", 1 );
+
+ // Default to environment test card
+ mlt_properties_set( properties, "test_card", mlt_environment( "MLT_TEST_CARD" ) );
+
+ // Hmm - default all consumers to yuv422 :-/
+ this->format = mlt_image_yuv422;
+
+ mlt_events_register( properties, "consumer-frame-show", ( mlt_transmitter )mlt_consumer_frame_show );
+ mlt_events_register( properties, "consumer-frame-render", ( mlt_transmitter )mlt_consumer_frame_render );
+ mlt_events_register( properties, "consumer-stopped", NULL );
+
+ // Register a property-changed listener to handle the profile property -
+ // subsequent properties can override the profile
+ this->event_listener = mlt_events_listen( properties, this, "property-changed", ( mlt_listener )mlt_consumer_property_changed );
+
+ // Create the push mutex and condition
+ pthread_mutex_init( &this->put_mutex, NULL );
+ pthread_cond_init( &this->put_cond, NULL );
+
+ }
+ return error;
+}
+
+/** Convert the profile into properties on the consumer.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param this a consumer
+ * \param profile a profile
+ * \param properties a properties list (typically, the consumer's)
+ */
+
+static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties )
+{
+ mlt_event_block( this->event_listener );
+ mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+ mlt_properties_set_int( properties, "frame_rate_num", profile->frame_rate_num );
+ mlt_properties_set_int( properties, "frame_rate_den", profile->frame_rate_den );
+ mlt_properties_set_int( properties, "width", profile->width );
+ mlt_properties_set_int( properties, "height", profile->height );
+ mlt_properties_set_int( properties, "progressive", profile->progressive );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
+ mlt_properties_set_int( properties, "sample_aspect_num", profile->sample_aspect_num );
+ mlt_properties_set_int( properties, "sample_aspect_den", profile->sample_aspect_den );
+ mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) );
+ mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num );
+ mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num );
+ mlt_event_unblock( this->event_listener );
+}
+
+/** The property-changed event listener
+ *
+ * \private \memberof mlt_consumer_s
+ * \param owner the service a service (ignored)
+ * \param this the consumer
+ * \param name the name of the property that changed
+ */
+
+static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name )
+{
+ if ( !strcmp( name, "profile" ) )
+ {
+ // Get the properies
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get the current profile
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+
+ // Load the new profile
+ mlt_profile new_profile = mlt_profile_init( mlt_properties_get( properties, name ) );
+
+ if ( new_profile )
+ {
+ // Copy the profile
+ if ( profile != NULL )
+ {
+ free( profile->description );
+ memcpy( profile, new_profile, sizeof( struct mlt_profile_s ) );
+ profile->description = strdup( new_profile->description );
+ mlt_profile_close( new_profile );
+ }
+ else
+ {
+ profile = new_profile;
+ }
+
+ // Apply to properties
+ apply_profile_properties( this, profile, properties );
+ }
+ }
+ else if ( !strcmp( name, "frame_rate_num" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ {
+ profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
+ mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+ }
+ }
+ else if ( !strcmp( name, "frame_rate_den" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ {
+ profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
+ mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+ }
+ }
+ else if ( !strcmp( name, "width" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ profile->width = mlt_properties_get_int( properties, "width" );
+ }
+ else if ( !strcmp( name, "height" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ profile->height = mlt_properties_get_int( properties, "height" );
+ }
+ else if ( !strcmp( name, "progressive" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ profile->progressive = mlt_properties_get_int( properties, "progressive" );
+ }
+ else if ( !strcmp( name, "sample_aspect_num" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
+ if ( profile )
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
+ }
+ else if ( !strcmp( name, "sample_aspect_den" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
+ if ( profile )
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
+ }
+ else if ( !strcmp( name, "display_aspect_num" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ {
+ profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
+ mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) );
+ }
+ }
+ else if ( !strcmp( name, "display_aspect_den" ) )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ {
+ profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
+ mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) );
+ }
+ }
+}
+
+/** The transmitter for the consumer-frame-show event
+ *
+ * Invokes the listener.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param listener a function pointer that will be invoked
+ * \param owner a properties list that will be passed to \p listener
+ * \param this a service that will be passed to \p listener
+ * \param args an array of pointers - the first entry is passed as a string to \p listener
+ */
+
+static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( mlt_frame )args[ 0 ] );
+}
+
+/** The transmitter for the consumer-frame-render event
+ *
+ * Invokes the listener.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param listener a function pointer that will be invoked
+ * \param owner a properties list that will be passed to \p listener
+ * \param this a service that will be passed to \p listener
+ * \param args an array of pointers - the first entry is passed as a string to \p listener
+ */
+
+static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( mlt_frame )args[ 0 ] );
+}
+
+/** Create a new consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param profile a profile (optional, but recommended)
+ * \return a new consumer
+ */
+
+mlt_consumer mlt_consumer_new( mlt_profile profile )
+{
+ // Create the memory for the structure
+ mlt_consumer this = malloc( sizeof( struct mlt_consumer_s ) );
+
+ // Initialise it
+ if ( this != NULL )
+ mlt_consumer_init( this, NULL, profile );
+
+ // Return it
+ return this;
+}
+
+/** Get the parent service object.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return the parent service class
+ * \see MLT_CONSUMER_SERVICE
+ */
+
+mlt_service mlt_consumer_service( mlt_consumer this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the consumer properties.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return the consumer's properties list
+ * \see MLT_CONSUMER_PROPERTIES
+ */
+
+mlt_properties mlt_consumer_properties( mlt_consumer this )
+{
+ return this != NULL ? MLT_SERVICE_PROPERTIES( &this->parent ) : NULL;
+}
+
+/** Connect the consumer to the producer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \param producer a producer
+ * \return > 0 warning, == 0 success, < 0 serious error,
+ * 1 = this service does not accept input,
+ * 2 = the producer is invalid,
+ * 3 = the producer is already registered with this consumer
+ */
+
+int mlt_consumer_connect( mlt_consumer this, mlt_service producer )
+{
+ return mlt_service_connect_producer( &this->parent, producer, 0 );
+}
+
+/** Start the consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return true if there was an error
+ */
+
+int mlt_consumer_start( mlt_consumer this )
+{
+ // Stop listening to the property-changed event
+ mlt_event_block( this->event_listener );
+
+ // Get the properies
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Determine if there's a test card producer
+ char *test_card = mlt_properties_get( properties, "test_card" );
+
+ // Just to make sure nothing is hanging around...
+ mlt_frame_close( this->put );
+ this->put = NULL;
+ this->put_active = 1;
+
+ // Deal with it now.
+ if ( test_card != NULL )
+ {
+ if ( mlt_properties_get_data( properties, "test_card_producer", NULL ) == NULL )
+ {
+ // Create a test card producer
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ mlt_producer producer = mlt_factory_producer( profile, NULL, test_card );
+
+ // Do we have a producer
+ if ( producer != NULL )
+ {
+ // Test card should loop I guess...
+ mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
+ //mlt_producer_set_speed( producer, 0 );
+ //mlt_producer_set_in_and_out( producer, 0, 0 );
+
+ // Set the test card on the consumer
+ mlt_properties_set_data( properties, "test_card_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ }
+ }
+ }
+ else
+ {
+ // Allow the hash table to speed things up
+ mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
+ }
+
+ // Set the frame duration in microseconds for the frame-dropping heuristic
+ int frame_duration = 1000000 / mlt_properties_get_int( properties, "frame_rate_num" ) *
+ mlt_properties_get_int( properties, "frame_rate_den" );
+ mlt_properties_set_int( properties, "frame_duration", frame_duration );
+
+ // Check and run an ante command
+ if ( mlt_properties_get( properties, "ante" ) )
+ if ( system( mlt_properties_get( properties, "ante" ) ) == -1 )
+ mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) );
+
+ // Set the real_time preference
+ this->real_time = mlt_properties_get_int( properties, "real_time" );
+
+ // Start the service
+ if ( this->start != NULL )
+ return this->start( this );
+
+ return 0;
+}
+
+/** An alternative method to feed frames into the consumer.
+ *
+ * Only valid if the consumer itself is not connected.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \param frame a frame
+ * \return true (ignore this for now)
+ */
+
+int mlt_consumer_put_frame( mlt_consumer this, mlt_frame frame )
+{
+ int error = 1;
+
+ // Get the service assoicated to the consumer
+ mlt_service service = MLT_CONSUMER_SERVICE( this );
+
+ if ( mlt_service_producer( service ) == NULL )
+ {
+ struct timeval now;
+ struct timespec tm;
+ pthread_mutex_lock( &this->put_mutex );
+ while ( this->put_active && this->put != NULL )
+ {
+ gettimeofday( &now, NULL );
+ tm.tv_sec = now.tv_sec + 1;
+ tm.tv_nsec = now.tv_usec * 1000;
+ pthread_cond_timedwait( &this->put_cond, &this->put_mutex, &tm );
+ }
+ if ( this->put_active && this->put == NULL )
+ this->put = frame;
+ else
+ mlt_frame_close( frame );
+ pthread_cond_broadcast( &this->put_cond );
+ pthread_mutex_unlock( &this->put_mutex );
+ }
+ else
+ {
+ mlt_frame_close( frame );
+ }
+
+ return error;
+}
+
+/** Protected method for consumer to get frames from connected service
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return a frame
+ */
+
+mlt_frame mlt_consumer_get_frame( mlt_consumer this )
+{
+ // Frame to return
+ mlt_frame frame = NULL;
+
+ // Get the service assoicated to the consumer
+ mlt_service service = MLT_CONSUMER_SERVICE( this );
+
+ // Get the consumer properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get the frame
+ if ( mlt_service_producer( service ) == NULL && mlt_properties_get_int( properties, "put_mode" ) )
+ {
+ struct timeval now;
+ struct timespec tm;
+ pthread_mutex_lock( &this->put_mutex );
+ while ( this->put_active && this->put == NULL )
+ {
+ gettimeofday( &now, NULL );
+ tm.tv_sec = now.tv_sec + 1;
+ tm.tv_nsec = now.tv_usec * 1000;
+ pthread_cond_timedwait( &this->put_cond, &this->put_mutex, &tm );
+ }
+ frame = this->put;
+ this->put = NULL;
+ pthread_cond_broadcast( &this->put_cond );
+ pthread_mutex_unlock( &this->put_mutex );
+ if ( frame != NULL )
+ mlt_service_apply_filters( service, frame, 0 );
+ }
+ else if ( mlt_service_producer( service ) != NULL )
+ {
+ mlt_service_get_frame( service, &frame, 0 );
+ }
+ else
+ {
+ frame = mlt_frame_init( service );
+ }
+
+ if ( frame != NULL )
+ {
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get the test card producer
+ mlt_producer test_card = mlt_properties_get_data( properties, "test_card_producer", NULL );
+
+ // Attach the test frame producer to it.
+ if ( test_card != NULL )
+ mlt_properties_set_data( frame_properties, "test_card_producer", test_card, 0, NULL, NULL );
+
+ // Attach the rescale property
+ mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale" ) );
+
+ // Aspect ratio and other jiggery pokery
+ mlt_properties_set_double( frame_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "aspect_ratio" ) );
+ mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" ) );
+ mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
+ }
+
+ // Return the frame
+ return frame;
+}
+
+/** Compute the time difference between now and a time value.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param time1 a time value to be compared against now
+ * \return the difference in microseconds
+ */
+
+static inline long time_difference( struct timeval *time1 )
+{
+ struct timeval time2;
+ time2.tv_sec = time1->tv_sec;
+ time2.tv_usec = time1->tv_usec;
+ gettimeofday( time1, NULL );
+ return time1->tv_sec * 1000000 + time1->tv_usec - time2.tv_sec * 1000000 - time2.tv_usec;
+}
+
+/** The thread procedure for asynchronously pulling frames through the service
+ * network connected to a consumer.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param arg a consumer
+ */
+
+static void *consumer_read_ahead_thread( void *arg )
+{
+ // The argument is the consumer
+ mlt_consumer this = arg;
+
+ // Get the properties of the consumer
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get the width and height
+ int width = mlt_properties_get_int( properties, "width" );
+ int height = mlt_properties_get_int( properties, "height" );
+
+ // See if video is turned off
+ int video_off = mlt_properties_get_int( properties, "video_off" );
+ int preview_off = mlt_properties_get_int( properties, "preview_off" );
+ int preview_format = mlt_properties_get_int( properties, "preview_format" );
+
+ // Get the audio settings
+ mlt_audio_format afmt = mlt_audio_pcm;
+ int counter = 0;
+ double fps = mlt_properties_get_double( properties, "fps" );
+ int channels = mlt_properties_get_int( properties, "channels" );
+ int frequency = mlt_properties_get_int( properties, "frequency" );
+ int samples = 0;
+ int16_t *pcm = NULL;
+
+ // See if audio is turned off
+ int audio_off = mlt_properties_get_int( properties, "audio_off" );
+
+ // Get the maximum size of the buffer
+ int buffer = mlt_properties_get_int( properties, "buffer" ) + 1;
+
+ // General frame variable
+ mlt_frame frame = NULL;
+ uint8_t *image = NULL;
+
+ // Time structures
+ struct timeval ante;
+
+ // Average time for get_frame and get_image
+ int count = 1;
+ int skipped = 0;
+ int64_t time_wait = 0;
+ int64_t time_frame = 0;
+ int64_t time_process = 0;
+ int skip_next = 0;
+ mlt_service lock_object = NULL;
+
+ if ( preview_off && preview_format != 0 )
+ this->format = preview_format;
+
+ // Get the first frame
+ frame = mlt_consumer_get_frame( this );
+
+ // Get the lock object
+ lock_object = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "consumer_lock_service", NULL );
+
+ // Lock it
+ if ( lock_object ) mlt_service_lock( lock_object );
+
+ // Get the image of the first frame
+ if ( !video_off )
+ {
+ mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-frame-render", frame, NULL );
+ mlt_frame_get_image( frame, &image, &this->format, &width, &height, 0 );
+ }
+
+ if ( !audio_off )
+ {
+ samples = mlt_sample_calculator( fps, frequency, counter++ );
+ mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+ }
+
+ // Unlock the lock object
+ if ( lock_object ) mlt_service_unlock( lock_object );
+
+ // Mark as rendered
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
+
+ // Get the starting time (can ignore the times above)
+ gettimeofday( &ante, NULL );
+
+ // Continue to read ahead
+ while ( this->ahead )
+ {
+ // Fetch width/height again
+ width = mlt_properties_get_int( properties, "width" );
+ height = mlt_properties_get_int( properties, "height" );
+
+ // Put the current frame into the queue
+ pthread_mutex_lock( &this->mutex );
+ while( this->ahead && mlt_deque_count( this->queue ) >= buffer )
+ pthread_cond_wait( &this->cond, &this->mutex );
+ mlt_deque_push_back( this->queue, frame );
+ pthread_cond_broadcast( &this->cond );
+ pthread_mutex_unlock( &this->mutex );
+
+ time_wait += time_difference( &ante );
+
+ // Get the next frame
+ frame = mlt_consumer_get_frame( this );
+ time_frame += time_difference( &ante );
+
+ // If there's no frame, we're probably stopped...
+ if ( frame == NULL )
+ continue;
+
+ // Attempt to fetch the lock object
+ lock_object = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "consumer_lock_service", NULL );
+
+ // Increment the count
+ count ++;
+
+ // Lock if there's a lock object
+ if ( lock_object ) mlt_service_lock( lock_object );
+
+ // All non normal playback frames should be shown
+ if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 )
+ {
+#ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
+#endif
+ skipped = 0;
+ time_frame = 0;
+ time_process = 0;
+ time_wait = 0;
+ count = 1;
+ skip_next = 0;
+ }
+
+ // Get the image
+ if ( !skip_next || this->real_time == -1 )
+ {
+ // Get the image, mark as rendered and time it
+ if ( !video_off )
+ {
+ mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-frame-render", frame, NULL );
+ mlt_frame_get_image( frame, &image, &this->format, &width, &height, 0 );
+ }
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
+ }
+ else
+ {
+ // Increment the number of sequentially skipped frames
+ skipped ++;
+ skip_next = 0;
+
+ // If we've reached an unacceptable level, reset everything
+ if ( skipped > 5 )
+ {
+ skipped = 0;
+ time_frame = 0;
+ time_process = 0;
+ time_wait = 0;
+ count = 1;
+ }
+ }
+
+ // Always process audio
+ if ( !audio_off )
+ {
+ samples = mlt_sample_calculator( fps, frequency, counter++ );
+ mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+ }
+
+ // Increment the time take for this frame
+ time_process += time_difference( &ante );
+
+ // Determine if the next frame should be skipped
+ if ( mlt_deque_count( this->queue ) <= 5 )
+ {
+ int frame_duration = mlt_properties_get_int( properties, "frame_duration" );
+ if ( ( ( time_wait + time_frame + time_process ) / count ) > frame_duration )
+ skip_next = 1;
+ }
+
+ // Unlock if there's a lock object
+ if ( lock_object ) mlt_service_unlock( lock_object );
+ }
+
+ // Remove the last frame
+ mlt_frame_close( frame );
+
+ return NULL;
+}
+
+/** Start the read/render thread.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+static void consumer_read_ahead_start( mlt_consumer this )
+{
+ // We're running now
+ this->ahead = 1;
+
+ // Create the frame queue
+ this->queue = mlt_deque_init( );
+
+ // Create the mutex
+ pthread_mutex_init( &this->mutex, NULL );
+
+ // Create the condition
+ pthread_cond_init( &this->cond, NULL );
+
+ // Create the read ahead
+ if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "priority" ) )
+ {
+ struct sched_param priority;
+ priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( this ), "priority" );
+ pthread_attr_t thread_attributes;
+ pthread_attr_init( &thread_attributes );
+ pthread_attr_setschedpolicy( &thread_attributes, SCHED_OTHER );
+ pthread_attr_setschedparam( &thread_attributes, &priority );
+ pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED );
+ pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM );
+ if ( pthread_create( &this->ahead_thread, &thread_attributes, consumer_read_ahead_thread, this ) < 0 )
+ pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this );
+ pthread_attr_destroy( &thread_attributes );
+ }
+ else
+ {
+ pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this );
+ }
+}
+
+/** Stop the read/render thread.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+static void consumer_read_ahead_stop( mlt_consumer this )
+{
+ // Make sure we're running
+ if ( this->ahead )
+ {
+ // Inform thread to stop
+ this->ahead = 0;
+
+ // Broadcast to the condition in case it's waiting
+ pthread_mutex_lock( &this->mutex );
+ pthread_cond_broadcast( &this->cond );
+ pthread_mutex_unlock( &this->mutex );
+
+ // Broadcast to the put condition in case it's waiting
+ pthread_mutex_lock( &this->put_mutex );
+ pthread_cond_broadcast( &this->put_cond );
+ pthread_mutex_unlock( &this->put_mutex );
+
+ // Join the thread
+ pthread_join( this->ahead_thread, NULL );
+
+ // Destroy the mutex
+ pthread_mutex_destroy( &this->mutex );
+
+ // Destroy the condition
+ pthread_cond_destroy( &this->cond );
+
+ // Wipe the queue
+ while ( mlt_deque_count( this->queue ) )
+ mlt_frame_close( mlt_deque_pop_back( this->queue ) );
+
+ // Close the queue
+ mlt_deque_close( this->queue );
+ }
+}
+
+/** Flush the read/render thread's buffer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+void mlt_consumer_purge( mlt_consumer this )
+{
+ if ( this->ahead )
+ {
+ pthread_mutex_lock( &this->mutex );
+ while ( mlt_deque_count( this->queue ) )
+ mlt_frame_close( mlt_deque_pop_back( this->queue ) );
+ pthread_cond_broadcast( &this->cond );
+ pthread_mutex_unlock( &this->mutex );
+ }
+}
+
+/** Get the next frame from the producer connected to a consumer.
+ *
+ * Typically, one uses this instead of \p mlt_consumer_get_frame to make
+ * the asynchronous/real-time behavior configurable at runtime.
+ * You should close the frame returned from this when you are done with it.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return a frame
+ */
+
+mlt_frame mlt_consumer_rt_frame( mlt_consumer this )
+{
+ // Frame to return
+ mlt_frame frame = NULL;
+
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Check if the user has requested real time or not
+ if ( this->real_time )
+ {
+ int size = 1;
+
+ // Is the read ahead running?
+ if ( this->ahead == 0 )
+ {
+ int buffer = mlt_properties_get_int( properties, "buffer" );
+ int prefill = mlt_properties_get_int( properties, "prefill" );
+ consumer_read_ahead_start( this );
+ if ( buffer > 1 )
+ size = prefill > 0 && prefill < buffer ? prefill : buffer;
+ }
+
+ // Get frame from queue
+ pthread_mutex_lock( &this->mutex );
+ while( this->ahead && mlt_deque_count( this->queue ) < size )
+ pthread_cond_wait( &this->cond, &this->mutex );
+ frame = mlt_deque_pop_front( this->queue );
+ pthread_cond_broadcast( &this->cond );
+ pthread_mutex_unlock( &this->mutex );
+ }
+ else
+ {
+ // Get the frame in non real time
+ frame = mlt_consumer_get_frame( this );
+
+ // This isn't true, but from the consumers perspective it is
+ if ( frame != NULL )
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
+ }
+
+ return frame;
+}
+
+/** Callback for the implementation to indicate a stopped condition.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+void mlt_consumer_stopped( mlt_consumer this )
+{
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "running", 0 );
+ mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-stopped", NULL );
+ mlt_event_unblock( this->event_listener );
+}
+
+/** Stop the consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return true if there was an error
+ */
+
+int mlt_consumer_stop( mlt_consumer this )
+{
+ // Get the properies
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Just in case...
+ mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping put waiting\n" );
+ pthread_mutex_lock( &this->put_mutex );
+ this->put_active = 0;
+ pthread_cond_broadcast( &this->put_cond );
+ pthread_mutex_unlock( &this->put_mutex );
+
+ // Stop the consumer
+ mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping consumer\n" );
+ if ( this->stop != NULL )
+ this->stop( this );
+
+ // Check if the user has requested real time or not and stop if necessary
+ mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping read_ahead\n" );
+ if ( mlt_properties_get_int( properties, "real_time" ) )
+ consumer_read_ahead_stop( this );
+
+ // Kill the test card
+ mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
+
+ // Check and run a post command
+ if ( mlt_properties_get( properties, "post" ) )
+ if (system( mlt_properties_get( properties, "post" ) ) == -1 )
+ mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) );
+
+ mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopped\n" );
+
+ return 0;
+}
+
+/** Determine if the consumer is stopped.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return true if the consumer is stopped
+ */
+
+int mlt_consumer_is_stopped( mlt_consumer this )
+{
+ // Check if the consumer is stopped
+ if ( this->is_stopped != NULL )
+ return this->is_stopped( this );
+
+ return 0;
+}
+
+/** Close and destroy the consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+void mlt_consumer_close( mlt_consumer this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_CONSUMER_PROPERTIES( this ) ) <= 0 )
+ {
+ // Get the childs close function
+ void ( *consumer_close )( ) = this->close;
+
+ if ( consumer_close )
+ {
+ // Just in case...
+ //mlt_consumer_stop( this );
+
+ this->close = NULL;
+ consumer_close( this );
+ }
+ else
+ {
+ // Make sure it only gets called once
+ this->parent.close = NULL;
+
+ // Destroy the push mutex and condition
+ pthread_mutex_destroy( &this->put_mutex );
+ pthread_cond_destroy( &this->put_cond );
+
+ mlt_service_close( &this->parent );
+ }
+ }
+}
--- /dev/null
+/**
+ * \file mlt_consumer.h
+ * \brief abstraction for all consumer services
+ * \see mlt_consumer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_CONSUMER_H_
+#define _MLT_CONSUMER_H_
+
+#include "mlt_service.h"
+#include "mlt_events.h"
+#include <pthread.h>
+
+/** \brief Consumer abstract service class
+ *
+ * A consumer is a service that pulls audio and video from the connected
+ * producers, filters, and transitions. Typically a consumer is used to
+ * output audio and/or video to a device, file, or socket.
+ *
+ * \extends mlt_service_s
+ * \properties \em rescale the scaling algorithm to pass on to all scaling
+ * filters, defaults to "bilinear"
+ * \properties \em buffer the number of frames to use in the asynchronous
+ * render thread, defaults to 25
+ * \properties \em frequency the audio sample rate to use in Hertz, defaults to 48000
+ * \properties \em channels the number of audio channels to use, defaults to 2
+ * \properties \em real_time the asynchronous behavior: 1 (default) for asynchronous
+ * with frame dropping, -1 for asynchronous without frame dropping, 0 to disable (synchronous)
+ * \properties \em test_card the name of a resource to use as the test card, defaults to
+ * environment variable MLT_TEST_CARD. If undefined, the hard-coded default test card is
+ * white silence. A test card is what appears when nothing is produced.
+ * \event \em consumer-frame-show Subclass implementations should fire this.
+ * \event \em consumer-frame-render The abstract class fires this.
+ * \event \em consumer-stopped
+ * \properties \em fps video frames per second as floating point (read only)
+ * \properties \em frame_rate_num the numerator of the video frame rate, overrides \p mlt_profile_s
+ * \properties \em frame_rate_den the denominator of the video frame rate, overrides \p mlt_profile_s
+ * \properties \em width the horizontal video resolution, overrides \p mlt_profile_s
+ * \properties \em height the vertical video resolution, overrides \p mlt_profile_s
+ * \properties \em progressive a flag that indicates if the video is interlaced
+ * or progressive, overrides \p mlt_profile_s
+ * \properties \em aspect_ratio the video sample (pixel) aspect ratio as floating point (read only)
+ * \properties \em sample_aspect_num the numerator of the sample aspect ratio, overrides \p mlt_profile_s
+ * \properties \em sample_aspect_den the denominator of the sample aspect ratio, overrides \p mlt_profile_s
+ * \properties \em display_ratio the video frame aspect ratio as floating point (read only)
+ * \properties \em display_aspect_num the numerator of the video frame aspect ratio, overrides \p mlt_profile_s
+ * \properties \em display_aspect_den the denominator of the video frame aspect ratio, overrides \p mlt_profile_s
+ *
+ */
+
+struct mlt_consumer_s
+{
+ /** A consumer is a service. */
+ struct mlt_service_s parent;
+
+ /** Start the consumer to pull frames (virtual function).
+ *
+ * \param mlt_consumer a consumer
+ * \return true if there was an error
+ */
+ int ( *start )( mlt_consumer );
+
+ /** Stop the consumer (virtual function).
+ *
+ * \param mlt_consumer a consumer
+ * \return true if there was an error
+ */
+ int ( *stop )( mlt_consumer );
+
+ /** Get whether the consumer is running or stopped (virtual function).
+ *
+ * \param mlt_consumer a consumer
+ * \return true if the consumer is stopped
+ */
+ int ( *is_stopped )( mlt_consumer );
+
+ /** The destructor virtual function
+ *
+ * \param mlt_consumer a consumer
+ */
+ void ( *close )( mlt_consumer );
+
+ void *local; /**< \private instance object */
+ void *child; /**< \private the object of a subclass */
+
+ int real_time;
+ int ahead;
+ mlt_image_format format;
+ mlt_deque queue;
+ pthread_t ahead_thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ pthread_mutex_t put_mutex;
+ pthread_cond_t put_cond;
+ mlt_frame put;
+ int put_active;
+ mlt_event event_listener;
+};
+
+#define MLT_CONSUMER_SERVICE( consumer ) ( &( consumer )->parent )
+#define MLT_CONSUMER_PROPERTIES( consumer ) MLT_SERVICE_PROPERTIES( MLT_CONSUMER_SERVICE( consumer ) )
+
+extern int mlt_consumer_init( mlt_consumer self, void *child, mlt_profile profile );
+extern mlt_consumer mlt_consumer_new( mlt_profile profile );
+extern mlt_service mlt_consumer_service( mlt_consumer self );
+extern mlt_properties mlt_consumer_properties( mlt_consumer self );
+extern int mlt_consumer_connect( mlt_consumer self, mlt_service producer );
+extern int mlt_consumer_start( mlt_consumer self );
+extern void mlt_consumer_purge( mlt_consumer self );
+extern int mlt_consumer_put_frame( mlt_consumer self, mlt_frame frame );
+extern mlt_frame mlt_consumer_get_frame( mlt_consumer self );
+extern mlt_frame mlt_consumer_rt_frame( mlt_consumer self );
+extern int mlt_consumer_stop( mlt_consumer self );
+extern int mlt_consumer_is_stopped( mlt_consumer self );
+extern void mlt_consumer_stopped( mlt_consumer self );
+extern void mlt_consumer_close( mlt_consumer );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_deque.c
+ * \brief double ended queue
+ * \see mlt_deque_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Local header files
+#include "mlt_deque.h"
+
+// System header files
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief Deque entry class
+ *
+ */
+
+typedef union
+{
+ void *addr;
+ int value;
+ double floating;
+}
+deque_entry;
+
+/** \brief Double-Ended Queue (deque) class
+ *
+ * The double-ended queue is a very versatile data structure. MLT uses it as
+ * list, stack, and circular queue.
+ */
+
+struct mlt_deque_s
+{
+ deque_entry *list;
+ int size;
+ int count;
+};
+
+/** Create a deque.
+ *
+ * \public \memberof mlt_deque_s
+ * \return a new deque
+ */
+
+mlt_deque mlt_deque_init( )
+{
+ mlt_deque this = malloc( sizeof( struct mlt_deque_s ) );
+ if ( this != NULL )
+ {
+ this->list = NULL;
+ this->size = 0;
+ this->count = 0;
+ }
+ return this;
+}
+
+/** Return the number of items in the deque.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return the number of items
+ */
+
+int mlt_deque_count( mlt_deque this )
+{
+ return this->count;
+}
+
+/** Allocate space on the deque.
+ *
+ * \private \memberof mlt_deque_s
+ * \param this a deque
+ * \return true if there was an error
+ */
+
+static int mlt_deque_allocate( mlt_deque this )
+{
+ if ( this->count == this->size )
+ {
+ this->list = realloc( this->list, sizeof( deque_entry ) * ( this->size + 20 ) );
+ this->size += 20;
+ }
+ return this->list == NULL;
+}
+
+/** Push an item to the end.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an opaque pointer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_back( mlt_deque this, void *item )
+{
+ int error = mlt_deque_allocate( this );
+
+ if ( error == 0 )
+ this->list[ this->count ++ ].addr = item;
+
+ return error;
+}
+
+/** Pop an item.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a pointer
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_pop_back( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ -- this->count ].addr : NULL;
+}
+
+/** Queue an item at the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an opaque pointer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_front( mlt_deque this, void *item )
+{
+ int error = mlt_deque_allocate( this );
+
+ if ( error == 0 )
+ {
+ memmove( &this->list[ 1 ], this->list, ( this->count ++ ) * sizeof( deque_entry ) );
+ this->list[ 0 ].addr = item;
+ }
+
+ return error;
+}
+
+/** Remove an item from the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a pointer
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_pop_front( mlt_deque this )
+{
+ void *item = NULL;
+
+ if ( this->count > 0 )
+ {
+ item = this->list[ 0 ].addr;
+ memmove( this->list, &this->list[ 1 ], ( -- this->count ) * sizeof( deque_entry ) );
+ }
+
+ return item;
+}
+
+/** Inquire on item at back of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_peek_back( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ this->count - 1 ].addr : NULL;
+}
+
+/** Inquire on item at front of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_peek_front( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ 0 ].addr : NULL;
+}
+
+/** Push an integer to the end.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an integer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_back_int( mlt_deque this, int item )
+{
+ int error = mlt_deque_allocate( this );
+
+ if ( error == 0 )
+ this->list[ this->count ++ ].value = item;
+
+ return error;
+}
+
+/** Pop an integer.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_pop_back_int( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ -- this->count ].value : 0;
+}
+
+/** Queue an integer at the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an integer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_front_int( mlt_deque this, int item )
+{
+ int error = mlt_deque_allocate( this );
+
+ if ( error == 0 )
+ {
+ memmove( &this->list[ 1 ], this->list, ( this->count ++ ) * sizeof( deque_entry ) );
+ this->list[ 0 ].value = item;
+ }
+
+ return error;
+}
+
+/** Remove an integer from the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_pop_front_int( mlt_deque this )
+{
+ int item = 0;
+
+ if ( this->count > 0 )
+ {
+ item = this->list[ 0 ].value;
+ memmove( this->list, &this->list[ 1 ], ( -- this->count ) * sizeof( deque_entry ) );
+ }
+
+ return item;
+}
+
+/** Inquire on an integer at back of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_peek_back_int( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ this->count - 1 ].value : 0;
+}
+
+/** Inquire on an integer at front of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_peek_front_int( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ 0 ].value : 0;
+}
+
+/** Push a double float to the end.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item a double float
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_back_double( mlt_deque this, double item )
+{
+ int error = mlt_deque_allocate( this );
+
+ if ( error == 0 )
+ this->list[ this->count ++ ].floating = item;
+
+ return error;
+}
+
+/** Pop a double float.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_pop_back_double( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ -- this->count ].floating : 0;
+}
+
+/** Queue a double float at the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item a double float
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_front_double( mlt_deque this, double item )
+{
+ int error = mlt_deque_allocate( this );
+
+ if ( error == 0 )
+ {
+ memmove( &this->list[ 1 ], this->list, ( this->count ++ ) * sizeof( deque_entry ) );
+ this->list[ 0 ].floating = item;
+ }
+
+ return error;
+}
+
+/** Remove a double float from the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_pop_front_double( mlt_deque this )
+{
+ double item = 0;
+
+ if ( this->count > 0 )
+ {
+ item = this->list[ 0 ].floating;
+ memmove( this->list, &this->list[ 1 ], ( -- this->count ) * sizeof( deque_entry ) );
+ }
+
+ return item;
+}
+
+/** Inquire on a double float at back of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_peek_back_double( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ this->count - 1 ].floating : 0;
+}
+
+/** Inquire on a double float at front of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_peek_front_double( mlt_deque this )
+{
+ return this->count > 0 ? this->list[ 0 ].floating : 0;
+}
+
+/** Destroy the queue.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ */
+
+void mlt_deque_close( mlt_deque this )
+{
+ free( this->list );
+ free( this );
+}
--- /dev/null
+/**
+ * \file mlt_deque.h
+ * \brief double ended queue
+ * \see mlt_deque_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_DEQUE_H_
+#define _MLT_DEQUE_H_
+
+#include "mlt_types.h"
+
+extern mlt_deque mlt_deque_init( );
+extern int mlt_deque_count( mlt_deque self );
+extern int mlt_deque_push_back( mlt_deque self, void *item );
+extern void *mlt_deque_pop_back( mlt_deque self );
+extern int mlt_deque_push_front( mlt_deque self, void *item );
+extern void *mlt_deque_pop_front( mlt_deque self );
+extern void *mlt_deque_peek_back( mlt_deque self );
+extern void *mlt_deque_peek_front( mlt_deque self );
+
+extern int mlt_deque_push_back_int( mlt_deque self, int item );
+extern int mlt_deque_pop_back_int( mlt_deque self );
+extern int mlt_deque_push_front_int( mlt_deque self, int item );
+extern int mlt_deque_pop_front_int( mlt_deque self );
+extern int mlt_deque_peek_back_int( mlt_deque self );
+extern int mlt_deque_peek_front_int( mlt_deque self );
+
+extern int mlt_deque_push_back_double( mlt_deque self, double item );
+extern double mlt_deque_pop_back_double( mlt_deque self );
+extern int mlt_deque_push_front_double( mlt_deque self, double item );
+extern double mlt_deque_pop_front_double( mlt_deque self );
+extern double mlt_deque_peek_back_double( mlt_deque self );
+extern double mlt_deque_peek_front_double( mlt_deque self );
+
+extern void mlt_deque_close( mlt_deque self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_events.c
+ * \brief event handling
+ * \see mlt_events_struct
+ *
+ * Copyright (C) 2004-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "mlt_properties.h"
+#include "mlt_events.h"
+
+/* Memory leak checks. */
+
+#undef _MLT_EVENT_CHECKS_
+
+#ifdef _MLT_EVENT_CHECKS_
+static int events_created = 0;
+static int events_destroyed = 0;
+#endif
+
+/** \brief Events class
+ *
+ * Events provide messages and notifications between services and the application.
+ * A service can register an event and fire/send it upon certain conditions or times.
+ * Likewise, a service or an application can listen/receive specific events on specific
+ * services.
+ */
+
+struct mlt_events_struct
+{
+ mlt_properties owner;
+ mlt_properties list;
+};
+
+typedef struct mlt_events_struct *mlt_events;
+
+/** \brief Event class
+ *
+ */
+
+struct mlt_event_struct
+{
+ mlt_events owner;
+ int ref_count;
+ int block_count;
+ mlt_listener listener;
+ void *service;
+};
+
+/** Increment the reference count on this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_inc_ref( mlt_event this )
+{
+ if ( this != NULL )
+ this->ref_count ++;
+}
+
+/** Increment the block count on this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_block( mlt_event this )
+{
+ if ( this != NULL && this->owner != NULL )
+ this->block_count ++;
+}
+
+/** Decrement the block count on this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_unblock( mlt_event this )
+{
+ if ( this != NULL && this->owner != NULL )
+ this->block_count --;
+}
+
+/** Close this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_close( mlt_event this )
+{
+ if ( this != NULL )
+ {
+ if ( -- this->ref_count == 1 )
+ this->owner = NULL;
+ if ( this->ref_count <= 0 )
+ {
+#ifdef _MLT_EVENT_CHECKS_
+ mlt_log( NULL, MLT_LOG_DEBUG, "Events created %d, destroyed %d\n", events_created, ++events_destroyed );
+#endif
+ free( this );
+ }
+ }
+}
+
+/* Forward declaration to private functions.
+*/
+
+static mlt_events mlt_events_fetch( mlt_properties );
+static void mlt_events_store( mlt_properties, mlt_events );
+static void mlt_events_close( mlt_events );
+
+/** Initialise the events structure.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ */
+
+void mlt_events_init( mlt_properties this )
+{
+ mlt_events events = mlt_events_fetch( this );
+ if ( events == NULL )
+ {
+ events = malloc( sizeof( struct mlt_events_struct ) );
+ events->list = mlt_properties_new( );
+ mlt_events_store( this, events );
+ }
+}
+
+/** Register an event and transmitter.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param id the name of an event
+ * \param transmitter the callback function to send an event message
+ * \return true if there was an error
+ */
+
+int mlt_events_register( mlt_properties this, const char *id, mlt_transmitter transmitter )
+{
+ int error = 1;
+ mlt_events events = mlt_events_fetch( this );
+ if ( events != NULL )
+ {
+ mlt_properties list = events->list;
+ char temp[ 128 ];
+ error = mlt_properties_set_data( list, id, transmitter, 0, NULL, NULL );
+ sprintf( temp, "list:%s", id );
+ if ( mlt_properties_get_data( list, temp, NULL ) == NULL )
+ mlt_properties_set_data( list, temp, mlt_properties_new( ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ }
+ return error;
+}
+
+/** Fire an event.
+ *
+ * This takes a variable number of arguments to supply to the listener.
+
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param id the name of an event
+ */
+
+void mlt_events_fire( mlt_properties this, const char *id, ... )
+{
+ mlt_events events = mlt_events_fetch( this );
+ if ( events != NULL )
+ {
+ int i = 0;
+ va_list alist;
+ void *args[ 10 ];
+ mlt_properties list = events->list;
+ mlt_properties listeners = NULL;
+ char temp[ 128 ];
+ mlt_transmitter transmitter = mlt_properties_get_data( list, id, NULL );
+ sprintf( temp, "list:%s", id );
+ listeners = mlt_properties_get_data( list, temp, NULL );
+
+ va_start( alist, id );
+ do
+ args[ i ] = va_arg( alist, void * );
+ while( args[ i ++ ] != NULL );
+ va_end( alist );
+
+ if ( listeners != NULL )
+ {
+ for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+ {
+ mlt_event event = mlt_properties_get_data_at( listeners, i, NULL );
+ if ( event != NULL && event->owner != NULL && event->block_count == 0 )
+ {
+ if ( transmitter != NULL )
+ transmitter( event->listener, event->owner, event->service, args );
+ else
+ event->listener( event->owner, event->service );
+ }
+ }
+ }
+ }
+}
+
+/** Register a listener.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ * \param id the name of the event to listen for
+ * \param listener the callback to receive an event message
+ * \return
+ */
+
+mlt_event mlt_events_listen( mlt_properties this, void *service, const char *id, mlt_listener listener )
+{
+ mlt_event event = NULL;
+ mlt_events events = mlt_events_fetch( this );
+ if ( events != NULL )
+ {
+ mlt_properties list = events->list;
+ mlt_properties listeners = NULL;
+ char temp[ 128 ];
+ sprintf( temp, "list:%s", id );
+ listeners = mlt_properties_get_data( list, temp, NULL );
+ if ( listeners != NULL )
+ {
+ int first_null = -1;
+ int i = 0;
+ for ( i = 0; event == NULL && i < mlt_properties_count( listeners ); i ++ )
+ {
+ mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+ if ( entry != NULL && entry->owner != NULL )
+ {
+ if ( entry->service == service && entry->listener == listener )
+ event = entry;
+ }
+ else if ( ( entry == NULL || entry->owner == NULL ) && first_null == -1 )
+ {
+ first_null = i;
+ }
+ }
+
+ if ( event == NULL )
+ {
+ event = malloc( sizeof( struct mlt_event_struct ) );
+ if ( event != NULL )
+ {
+#ifdef _MLT_EVENT_CHECKS_
+ events_created ++;
+#endif
+ sprintf( temp, "%d", first_null == -1 ? mlt_properties_count( listeners ) : first_null );
+ event->owner = events;
+ event->ref_count = 0;
+ event->block_count = 0;
+ event->listener = listener;
+ event->service = service;
+ mlt_properties_set_data( listeners, temp, event, 0, ( mlt_destructor )mlt_event_close, NULL );
+ mlt_event_inc_ref( event );
+ }
+ }
+
+ }
+ }
+ return event;
+}
+
+/** Block all events for a given service.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ */
+
+void mlt_events_block( mlt_properties this, void *service )
+{
+ mlt_events events = mlt_events_fetch( this );
+ if ( events != NULL )
+ {
+ int i = 0, j = 0;
+ mlt_properties list = events->list;
+ for ( j = 0; j < mlt_properties_count( list ); j ++ )
+ {
+ char *temp = mlt_properties_get_name( list, j );
+ if ( !strncmp( temp, "list:", 5 ) )
+ {
+ mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
+ for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+ {
+ mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+ if ( entry != NULL && entry->service == service )
+ mlt_event_block( entry );
+ }
+ }
+ }
+ }
+}
+
+/** Unblock all events for a given service.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ */
+
+void mlt_events_unblock( mlt_properties this, void *service )
+{
+ mlt_events events = mlt_events_fetch( this );
+ if ( events != NULL )
+ {
+ int i = 0, j = 0;
+ mlt_properties list = events->list;
+ for ( j = 0; j < mlt_properties_count( list ); j ++ )
+ {
+ char *temp = mlt_properties_get_name( list, j );
+ if ( !strncmp( temp, "list:", 5 ) )
+ {
+ mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
+ for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+ {
+ mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+ if ( entry != NULL && entry->service == service )
+ mlt_event_unblock( entry );
+ }
+ }
+ }
+ }
+}
+
+/** Disconnect all events for a given service.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ */
+
+void mlt_events_disconnect( mlt_properties this, void *service )
+{
+ mlt_events events = mlt_events_fetch( this );
+ if ( events != NULL )
+ {
+ int i = 0, j = 0;
+ mlt_properties list = events->list;
+ for ( j = 0; j < mlt_properties_count( list ); j ++ )
+ {
+ char *temp = mlt_properties_get_name( list, j );
+ if ( !strncmp( temp, "list:", 5 ) )
+ {
+ mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
+ for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+ {
+ mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+ char *name = mlt_properties_get_name( listeners, i );
+ if ( entry != NULL && entry->service == service )
+ mlt_properties_set_data( listeners, name, NULL, 0, NULL, NULL );
+ }
+ }
+ }
+ }
+}
+
+/** \brief private to mlt_events_struct, used by mlt_events_wait_for() */
+
+typedef struct
+{
+ int done;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+}
+condition_pair;
+
+/** The event listener callback for the wait functions.
+ *
+ * \private \memberof mlt_events_struct
+ * \param this a properties list
+ * \param pair a condition pair
+ */
+
+static void mlt_events_listen_for( mlt_properties this, condition_pair *pair )
+{
+ pthread_mutex_lock( &pair->mutex );
+ if ( pair->done == 0 )
+ {
+ pthread_cond_signal( &pair->cond );
+ pthread_mutex_unlock( &pair->mutex );
+ }
+}
+
+/** Prepare to wait for an event.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param id the name of the event to wait for
+ * \return an event
+ */
+
+mlt_event mlt_events_setup_wait_for( mlt_properties this, const char *id )
+{
+ condition_pair *pair = malloc( sizeof( condition_pair ) );
+ pair->done = 0;
+ pthread_cond_init( &pair->cond, NULL );
+ pthread_mutex_init( &pair->mutex, NULL );
+ pthread_mutex_lock( &pair->mutex );
+ return mlt_events_listen( this, pair, id, ( mlt_listener )mlt_events_listen_for );
+}
+
+/** Wait for an event.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param event an event
+ */
+
+void mlt_events_wait_for( mlt_properties this, mlt_event event )
+{
+ if ( event != NULL )
+ {
+ condition_pair *pair = event->service;
+ pthread_cond_wait( &pair->cond, &pair->mutex );
+ }
+}
+
+/** Cleanup after waiting for an event.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param event an event
+ */
+
+void mlt_events_close_wait_for( mlt_properties this, mlt_event event )
+{
+ if ( event != NULL )
+ {
+ condition_pair *pair = event->service;
+ event->owner = NULL;
+ pair->done = 0;
+ pthread_mutex_unlock( &pair->mutex );
+ pthread_mutex_destroy( &pair->mutex );
+ pthread_cond_destroy( &pair->cond );
+ }
+}
+
+/** Fetch the events object.
+ *
+ * \private \memberof mlt_events_struct
+ * \param this a properties list
+ * \return an events object
+ */
+
+static mlt_events mlt_events_fetch( mlt_properties this )
+{
+ mlt_events events = NULL;
+ if ( this != NULL )
+ events = mlt_properties_get_data( this, "_events", NULL );
+ return events;
+}
+
+/** Store the events object.
+ *
+ * \private \memberof mlt_events_struct
+ * \param this a properties list
+ * \param events an events object
+ */
+
+static void mlt_events_store( mlt_properties this, mlt_events events )
+{
+ if ( this != NULL && events != NULL )
+ mlt_properties_set_data( this, "_events", events, 0, ( mlt_destructor )mlt_events_close, NULL );
+}
+
+/** Close the events object.
+ *
+ * \private \memberof mlt_events_struct
+ * \param events an events object
+ */
+
+static void mlt_events_close( mlt_events events )
+{
+ if ( events != NULL )
+ {
+ mlt_properties_close( events->list );
+ free( events );
+ }
+}
--- /dev/null
+/**
+ * \file mlt_events.h
+ * \brief event handling
+ * \see mlt_events_struct
+ *
+ * Copyright (C) 2004-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_EVENTS_H_
+#define _MLT_EVENTS_H_
+
+#include "mlt_types.h"
+
+#if GCC_VERSION >= 40000
+typedef void ( *mlt_transmitter )( void *, ... );
+typedef void ( *mlt_listener )( void *, ... );
+#else
+/** callback function to send an event message
+ *
+ */
+typedef void ( *mlt_transmitter )( );
+/** event handler when receiving an event message
+ * \param the properties object on which the event was registered
+ * \param an opaque pointer to a service or really an object
+ * \param variable args supplied by the transmitter
+ */
+typedef void ( *mlt_listener )( );
+#endif
+
+extern void mlt_events_init( mlt_properties self );
+extern int mlt_events_register( mlt_properties self, const char *id, mlt_transmitter transmitter );
+extern void mlt_events_fire( mlt_properties self, const char *id, ... );
+extern mlt_event mlt_events_listen( mlt_properties self, void *service, const char *id, mlt_listener listener );
+extern void mlt_events_block( mlt_properties self, void *service );
+extern void mlt_events_unblock( mlt_properties self, void *service );
+extern void mlt_events_disconnect( mlt_properties self, void *service );
+
+extern mlt_event mlt_events_setup_wait_for( mlt_properties self, const char *id );
+extern void mlt_events_wait_for( mlt_properties self, mlt_event event );
+extern void mlt_events_close_wait_for( mlt_properties self, mlt_event event );
+
+extern void mlt_event_inc_ref( mlt_event self );
+extern void mlt_event_block( mlt_event self );
+extern void mlt_event_unblock( mlt_event self );
+extern void mlt_event_close( mlt_event self );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_factory.c
+ * \brief the factory method interfaces
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt.h"
+#include "mlt_repository.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** the default subdirectory of the libdir for holding modules (plugins) */
+#define PREFIX_LIB LIBDIR "/mlt"
+/** the default subdirectory of the install prefix for holding module (plugin) data */
+#define PREFIX_DATA PREFIX "/share/mlt"
+
+
+/** holds the full path to the modules directory - initialized and retained for the entire session */
+static char *mlt_directory = NULL;
+/** a global properties list for holding environment config data and things needing session-oriented cleanup */
+static mlt_properties global_properties = NULL;
+/** the global repository singleton */
+static mlt_repository repository = NULL;
+/** the events object for the factory events */
+static mlt_properties event_object = NULL;
+/** for tracking the unique_id set on each constructed service */
+static int unique_id = 0;
+
+/* Event transmitters. */
+
+/** the -create-request event transmitter
+ *
+ * \param listener
+ * \param owner
+ * \param this
+ * \param args
+ */
+
+static void mlt_factory_create_request( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( char * )args[ 0 ], ( char * )args[ 1 ], ( mlt_service * )args[ 2 ] );
+}
+
+/** the -create-done event transmitter
+ *
+ * \param listener
+ * \param owner
+ * \param this
+ * \param args
+ */
+
+static void mlt_factory_create_done( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( char * )args[ 0 ], ( char * )args[ 1 ], ( mlt_service )args[ 2 ] );
+}
+
+/** Construct the repository and factories.
+ *
+ * The environment variable MLT_PRODUCER is the name of a default producer often used by other services, defaults to "fezzil".
+ *
+ * The environment variable MLT_CONSUMER is the name of a default consumer, defaults to "sdl".
+ *
+ * The environment variable MLT_TEST_CARD is the name of a producer or file to be played when nothing is available (all tracks blank).
+ *
+ * The environment variable MLT_DATA overrides the default full path to the MLT and module supplemental data files, defaults to \p PREFIX_DATA.
+ *
+ * The environment variable MLT_PROFILE defaults to "dv_pal."
+ *
+ * The environment variable MLT_REPOSITORY overrides the default location of the plugin modules, defaults to \p PREFIX_LIB.
+ *
+ * \param directory an optional full path to a directory containing the modules that overrides the default and
+ * the MLT_REPOSITORY environment variable
+ * \return the repository
+ */
+
+mlt_repository mlt_factory_init( const char *directory )
+{
+ // Only initialise once
+ if ( mlt_directory == NULL )
+ {
+ // Allow user over rides
+ if ( directory == NULL || !strcmp( directory, "" ) )
+ directory = getenv( "MLT_REPOSITORY" );
+
+ // If no directory is specified, default to install directory
+ if ( directory == NULL )
+ directory = PREFIX_LIB;
+
+ // Store the prefix for later retrieval
+ mlt_directory = strdup( directory );
+
+ // Initialise the pool
+ mlt_pool_init( );
+
+ // Create and set up the events object
+ event_object = mlt_properties_new( );
+ mlt_events_init( event_object );
+ mlt_events_register( event_object, "producer-create-request", ( mlt_transmitter )mlt_factory_create_request );
+ mlt_events_register( event_object, "producer-create-done", ( mlt_transmitter )mlt_factory_create_done );
+ mlt_events_register( event_object, "filter-create-request", ( mlt_transmitter )mlt_factory_create_request );
+ mlt_events_register( event_object, "filter-create-done", ( mlt_transmitter )mlt_factory_create_done );
+ mlt_events_register( event_object, "transition-create-request", ( mlt_transmitter )mlt_factory_create_request );
+ mlt_events_register( event_object, "transition-create-done", ( mlt_transmitter )mlt_factory_create_done );
+ mlt_events_register( event_object, "consumer-create-request", ( mlt_transmitter )mlt_factory_create_request );
+ mlt_events_register( event_object, "consumer-create-done", ( mlt_transmitter )mlt_factory_create_done );
+
+ // Create the global properties
+ global_properties = mlt_properties_new( );
+
+ // Create the repository of services
+ repository = mlt_repository_init( directory );
+
+ // Force a clean up when app closes
+ atexit( mlt_factory_close );
+ }
+
+ // Allow property refresh on a subsequent initialisation
+ if ( global_properties != NULL )
+ {
+ mlt_properties_set_or_default( global_properties, "MLT_NORMALISATION", getenv( "MLT_NORMALISATION" ), "PAL" );
+ mlt_properties_set_or_default( global_properties, "MLT_PRODUCER", getenv( "MLT_PRODUCER" ), "fezzik" );
+ mlt_properties_set_or_default( global_properties, "MLT_CONSUMER", getenv( "MLT_CONSUMER" ), "sdl" );
+ mlt_properties_set( global_properties, "MLT_TEST_CARD", getenv( "MLT_TEST_CARD" ) );
+ mlt_properties_set_or_default( global_properties, "MLT_PROFILE", getenv( "MLT_PROFILE" ), "dv_pal" );
+ mlt_properties_set_or_default( global_properties, "MLT_DATA", getenv( "MLT_DATA" ), PREFIX_DATA );
+ }
+
+
+ return repository;
+}
+
+/** Fetch the events object.
+ *
+ * \return the global factory event object
+ */
+
+mlt_properties mlt_factory_event_object( )
+{
+ return event_object;
+}
+
+/** Fetch the module directory used in this instance.
+ *
+ * \return the full path to the module directory that this session is using
+ */
+
+const char *mlt_factory_directory( )
+{
+ return mlt_directory;
+}
+
+/** Get a value from the environment.
+ *
+ * \param name the name of a MLT (runtime configuration) environment variable
+ * \return the value of the variable
+ */
+
+char *mlt_environment( const char *name )
+{
+ if ( global_properties )
+ return mlt_properties_get( global_properties, name );
+ else
+ return NULL;
+}
+
+/** Set a value in the environment.
+ *
+ * \param name the name of a MLT environment variable
+ * \param value the value of the variable
+ * \return true on error
+ */
+
+int mlt_environment_set( const char *name, const char *value )
+{
+ if ( global_properties )
+ return mlt_properties_set( global_properties, name, value );
+ else
+ return -1;
+}
+
+/** Set some properties common to all services.
+ *
+ * This sets _unique_id, \p mlt_type, \p mlt_service (unless _mlt_service_hidden), and _profile.
+ *
+ * \param properties a service's properties list
+ * \param profile the \p mlt_profile supplied to the factory function
+ * \param type the MLT service class
+ * \param service the name of the service
+ */
+
+static void set_common_properties( mlt_properties properties, mlt_profile profile, const char *type, const char *service )
+{
+ mlt_properties_set_int( properties, "_unique_id", ++ unique_id );
+ mlt_properties_set( properties, "mlt_type", type );
+ if ( mlt_properties_get_int( properties, "_mlt_service_hidden" ) == 0 )
+ mlt_properties_set( properties, "mlt_service", service );
+ if ( profile != NULL )
+ mlt_properties_set_data( properties, "_profile", profile, 0, NULL, NULL );
+}
+
+/** Fetch a producer from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the producer (optional, defaults to MLT_PRODUCER)
+ * \param input an optional argument to the producer constructor, typically a string
+ * \return a new producer
+ */
+
+mlt_producer mlt_factory_producer( mlt_profile profile, const char *service, const void *input )
+{
+ mlt_producer obj = NULL;
+
+ // Pick up the default normalising producer if necessary
+ if ( service == NULL )
+ service = mlt_environment( "MLT_PRODUCER" );
+
+ // Offer the application the chance to 'create'
+ mlt_events_fire( event_object, "producer-create-request", service, input, &obj, NULL );
+
+ // Try to instantiate via the specified service
+ if ( obj == NULL )
+ {
+ obj = mlt_repository_create( repository, profile, producer_type, service, input );
+ mlt_events_fire( event_object, "producer-create-done", service, input, obj, NULL );
+ if ( obj != NULL )
+ {
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( obj );
+ set_common_properties( properties, profile, "producer", service );
+ }
+ }
+ return obj;
+}
+
+/** Fetch a filter from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the filter
+ * \param input an optional argument to the filter constructor, typically a string
+ * \return a new filter
+ */
+
+mlt_filter mlt_factory_filter( mlt_profile profile, const char *service, const void *input )
+{
+ mlt_filter obj = NULL;
+
+ // Offer the application the chance to 'create'
+ mlt_events_fire( event_object, "filter-create-request", service, input, &obj, NULL );
+
+ if ( obj == NULL )
+ {
+ obj = mlt_repository_create( repository, profile, filter_type, service, input );
+ mlt_events_fire( event_object, "filter-create-done", service, input, obj, NULL );
+ }
+
+ if ( obj != NULL )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( obj );
+ set_common_properties( properties, profile, "filter", service );
+ }
+ return obj;
+}
+
+/** Fetch a transition from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the transition
+ * \param input an optional argument to the transition constructor, typically a string
+ * \return a new transition
+ */
+
+mlt_transition mlt_factory_transition( mlt_profile profile, const char *service, const void *input )
+{
+ mlt_transition obj = NULL;
+
+ // Offer the application the chance to 'create'
+ mlt_events_fire( event_object, "transition-create-request", service, input, &obj, NULL );
+
+ if ( obj == NULL )
+ {
+ obj = mlt_repository_create( repository, profile, transition_type, service, input );
+ mlt_events_fire( event_object, "transition-create-done", service, input, obj, NULL );
+ }
+
+ if ( obj != NULL )
+ {
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( obj );
+ set_common_properties( properties, profile, "transition", service );
+ }
+ return obj;
+}
+
+/** Fetch a consumer from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the consumer (optional, defaults to MLT_CONSUMER)
+ * \param input an optional argument to the consumer constructor, typically a string
+ * \return a new consumer
+ */
+
+mlt_consumer mlt_factory_consumer( mlt_profile profile, const char *service, const void *input )
+{
+ mlt_consumer obj = NULL;
+
+ if ( service == NULL )
+ service = mlt_environment( "MLT_CONSUMER" );
+
+ // Offer the application the chance to 'create'
+ mlt_events_fire( event_object, "consumer-create-request", service, input, &obj, NULL );
+
+ if ( obj == NULL )
+ {
+ obj = mlt_repository_create( repository, profile, consumer_type, service, input );
+ mlt_events_fire( event_object, "consumer-create-done", service, input, obj, NULL );
+ }
+
+ if ( obj != NULL )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( obj );
+ set_common_properties( properties, profile, "consumer", service );
+ }
+ return obj;
+}
+
+/** Register an object for clean up.
+ *
+ * \param ptr an opaque pointer to anything allocated on the heap
+ * \param destructor the function pointer of the deallocation subroutine (e.g., free or \p mlt_pool_release)
+ */
+
+void mlt_factory_register_for_clean_up( void *ptr, mlt_destructor destructor )
+{
+ char unique[ 256 ];
+ sprintf( unique, "%08d", mlt_properties_count( global_properties ) );
+ mlt_properties_set_data( global_properties, unique, ptr, 0, destructor, NULL );
+}
+
+/** Close the factory.
+ *
+ * Cleanup all resources for the session.
+ */
+
+void mlt_factory_close( )
+{
+ if ( mlt_directory != NULL )
+ {
+ mlt_properties_close( event_object );
+ mlt_properties_close( global_properties );
+ mlt_repository_close( repository );
+ free( mlt_directory );
+ mlt_directory = NULL;
+ mlt_pool_close( );
+ }
+}
+
+mlt_properties mlt_global_properties( )
+{
+ return global_properties;
+}
--- /dev/null
+/**
+ * \file mlt_factory.h
+ * \brief the factory method interfaces
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_FACTORY_H
+#define _MLT_FACTORY_H
+
+#include "mlt_types.h"
+#include "mlt_profile.h"
+#include "mlt_repository.h"
+
+/**
+ * \event \em producer-create-request fired when mlt_factory_producer is called
+ * \event \em producer-create-done fired when a producer registers itself
+ * \event \em filter-create-request fired when mlt_factory_filter is called
+ * \event \em filter-create-done fired when a filter registers itself
+ * \event \em transition-create-request fired when mlt_factory_transition is called
+ * \event \em transition-create-done fired when a transition registers itself
+ * \event \em consumer-create-request fired when mlt_factory_consumer is called
+ * \event \em consumer-create-done fired when a consumer registers itself
+ */
+
+extern mlt_repository mlt_factory_init( const char *directory );
+extern const char *mlt_factory_directory( );
+extern char *mlt_environment( const char *name );
+extern int mlt_environment_set( const char *name, const char *value );
+extern mlt_properties mlt_factory_event_object( );
+extern mlt_producer mlt_factory_producer( mlt_profile profile, const char *name, const void *input );
+extern mlt_filter mlt_factory_filter( mlt_profile profile, const char *name, const void *input );
+extern mlt_transition mlt_factory_transition( mlt_profile profile, const char *name, const void *input );
+extern mlt_consumer mlt_factory_consumer( mlt_profile profile, const char *name, const void *input );
+extern void mlt_factory_register_for_clean_up( void *ptr, mlt_destructor destructor );
+extern void mlt_factory_close( );
+extern mlt_properties mlt_global_properties( );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_field.c
+ * \brief a field for planting multiple transitions and filters
+ * \see mlt_field_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_field.h"
+#include "mlt_service.h"
+#include "mlt_filter.h"
+#include "mlt_transition.h"
+#include "mlt_multitrack.h"
+#include "mlt_tractor.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief Field class
+ *
+ * The field is a convenience class that works with the tractor and multitrack classes to manage track filters and transitions.
+ */
+
+struct mlt_field_s
+{
+ /// This is the producer we're connected to
+ mlt_service producer;
+
+ /// Multitrack
+ mlt_multitrack multitrack;
+
+ /// Tractor
+ mlt_tractor tractor;
+};
+
+/** Construct a field, mulitrack, and tractor.
+ *
+ * \public \memberof mlt_field_s
+ * \return a new field
+ */
+
+mlt_field mlt_field_init( )
+{
+ // Initialise the field
+ mlt_field this = calloc( sizeof( struct mlt_field_s ), 1 );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Construct a multitrack
+ this->multitrack = mlt_multitrack_init( );
+
+ // Construct a tractor
+ this->tractor = mlt_tractor_init( );
+
+ // The first plant will be connected to the mulitrack
+ this->producer = MLT_MULTITRACK_SERVICE( this->multitrack );
+
+ // Connect the tractor to the multitrack
+ mlt_tractor_connect( this->tractor, this->producer );
+ }
+
+ // Return this
+ return this;
+}
+
+/** Construct a field and initialize with supplied multitrack and tractor.
+ *
+ * \public \memberof mlt_field_s
+ * \param multitrack a multitrack
+ * \param tractor a tractor
+ * \return a new field
+ */
+
+mlt_field mlt_field_new( mlt_multitrack multitrack, mlt_tractor tractor )
+{
+ // Initialise the field
+ mlt_field this = calloc( sizeof( struct mlt_field_s ), 1 );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Construct a multitrack
+ this->multitrack = multitrack;
+
+ // Construct a tractor
+ this->tractor = tractor;
+
+ // The first plant will be connected to the mulitrack
+ this->producer = MLT_MULTITRACK_SERVICE( this->multitrack );
+
+ // Connect the tractor to the multitrack
+ mlt_tractor_connect( this->tractor, this->producer );
+ }
+
+ // Return this
+ return this;
+}
+
+/** Get the service associated to this field.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return the tractor as a service
+ */
+
+mlt_service mlt_field_service( mlt_field this )
+{
+ return MLT_TRACTOR_SERVICE( this->tractor );
+}
+
+/** Get the multitrack.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return the multitrack
+ */
+
+mlt_multitrack mlt_field_multitrack( mlt_field this )
+{
+ return this != NULL ? this->multitrack : NULL;
+}
+
+/** Get the tractor.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return the tractor
+ */
+
+mlt_tractor mlt_field_tractor( mlt_field this )
+{
+ return this != NULL ? this->tractor : NULL;
+}
+
+/** Get the properties associated to this field.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return a properties list
+ */
+
+mlt_properties mlt_field_properties( mlt_field this )
+{
+ return MLT_SERVICE_PROPERTIES( mlt_field_service( this ) );
+}
+
+/** Plant a filter.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \param that a filter
+ * \param track the track index
+ * \return true if there was an error
+ */
+
+int mlt_field_plant_filter( mlt_field this, mlt_filter that, int track )
+{
+ // Connect the filter to the last producer
+ int result = mlt_filter_connect( that, this->producer, track );
+
+ // If sucessful, then we'll use this for connecting in the future
+ if ( result == 0 )
+ {
+ // This is now the new producer
+ this->producer = MLT_FILTER_SERVICE( that );
+
+ // Reconnect tractor to new producer
+ mlt_tractor_connect( this->tractor, this->producer );
+
+ // Fire an event
+ mlt_events_fire( mlt_field_properties( this ), "service-changed", NULL );
+ }
+
+ return result;
+}
+
+/** Plant a transition.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \param that a transition
+ * \param a_track input A's track index
+ * \param b_track input B's track index
+ * \return true if there was an error
+ */
+
+int mlt_field_plant_transition( mlt_field this, mlt_transition that, int a_track, int b_track )
+{
+ // Connect the transition to the last producer
+ int result = mlt_transition_connect( that, this->producer, a_track, b_track );
+
+ // If sucessful, then we'll use this for connecting in the future
+ if ( result == 0 )
+ {
+ // This is now the new producer
+ this->producer = MLT_TRANSITION_SERVICE( that );
+
+ // Reconnect tractor to new producer
+ mlt_tractor_connect( this->tractor, this->producer );
+
+ // Fire an event
+ mlt_events_fire( mlt_field_properties( this ), "service-changed", NULL );
+ }
+
+ return 0;
+}
+
+/** Close the field.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ */
+
+void mlt_field_close( mlt_field this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( mlt_field_properties( this ) ) <= 0 )
+ {
+ //mlt_tractor_close( this->tractor );
+ //mlt_multitrack_close( this->multitrack );
+ free( this );
+ }
+}
+
+/** Remove a filter or transition from the field.
+ *
+ * \public \memberof mlt_field_s
+ * \param self a field
+ * \param service the filter or transition to remove
+ */
+
+void mlt_field_disconnect_service( mlt_field self, mlt_service service )
+{
+ mlt_service p = mlt_service_producer( service );
+ mlt_service c = mlt_service_consumer( service);
+ int i;
+ switch ( mlt_service_identify(c) )
+ {
+ case filter_type:
+ i = mlt_filter_get_track( MLT_FILTER(c) );
+ mlt_service_connect_producer( c, p, i );
+ break;
+ case transition_type:
+ i = mlt_transition_get_a_track ( MLT_TRANSITION(c) );
+ mlt_service_connect_producer( c, p, i );
+ MLT_TRANSITION(c)->producer = p;
+ break;
+ case tractor_type:
+ self->producer = p;
+ mlt_tractor_connect( MLT_TRACTOR(c), p );
+ default:
+ break;
+ }
+ mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL );
+}
--- /dev/null
+/**
+ * \file mlt_field.h
+ * \brief a field for planting multiple transitions and services
+ * \see mlt_field_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_FIELD_H_
+#define _MLT_FIELD_H_
+
+#include "mlt_types.h"
+
+extern mlt_field mlt_field_init( );
+extern mlt_field mlt_field_new( mlt_multitrack multitrack, mlt_tractor tractor );
+extern mlt_service mlt_field_service( mlt_field self );
+extern mlt_tractor mlt_field_tractor( mlt_field self );
+extern mlt_multitrack mlt_field_multitrack( mlt_field self );
+extern mlt_properties mlt_field_properties( mlt_field self );
+extern int mlt_field_plant_filter( mlt_field self, mlt_filter that, int track );
+extern int mlt_field_plant_transition( mlt_field self, mlt_transition that, int a_track, int b_track );
+extern void mlt_field_close( mlt_field self );
+extern void mlt_field_disconnect_service( mlt_field self, mlt_service service );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_filter.c
+ * \brief abstraction for all filter services
+ * \see mlt_filter_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_filter.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int filter_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** Initialize a new filter.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param child the object of a subclass
+ * \return true if there was an error
+ */
+
+int mlt_filter_init( mlt_filter this, void *child )
+{
+ mlt_service service = &this->parent;
+ memset( this, 0, sizeof( struct mlt_filter_s ) );
+ this->child = child;
+ if ( mlt_service_init( service, this ) == 0 )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ // Override the get_frame method
+ service->get_frame = filter_get_frame;
+
+ // Define the destructor
+ service->close = ( mlt_destructor )mlt_filter_close;
+ service->close_object = this;
+
+ // Default in, out, track properties
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", 0 );
+ mlt_properties_set_int( properties, "track", 0 );
+
+ return 0;
+ }
+ return 1;
+}
+
+/** Create a new filter and initialize it.
+ *
+ * \public \memberof mlt_filter_s
+ * \return a new filter
+ */
+
+mlt_filter mlt_filter_new( )
+{
+ mlt_filter this = calloc( 1, sizeof( struct mlt_filter_s ) );
+ if ( this != NULL )
+ mlt_filter_init( this, NULL );
+ return this;
+}
+
+/** Get the service class interface.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the service parent class
+ * \see MLT_FILTER_SERVICE
+ */
+
+mlt_service mlt_filter_service( mlt_filter this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the filter properties.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the properties list for the filter
+ * \see MLT_FILTER_PROPERTIES
+ */
+
+mlt_properties mlt_filter_properties( mlt_filter this )
+{
+ return MLT_SERVICE_PROPERTIES( MLT_FILTER_SERVICE( this ) );
+}
+
+/** Connect this filter to a producers track. Note that a filter only operates
+ * on a single track, and by default it operates on the entirety of that track.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param producer the producer to which to connect this filter
+ * \param index which of potentially multiple producers to this service (0 based)
+ */
+
+int mlt_filter_connect( mlt_filter this, mlt_service producer, int index )
+{
+ int ret = mlt_service_connect_producer( &this->parent, producer, index );
+
+ // If the connection was successful, grab the producer, track and reset in/out
+ if ( ret == 0 )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", 0 );
+ mlt_properties_set_int( properties, "track", index );
+ }
+
+ return ret;
+}
+
+/** Set the starting and ending time.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param in the time relative to the producer at which start applying the filter
+ * \param out the time relative to the producer at which to stop applying the filter
+ */
+
+
+void mlt_filter_set_in_and_out( mlt_filter this, mlt_position in, mlt_position out )
+{
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+ mlt_properties_set_position( properties, "in", in );
+ mlt_properties_set_position( properties, "out", out );
+}
+
+/** Return the track that this filter is operating on.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return true on error
+ */
+
+
+int mlt_filter_get_track( mlt_filter this )
+{
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+ return mlt_properties_get_int( properties, "track" );
+}
+
+/** Get the in point.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the start time for the filter relative to the producer
+ */
+
+
+mlt_position mlt_filter_get_in( mlt_filter this )
+{
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+ return mlt_properties_get_position( properties, "in" );
+}
+
+/** Get the out point.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the ending time for the filter relative to the producer
+ */
+
+
+mlt_position mlt_filter_get_out( mlt_filter this )
+{
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+ return mlt_properties_get_position( properties, "out" );
+}
+
+/** Process the frame.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param frame a frame
+ * \return a frame
+ */
+
+
+mlt_frame mlt_filter_process( mlt_filter this, mlt_frame frame )
+{
+ int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "disable" );
+ if ( disable || this->process == NULL )
+ return frame;
+ else
+ return this->process( this, frame );
+}
+
+/** Get a frame from this filter.
+ *
+ * \private \memberof mlt_filter_s
+ * \param service a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the producer
+ * \return true on error
+ */
+
+
+static int filter_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
+{
+ mlt_filter this = service->child;
+
+ // Get coords in/out/track
+ int track = mlt_filter_get_track( this );
+ int in = mlt_filter_get_in( this );
+ int out = mlt_filter_get_out( this );
+
+ // Get the producer this is connected to
+ mlt_service producer = mlt_service_producer( &this->parent );
+
+ // If the frame request is for this filters track, we need to process it
+ if ( index == track || track == -1 )
+ {
+ int ret = mlt_service_get_frame( producer, frame, index );
+ if ( ret == 0 )
+ {
+ mlt_position position = mlt_frame_get_position( *frame );
+ if ( position >= in && ( out == 0 || position <= out ) )
+ *frame = mlt_filter_process( this, *frame );
+ return 0;
+ }
+ else
+ {
+ *frame = mlt_frame_init( service );
+ return 0;
+ }
+ }
+ else
+ {
+ return mlt_service_get_frame( producer, frame, index );
+ }
+}
+
+/** Close and destroy the filter.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ */
+
+
+void mlt_filter_close( mlt_filter this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_FILTER_PROPERTIES( this ) ) <= 0 )
+ {
+ if ( this->close != NULL )
+ {
+ this->close( this );
+ }
+ else
+ {
+ this->parent.close = NULL;
+ mlt_service_close( &this->parent );
+ }
+ free( this );
+ }
+}
--- /dev/null
+/**
+ * \file mlt_filter.h
+ * \brief abstraction for all filter services
+ * \see mlt_filter_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_FILTER_H_
+#define _MLT_FILTER_H_
+
+#include "mlt_service.h"
+
+/** \brief Filter abstract service class
+ *
+ * A filter is a service that may modify the output of a single producer.
+ *
+ * \extends mlt_service_s
+ * \properties \em track the index of the track of a multitrack on which the filter is applied
+ */
+
+struct mlt_filter_s
+{
+ /** We're implementing service here */
+ struct mlt_service_s parent;
+
+ /** public virtual */
+ void ( *close )( mlt_filter );
+
+ /** protected filter method */
+ mlt_frame ( *process )( mlt_filter, mlt_frame );
+
+ /** Protected */
+ void *child;
+};
+
+#define MLT_FILTER_SERVICE( filter ) ( &( filter )->parent )
+#define MLT_FILTER_PROPERTIES( filter ) MLT_SERVICE_PROPERTIES( MLT_FILTER_SERVICE( filter ) )
+
+extern int mlt_filter_init( mlt_filter self, void *child );
+extern mlt_filter mlt_filter_new( );
+extern mlt_service mlt_filter_service( mlt_filter self );
+extern mlt_properties mlt_filter_properties( mlt_filter self );
+extern mlt_frame mlt_filter_process( mlt_filter self, mlt_frame that );
+extern int mlt_filter_connect( mlt_filter self, mlt_service producer, int index );
+extern void mlt_filter_set_in_and_out( mlt_filter self, mlt_position in, mlt_position out );
+extern int mlt_filter_get_track( mlt_filter self );
+extern mlt_position mlt_filter_get_in( mlt_filter self );
+extern mlt_position mlt_filter_get_out( mlt_filter self );
+extern void mlt_filter_close( mlt_filter );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_frame.c
+ * \brief interface for all frame classes
+ * \see mlt_frame_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_frame.h"
+#include "mlt_producer.h"
+#include "mlt_factory.h"
+#include "mlt_profile.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/** Constructor for a frame.
+*/
+
+mlt_frame mlt_frame_init( mlt_service service )
+{
+ // Allocate a frame
+ mlt_frame this = calloc( sizeof( struct mlt_frame_s ), 1 );
+
+ if ( this != NULL )
+ {
+ mlt_profile profile = mlt_service_profile( service );
+
+ // Initialise the properties
+ mlt_properties properties = &this->parent;
+ mlt_properties_init( properties, this );
+
+ // Set default properties on the frame
+ mlt_properties_set_position( properties, "_position", 0.0 );
+ mlt_properties_set_data( properties, "image", NULL, 0, NULL, NULL );
+ mlt_properties_set_int( properties, "width", profile? profile->width : 720 );
+ mlt_properties_set_int( properties, "height", profile? profile->height : 576 );
+ mlt_properties_set_int( properties, "normalised_width", profile? profile->width : 720 );
+ mlt_properties_set_int( properties, "normalised_height", profile? profile->height : 576 );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( NULL ) );
+ mlt_properties_set_data( properties, "audio", NULL, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "alpha", NULL, 0, NULL, NULL );
+
+ // Construct stacks for frames and methods
+ this->stack_image = mlt_deque_init( );
+ this->stack_audio = mlt_deque_init( );
+ this->stack_service = mlt_deque_init( );
+ }
+
+ return this;
+}
+
+/** Fetch the frames properties.
+*/
+
+mlt_properties mlt_frame_properties( mlt_frame this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Check if we have a way to derive something other than a test card.
+*/
+
+int mlt_frame_is_test_card( mlt_frame this )
+{
+ return mlt_deque_count( this->stack_image ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_image" );
+}
+
+/** Check if we have a way to derive something other than test audio.
+*/
+
+int mlt_frame_is_test_audio( mlt_frame this )
+{
+ return mlt_deque_count( this->stack_audio ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_audio" );
+}
+
+/** Get the aspect ratio of the frame.
+*/
+
+double mlt_frame_get_aspect_ratio( mlt_frame this )
+{
+ return mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "aspect_ratio" );
+}
+
+/** Set the aspect ratio of the frame.
+*/
+
+int mlt_frame_set_aspect_ratio( mlt_frame this, double value )
+{
+ return mlt_properties_set_double( MLT_FRAME_PROPERTIES( this ), "aspect_ratio", value );
+}
+
+/** Get the position of this frame.
+*/
+
+mlt_position mlt_frame_get_position( mlt_frame this )
+{
+ int pos = mlt_properties_get_position( MLT_FRAME_PROPERTIES( this ), "_position" );
+ return pos < 0 ? 0 : pos;
+}
+
+/** Set the position of this frame.
+*/
+
+int mlt_frame_set_position( mlt_frame this, mlt_position value )
+{
+ return mlt_properties_set_position( MLT_FRAME_PROPERTIES( this ), "_position", value );
+}
+
+/** Stack a get_image callback.
+*/
+
+int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image )
+{
+ return mlt_deque_push_back( this->stack_image, get_image );
+}
+
+/** Pop a get_image callback.
+*/
+
+mlt_get_image mlt_frame_pop_get_image( mlt_frame this )
+{
+ return mlt_deque_pop_back( this->stack_image );
+}
+
+/** Push a frame.
+*/
+
+int mlt_frame_push_frame( mlt_frame this, mlt_frame that )
+{
+ return mlt_deque_push_back( this->stack_image, that );
+}
+
+/** Pop a frame.
+*/
+
+mlt_frame mlt_frame_pop_frame( mlt_frame this )
+{
+ return mlt_deque_pop_back( this->stack_image );
+}
+
+/** Push a service.
+*/
+
+int mlt_frame_push_service( mlt_frame this, void *that )
+{
+ return mlt_deque_push_back( this->stack_image, that );
+}
+
+/** Pop a service.
+*/
+
+void *mlt_frame_pop_service( mlt_frame this )
+{
+ return mlt_deque_pop_back( this->stack_image );
+}
+
+/** Push a service.
+*/
+
+int mlt_frame_push_service_int( mlt_frame this, int that )
+{
+ return mlt_deque_push_back_int( this->stack_image, that );
+}
+
+/** Pop a service.
+*/
+
+int mlt_frame_pop_service_int( mlt_frame this )
+{
+ return mlt_deque_pop_back_int( this->stack_image );
+}
+
+/** Push an audio item on the stack.
+*/
+
+int mlt_frame_push_audio( mlt_frame this, void *that )
+{
+ return mlt_deque_push_back( this->stack_audio, that );
+}
+
+/** Pop an audio item from the stack
+*/
+
+void *mlt_frame_pop_audio( mlt_frame this )
+{
+ return mlt_deque_pop_back( this->stack_audio );
+}
+
+/** Return the service stack
+*/
+
+mlt_deque mlt_frame_service_stack( mlt_frame this )
+{
+ return this->stack_service;
+}
+
+/** Replace image stack with the information provided.
+
+ This might prove to be unreliable and restrictive - the idea is that a transition
+ which normally uses two images may decide to only use the b frame (ie: in the case
+ of a composite where the b frame completely obscures the a frame).
+
+ The image must be writable and the destructor for the image itself must be taken
+ care of on another frame and that frame cannot have a replace applied to it...
+ Further it assumes that no alpha mask is in use.
+
+ For these reasons, it can only be used in a specific situation - when you have
+ multiple tracks each with their own transition and these transitions are applied
+ in a strictly reversed order (ie: highest numbered [lowest track] is processed
+ first).
+
+ More reliable approach - the cases should be detected during the process phase
+ and the upper tracks should simply not be invited to stack...
+*/
+
+void mlt_frame_replace_image( mlt_frame this, uint8_t *image, mlt_image_format format, int width, int height )
+{
+ // Remove all items from the stack
+ while( mlt_deque_pop_back( this->stack_image ) ) ;
+
+ // Update the information
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", image, 0, NULL, NULL );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "width", width );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "height", height );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "format", format );
+ this->get_alpha_mask = NULL;
+}
+
+/** Get the image associated to the frame.
+*/
+
+int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+ mlt_get_image get_image = mlt_frame_pop_get_image( this );
+ mlt_producer producer = mlt_properties_get_data( properties, "test_card_producer", NULL );
+ int error = 0;
+
+ if ( get_image != NULL )
+ {
+ mlt_properties_set_int( properties, "image_count", mlt_properties_get_int( properties, "image_count" ) - 1 );
+ mlt_position position = mlt_frame_get_position( this );
+ error = get_image( this, buffer, format, width, height, writable );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+ mlt_properties_set_int( properties, "format", *format );
+ mlt_frame_set_position( this, position );
+ }
+ else if ( mlt_properties_get_data( properties, "image", NULL ) != NULL )
+ {
+ *format = mlt_properties_get_int( properties, "format" );
+ *buffer = mlt_properties_get_data( properties, "image", NULL );
+ *width = mlt_properties_get_int( properties, "width" );
+ *height = mlt_properties_get_int( properties, "height" );
+ }
+ else if ( producer != NULL )
+ {
+ mlt_frame test_frame = NULL;
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &test_frame, 0 );
+ if ( test_frame != NULL )
+ {
+ mlt_properties test_properties = MLT_FRAME_PROPERTIES( test_frame );
+ mlt_properties_set_double( test_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
+ mlt_properties_set( test_properties, "rescale.interp", mlt_properties_get( properties, "rescale.interp" ) );
+ mlt_frame_get_image( test_frame, buffer, format, width, height, writable );
+ mlt_properties_set_data( properties, "test_card_frame", test_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, NULL, NULL );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+ mlt_properties_set_int( properties, "format", *format );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_frame_get_aspect_ratio( test_frame ) );
+ }
+ else
+ {
+ mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
+ mlt_frame_get_image( this, buffer, format, width, height, writable );
+ }
+ }
+ else
+ {
+ register uint8_t *p;
+ register uint8_t *q;
+ int size = 0;
+
+ *width = *width == 0 ? 720 : *width;
+ *height = *height == 0 ? 576 : *height;
+ size = *width * *height;
+
+ mlt_properties_set_int( properties, "format", *format );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+ mlt_properties_set_int( properties, "aspect_ratio", 0 );
+
+ switch( *format )
+ {
+ case mlt_image_none:
+ size = 0;
+ *buffer = NULL;
+ break;
+ case mlt_image_rgb24:
+ size *= 3;
+ size += *width * 3;
+ *buffer = mlt_pool_alloc( size );
+ if ( *buffer )
+ memset( *buffer, 255, size );
+ break;
+ case mlt_image_rgb24a:
+ case mlt_image_opengl:
+ size *= 4;
+ size += *width * 4;
+ *buffer = mlt_pool_alloc( size );
+ if ( *buffer )
+ memset( *buffer, 255, size );
+ break;
+ case mlt_image_yuv422:
+ size *= 2;
+ size += *width * 2;
+ *buffer = mlt_pool_alloc( size );
+ p = *buffer;
+ q = p + size;
+ while ( p != NULL && p != q )
+ {
+ *p ++ = 235;
+ *p ++ = 128;
+ }
+ break;
+ case mlt_image_yuv420p:
+ size = size * 3 / 2;
+ *buffer = mlt_pool_alloc( size );
+ if ( *buffer )
+ memset( *buffer, 255, size );
+ break;
+ }
+
+ mlt_properties_set_data( properties, "image", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "test_image", 1 );
+ }
+
+ mlt_properties_set_int( properties, "scaled_width", *width );
+ mlt_properties_set_int( properties, "scaled_height", *height );
+
+ return error;
+}
+
+uint8_t *mlt_frame_get_alpha_mask( mlt_frame this )
+{
+ uint8_t *alpha = NULL;
+ if ( this != NULL )
+ {
+ if ( this->get_alpha_mask != NULL )
+ alpha = this->get_alpha_mask( this );
+ if ( alpha == NULL )
+ alpha = mlt_properties_get_data( &this->parent, "alpha", NULL );
+ if ( alpha == NULL )
+ {
+ int size = mlt_properties_get_int( &this->parent, "scaled_width" ) * mlt_properties_get_int( &this->parent, "scaled_height" );
+ alpha = mlt_pool_alloc( size );
+ memset( alpha, 255, size );
+ mlt_properties_set_data( &this->parent, "alpha", alpha, size, mlt_pool_release, NULL );
+ }
+ }
+ return alpha;
+}
+
+int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ mlt_get_audio get_audio = mlt_frame_pop_audio( this );
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+ int hide = mlt_properties_get_int( properties, "test_audio" );
+
+ if ( hide == 0 && get_audio != NULL )
+ {
+ mlt_position position = mlt_frame_get_position( this );
+ get_audio( this, buffer, format, frequency, channels, samples );
+ mlt_frame_set_position( this, position );
+ }
+ else if ( mlt_properties_get_data( properties, "audio", NULL ) )
+ {
+ *buffer = mlt_properties_get_data( properties, "audio", NULL );
+ *frequency = mlt_properties_get_int( properties, "audio_frequency" );
+ *channels = mlt_properties_get_int( properties, "audio_channels" );
+ *samples = mlt_properties_get_int( properties, "audio_samples" );
+ }
+ else
+ {
+ int size = 0;
+ *samples = *samples <= 0 ? 1920 : *samples;
+ *channels = *channels <= 0 ? 2 : *channels;
+ *frequency = *frequency <= 0 ? 48000 : *frequency;
+ size = *samples * *channels * sizeof( int16_t );
+ *buffer = mlt_pool_alloc( size );
+ if ( *buffer != NULL )
+ memset( *buffer, 0, size );
+ mlt_properties_set_data( properties, "audio", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "test_audio", 1 );
+ }
+
+ mlt_properties_set_int( properties, "audio_frequency", *frequency );
+ mlt_properties_set_int( properties, "audio_channels", *channels );
+ mlt_properties_set_int( properties, "audio_samples", *samples );
+
+ if ( mlt_properties_get( properties, "meta.volume" ) )
+ {
+ double value = mlt_properties_get_double( properties, "meta.volume" );
+
+ if ( value == 0.0 )
+ {
+ memset( *buffer, 0, *samples * *channels * 2 );
+ }
+ else if ( value != 1.0 )
+ {
+ int total = *samples * *channels;
+ int16_t *p = *buffer;
+ while ( total -- )
+ {
+ *p = *p * value;
+ p ++;
+ }
+ }
+
+ mlt_properties_set( properties, "meta.volume", NULL );
+ }
+
+ return 0;
+}
+
+unsigned char *mlt_frame_get_waveform( mlt_frame this, int w, int h )
+{
+ int16_t *pcm = NULL;
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+ mlt_audio_format format = mlt_audio_pcm;
+ int frequency = 32000; // lower frequency available?
+ int channels = 2;
+ double fps = mlt_profile_fps( NULL );
+ int samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( this ) );
+
+ // Get the pcm data
+ mlt_frame_get_audio( this, &pcm, &format, &frequency, &channels, &samples );
+
+ // Make an 8-bit buffer large enough to hold rendering
+ int size = w * h;
+ unsigned char *bitmap = ( unsigned char* )mlt_pool_alloc( size );
+ if ( bitmap != NULL )
+ memset( bitmap, 0, size );
+ mlt_properties_set_data( properties, "waveform", bitmap, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Render vertical lines
+ int16_t *ubound = pcm + samples * channels;
+ int skip = samples / w - 1;
+ int i, j, k;
+
+ // Iterate sample stream and along x coordinate
+ for ( i = 0; i < w && pcm < ubound; i++ )
+ {
+ // pcm data has channels interleaved
+ for ( j = 0; j < channels; j++ )
+ {
+ // Determine sample's magnitude from 2s complement;
+ int pcm_magnitude = *pcm < 0 ? ~(*pcm) + 1 : *pcm;
+ // The height of a line is the ratio of the magnitude multiplied by
+ // half the vertical resolution
+ int height = ( int )( ( double )( pcm_magnitude ) / 32768 * h / 2 );
+ // Determine the starting y coordinate - left channel above center,
+ // right channel below - currently assumes 2 channels
+ int displacement = ( h / 2 ) - ( 1 - j ) * height;
+ // Position buffer pointer using y coordinate, stride, and x coordinate
+ unsigned char *p = &bitmap[ i + displacement * w ];
+
+ // Draw vertical line
+ for ( k = 0; k < height; k++ )
+ p[ w * k ] = 0xFF;
+
+ pcm++;
+ }
+ pcm += skip * channels;
+ }
+
+ return bitmap;
+}
+
+mlt_producer mlt_frame_get_original_producer( mlt_frame this )
+{
+ if ( this != NULL )
+ return mlt_properties_get_data( MLT_FRAME_PROPERTIES( this ), "_producer", NULL );
+ return NULL;
+}
+
+void mlt_frame_close( mlt_frame this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( this ) ) <= 0 )
+ {
+ mlt_deque_close( this->stack_image );
+ mlt_deque_close( this->stack_audio );
+ while( mlt_deque_peek_back( this->stack_service ) )
+ mlt_service_close( mlt_deque_pop_back( this->stack_service ) );
+ mlt_deque_close( this->stack_service );
+ mlt_properties_close( &this->parent );
+ free( this );
+ }
+}
+
+/***** convenience functions *****/
+
+int mlt_convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, unsigned int total )
+{
+ int ret = 0;
+ int yy, uu, vv;
+ int r,g,b;
+ total /= 2;
+ while (total--)
+ {
+ yy = yuv[0];
+ uu = yuv[1];
+ vv = yuv[3];
+ YUV2RGB(yy, uu, vv, r, g, b);
+ rgba[0] = r;
+ rgba[1] = g;
+ rgba[2] = b;
+ rgba[3] = 255;
+ yy = yuv[2];
+ YUV2RGB(yy, uu, vv, r, g, b);
+ rgba[4] = r;
+ rgba[5] = g;
+ rgba[6] = b;
+ rgba[7] = 255;
+ yuv += 4;
+ rgba += 8;
+ }
+ return ret;
+}
+
+int mlt_convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha )
+{
+ int ret = 0;
+ register int y0, y1, u0, u1, v0, v1;
+ register int r, g, b;
+ register uint8_t *d = yuv;
+ register int i, j;
+
+ if ( alpha )
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgba + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ *alpha++ = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ *alpha++ = *s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ *alpha++ = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ else
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgba + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+
+ return ret;
+}
+
+int mlt_convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv )
+{
+ int ret = 0;
+ register int y0, y1, u0, u1, v0, v1;
+ register int r, g, b;
+ register uint8_t *d = yuv;
+ register int i, j;
+
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgb + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ return ret;
+}
+
+int mlt_convert_bgr24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha )
+{
+ int ret = 0;
+ register int y0, y1, u0, u1, v0, v1;
+ register int r, g, b;
+ register uint8_t *d = yuv;
+ register int i, j;
+
+ if ( alpha )
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgba + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ *alpha++ = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ *alpha++ = *s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ *alpha++ = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ else
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgba + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ return ret;
+}
+
+int mlt_convert_bgr24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv )
+{
+ int ret = 0;
+ register int y0, y1, u0, u1, v0, v1;
+ register int r, g, b;
+ register uint8_t *d = yuv;
+ register int i, j;
+
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgb + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ b = *s++;
+ g = *s++;
+ r = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ return ret;
+}
+
+int mlt_convert_argb_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha )
+{
+ int ret = 0;
+ register int y0, y1, u0, u1, v0, v1;
+ register int r, g, b;
+ register uint8_t *d = yuv;
+ register int i, j;
+
+ if ( alpha )
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgba + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ *alpha++ = *s++;
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *alpha++ = *s++;
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ *alpha++ = *s++;
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ else
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *s = rgba + ( stride * i );
+ for ( j = 0; j < ( width / 2 ); j++ )
+ {
+ s++;
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ s++;
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y1, u1 , v1);
+ *d++ = y0;
+ *d++ = (u0+u1) >> 1;
+ *d++ = y1;
+ *d++ = (v0+v1) >> 1;
+ }
+ if ( width % 2 )
+ {
+ s++;
+ r = *s++;
+ g = *s++;
+ b = *s++;
+ RGB2YUV (r, g, b, y0, u0 , v0);
+ *d++ = y0;
+ *d++ = u0;
+ }
+ }
+ return ret;
+}
+
+int mlt_convert_yuv420p_to_yuv422( uint8_t *yuv420p, int width, int height, int stride, uint8_t *yuv )
+{
+ int ret = 0;
+ register int i, j;
+
+ int half = width >> 1;
+
+ uint8_t *Y = yuv420p;
+ uint8_t *U = Y + width * height;
+ uint8_t *V = U + width * height / 4;
+
+ register uint8_t *d = yuv;
+
+ for ( i = 0; i < height; i++ )
+ {
+ register uint8_t *u = U + ( i / 2 ) * ( half );
+ register uint8_t *v = V + ( i / 2 ) * ( half );
+
+ for ( j = 0; j < half; j++ )
+ {
+ *d ++ = *Y ++;
+ *d ++ = *u ++;
+ *d ++ = *Y ++;
+ *d ++ = *v ++;
+ }
+ }
+ return ret;
+}
+
+uint8_t *mlt_resize_alpha( uint8_t *input, int owidth, int oheight, int iwidth, int iheight, uint8_t alpha_value )
+{
+ uint8_t *output = NULL;
+
+ if ( input != NULL && ( iwidth != owidth || iheight != oheight ) && ( owidth > 6 && oheight > 6 ) )
+ {
+ uint8_t *out_line;
+ int offset_x = ( owidth - iwidth ) / 2;
+ int offset_y = ( oheight - iheight ) / 2;
+ int iused = iwidth;
+
+ output = mlt_pool_alloc( owidth * oheight );
+ memset( output, alpha_value, owidth * oheight );
+
+ offset_x -= offset_x % 2;
+
+ out_line = output + offset_y * owidth;
+ out_line += offset_x;
+
+ // Loop for the entirety of our output height.
+ while ( iheight -- )
+ {
+ // We're in the input range for this row.
+ memcpy( out_line, input, iused );
+
+ // Move to next input line
+ input += iwidth;
+
+ // Move to next output line
+ out_line += owidth;
+ }
+ }
+
+ return output;
+}
+
+void mlt_resize_yuv422( uint8_t *output, int owidth, int oheight, uint8_t *input, int iwidth, int iheight )
+{
+ // Calculate strides
+ int istride = iwidth * 2;
+ int ostride = owidth * 2;
+ int offset_x = ( owidth - iwidth );
+ int offset_y = ( oheight - iheight ) / 2;
+ uint8_t *in_line = input;
+ uint8_t *out_line;
+ int size = owidth * oheight;
+ uint8_t *p = output;
+
+ // Optimisation point
+ if ( output == NULL || input == NULL || ( owidth <= 6 || oheight <= 6 || iwidth <= 6 || oheight <= 6 ) )
+ {
+ return;
+ }
+ else if ( iwidth == owidth && iheight == oheight )
+ {
+ memcpy( output, input, iheight * istride );
+ return;
+ }
+
+ while( size -- )
+ {
+ *p ++ = 16;
+ *p ++ = 128;
+ }
+
+ offset_x -= offset_x % 4;
+
+ out_line = output + offset_y * ostride;
+ out_line += offset_x;
+
+ // Loop for the entirety of our output height.
+ while ( iheight -- )
+ {
+ // We're in the input range for this row.
+ memcpy( out_line, in_line, iwidth * 2 );
+
+ // Move to next input line
+ in_line += istride;
+
+ // Move to next output line
+ out_line += ostride;
+ }
+}
+
+/** A resizing function for yuv422 frames - this does not rescale, but simply
+ resizes. It assumes yuv422 images available on the frame so use with care.
+*/
+
+uint8_t *mlt_frame_resize_yuv422( mlt_frame this, int owidth, int oheight )
+{
+ // Get properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the input image, width and height
+ uint8_t *input = mlt_properties_get_data( properties, "image", NULL );
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+
+ int iwidth = mlt_properties_get_int( properties, "width" );
+ int iheight = mlt_properties_get_int( properties, "height" );
+
+ // If width and height are correct, don't do anything
+ if ( iwidth != owidth || iheight != oheight )
+ {
+ uint8_t alpha_value = mlt_properties_get_int( properties, "resize_alpha" );
+
+ // Create the output image
+ uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+ // Call the generic resize
+ mlt_resize_yuv422( output, owidth, oheight, input, iwidth, iheight );
+
+ // Now update the frame
+ mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+
+ // We should resize the alpha too
+ alpha = mlt_resize_alpha( alpha, owidth, oheight, iwidth, iheight, alpha_value );
+ if ( alpha != NULL )
+ {
+ mlt_properties_set_data( properties, "alpha", alpha, owidth * oheight, ( mlt_destructor )mlt_pool_release, NULL );
+ this->get_alpha_mask = NULL;
+ }
+
+ // Return the output
+ return output;
+ }
+ // No change, return input
+ return input;
+}
+
+/** A rescaling function for yuv422 frames - low quality, and provided for testing
+ only. It assumes yuv422 images available on the frame so use with care.
+*/
+
+uint8_t *mlt_frame_rescale_yuv422( mlt_frame this, int owidth, int oheight )
+{
+ // Get properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the input image, width and height
+ uint8_t *input = mlt_properties_get_data( properties, "image", NULL );
+ int iwidth = mlt_properties_get_int( properties, "width" );
+ int iheight = mlt_properties_get_int( properties, "height" );
+
+ // If width and height are correct, don't do anything
+ if ( iwidth != owidth || iheight != oheight )
+ {
+ // Create the output image
+ uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+ // Calculate strides
+ int istride = iwidth * 2;
+ int ostride = owidth * 2;
+
+ iwidth = iwidth - ( iwidth % 4 );
+
+ // Derived coordinates
+ int dy, dx;
+
+ // Calculate ranges
+ int out_x_range = owidth / 2;
+ int out_y_range = oheight / 2;
+ int in_x_range = iwidth / 2;
+ int in_y_range = iheight / 2;
+
+ // Output pointers
+ register uint8_t *out_line = output;
+ register uint8_t *out_ptr;
+
+ // Calculate a middle pointer
+ uint8_t *in_middle = input + istride * in_y_range + in_x_range * 2;
+ uint8_t *in_line;
+
+ // Generate the affine transform scaling values
+ register int scale_width = ( iwidth << 16 ) / owidth;
+ register int scale_height = ( iheight << 16 ) / oheight;
+ register int base = 0;
+
+ int outer = out_x_range * scale_width;
+ int bottom = out_y_range * scale_height;
+
+ // Loop for the entirety of our output height.
+ for ( dy = - bottom; dy < bottom; dy += scale_height )
+ {
+ // Start at the beginning of the line
+ out_ptr = out_line;
+
+ // Pointer to the middle of the input line
+ in_line = in_middle + ( dy >> 16 ) * istride;
+
+ // Loop for the entirety of our output row.
+ for ( dx = - outer; dx < outer; dx += scale_width )
+ {
+ base = dx >> 15;
+ base &= 0xfffffffe;
+ *out_ptr ++ = *( in_line + base );
+ base &= 0xfffffffc;
+ *out_ptr ++ = *( in_line + base + 1 );
+ dx += scale_width;
+ base = dx >> 15;
+ base &= 0xfffffffe;
+ *out_ptr ++ = *( in_line + base );
+ base &= 0xfffffffc;
+ *out_ptr ++ = *( in_line + base + 3 );
+ }
+
+ // Move to next output line
+ out_line += ostride;
+ }
+
+ // Now update the frame
+ mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+
+ // Return the output
+ return output;
+ }
+
+ // No change, return input
+ return input;
+}
+
+int mlt_frame_mix_audio( mlt_frame this, mlt_frame that, float weight_start, float weight_end, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ int ret = 0;
+ int16_t *src, *dest;
+ int frequency_src = *frequency, frequency_dest = *frequency;
+ int channels_src = *channels, channels_dest = *channels;
+ int samples_src = *samples, samples_dest = *samples;
+ int i, j;
+ double d = 0, s = 0;
+
+ mlt_frame_get_audio( that, &src, format, &frequency_src, &channels_src, &samples_src );
+ mlt_frame_get_audio( this, &dest, format, &frequency_dest, &channels_dest, &samples_dest );
+
+ int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 );
+ if ( silent )
+ memset( dest, 0, samples_dest * channels_dest * sizeof( int16_t ) );
+
+ silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( that ), "silent_audio" );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( that ), "silent_audio", 0 );
+ if ( silent )
+ memset( src, 0, samples_src * channels_src * sizeof( int16_t ) );
+
+ if ( channels_src > 6 )
+ channels_src = 0;
+ if ( channels_dest > 6 )
+ channels_dest = 0;
+ if ( samples_src > 4000 )
+ samples_src = 0;
+ if ( samples_dest > 4000 )
+ samples_dest = 0;
+
+ // determine number of samples to process
+ *samples = samples_src < samples_dest ? samples_src : samples_dest;
+ *channels = channels_src < channels_dest ? channels_src : channels_dest;
+ *buffer = dest;
+ *frequency = frequency_dest;
+
+ // Compute a smooth ramp over start to end
+ float weight = weight_start;
+ float weight_step = ( weight_end - weight_start ) / *samples;
+
+ if ( src == dest )
+ {
+ *samples = samples_src;
+ *channels = channels_src;
+ *buffer = src;
+ *frequency = frequency_src;
+ return ret;
+ }
+
+ // Mixdown
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ if ( j < channels_dest )
+ d = (double) dest[ i * channels_dest + j ];
+ if ( j < channels_src )
+ s = (double) src[ i * channels_src + j ];
+ dest[ i * channels_dest + j ] = s * weight + d * ( 1.0 - weight );
+ }
+ weight += weight_step;
+ }
+
+ return ret;
+}
+
+// Replacement for broken mlt_frame_audio_mix - this filter uses an inline low pass filter
+// to allow mixing without volume hacking
+int mlt_frame_combine_audio( mlt_frame this, mlt_frame that, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ int ret = 0;
+ int16_t *src, *dest;
+ int frequency_src = *frequency, frequency_dest = *frequency;
+ int channels_src = *channels, channels_dest = *channels;
+ int samples_src = *samples, samples_dest = *samples;
+ int i, j;
+ double vp[ 6 ];
+ double b_weight = 1.0;
+
+ if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "meta.mixdown" ) )
+ b_weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "meta.volume" );
+
+ mlt_frame_get_audio( that, &src, format, &frequency_src, &channels_src, &samples_src );
+ mlt_frame_get_audio( this, &dest, format, &frequency_dest, &channels_dest, &samples_dest );
+
+ int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 );
+ if ( silent )
+ memset( dest, 0, samples_dest * channels_dest * sizeof( int16_t ) );
+
+ silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( that ), "silent_audio" );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( that ), "silent_audio", 0 );
+ if ( silent )
+ memset( src, 0, samples_src * channels_src * sizeof( int16_t ) );
+
+ if ( src == dest )
+ {
+ *samples = samples_src;
+ *channels = channels_src;
+ *buffer = src;
+ *frequency = frequency_src;
+ return ret;
+ }
+
+ // determine number of samples to process
+ *samples = samples_src < samples_dest ? samples_src : samples_dest;
+ *channels = channels_src < channels_dest ? channels_src : channels_dest;
+ *buffer = dest;
+ *frequency = frequency_dest;
+
+ for ( j = 0; j < *channels; j++ )
+ vp[ j ] = ( double )dest[ j ];
+
+ double Fc = 0.5;
+ double B = exp(-2.0 * M_PI * Fc);
+ double A = 1.0 - B;
+ double v;
+
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ v = ( double )( b_weight * dest[ i * channels_dest + j ] + src[ i * channels_src + j ] );
+ v = v < -32767 ? -32767 : v > 32768 ? 32768 : v;
+ vp[ j ] = dest[ i * channels_dest + j ] = ( int16_t )( v * A + vp[ j ] * B );
+ }
+ }
+
+ return ret;
+}
+
+/* Will this break when mlt_position is converted to double? -Zach */
+int mlt_sample_calculator( float fps, int frequency, int64_t position )
+{
+ int samples = 0;
+
+ if ( ( int )( fps * 100 ) == 2997 )
+ {
+ samples = frequency / 30;
+
+ switch ( frequency )
+ {
+ case 48000:
+ if ( position % 5 != 0 )
+ samples += 2;
+ break;
+ case 44100:
+ if ( position % 300 == 0 )
+ samples = 1471;
+ else if ( position % 30 == 0 )
+ samples = 1470;
+ else if ( position % 2 == 0 )
+ samples = 1472;
+ else
+ samples = 1471;
+ break;
+ case 32000:
+ if ( position % 30 == 0 )
+ samples = 1068;
+ else if ( position % 29 == 0 )
+ samples = 1067;
+ else if ( position % 4 == 2 )
+ samples = 1067;
+ else
+ samples = 1068;
+ break;
+ default:
+ samples = 0;
+ }
+ }
+ else if ( fps != 0 )
+ {
+ samples = frequency / fps;
+ }
+
+ return samples;
+}
+
+int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t frame )
+{
+ int64_t samples = 0;
+
+ // TODO: Correct rules for NTSC and drop the * 100 hack
+ if ( ( int )( fps * 100 ) == 2997 )
+ {
+ samples = ( ( double )( frame * frequency ) / 30 );
+ switch( frequency )
+ {
+ case 48000:
+ samples += 2 * ( frame / 5 );
+ break;
+ case 44100:
+ samples += frame + ( frame / 2 ) - ( frame / 30 ) + ( frame / 300 );
+ break;
+ case 32000:
+ samples += ( 2 * frame ) - ( frame / 4 ) - ( frame / 29 );
+ break;
+ }
+ }
+ else if ( fps != 0 )
+ {
+ samples = ( ( frame * frequency ) / ( int )fps );
+ }
+
+ return samples;
+}
--- /dev/null
+/**
+ * \file mlt_frame.h
+ * \brief interface for all frame classes
+ * \see mlt_frame_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_FRAME_H_
+#define _MLT_FRAME_H_
+
+#include "mlt_properties.h"
+#include "mlt_deque.h"
+#include "mlt_service.h"
+
+/** callback function to get video data
+ *
+ */
+
+typedef int ( *mlt_get_image )( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable );
+
+/** callback function to get audio data
+ *
+ */
+
+typedef int ( *mlt_get_audio )( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+
+/** \brief Frame class
+ *
+ * \properties \em test_image set if the frame holds a "test card" image
+ * \properties \em test_audio set if the frame holds "test card" audio
+ * \properties \em _producer holds a reference to the frame's end producer
+ * \properties \em _speed the current speed of the producer that generated the frame
+ * \properties \em _position the position of the frame
+ * \properties \em meta.* holds metadata
+ * \properties \em hide set to 1 to hide the video, 2 to mute the audio
+ * \properties \em last_track a flag to indicate an end-of-tracks frame
+ */
+
+struct mlt_frame_s
+{
+ /* We're extending properties here */
+ struct mlt_properties_s parent;
+
+ /* Virtual methods */
+ uint8_t * ( *get_alpha_mask )( mlt_frame self );
+
+ /* Private properties */
+ mlt_deque stack_image;
+ mlt_deque stack_audio;
+ mlt_deque stack_service;
+};
+
+#define MLT_FRAME_PROPERTIES( frame ) ( &( frame )->parent )
+#define MLT_FRAME_SERVICE_STACK( frame ) ( ( frame )->stack_service )
+#define MLT_FRAME_IMAGE_STACK( frame ) ( ( frame )->stack_image )
+#define MLT_FRAME_AUDIO_STACK( frame ) ( ( frame )->stack_audio )
+
+extern mlt_frame mlt_frame_init( mlt_service service );
+extern mlt_properties mlt_frame_properties( mlt_frame self );
+extern int mlt_frame_is_test_card( mlt_frame self );
+extern int mlt_frame_is_test_audio( mlt_frame self );
+extern double mlt_frame_get_aspect_ratio( mlt_frame self );
+extern int mlt_frame_set_aspect_ratio( mlt_frame self, double value );
+extern mlt_position mlt_frame_get_position( mlt_frame self );
+extern int mlt_frame_set_position( mlt_frame self, mlt_position value );
+extern void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height );
+extern int mlt_frame_get_image( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable );
+extern uint8_t *mlt_frame_get_alpha_mask( mlt_frame self );
+extern int mlt_frame_get_audio( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+extern unsigned char *mlt_frame_get_waveform( mlt_frame self, int w, int h );
+extern int mlt_frame_push_get_image( mlt_frame self, mlt_get_image get_image );
+extern mlt_get_image mlt_frame_pop_get_image( mlt_frame self );
+extern int mlt_frame_push_frame( mlt_frame self, mlt_frame that );
+extern mlt_frame mlt_frame_pop_frame( mlt_frame self );
+extern int mlt_frame_push_service( mlt_frame self, void *that );
+extern void *mlt_frame_pop_service( mlt_frame self );
+extern int mlt_frame_push_service_int( mlt_frame self, int that );
+extern int mlt_frame_pop_service_int( mlt_frame self );
+extern int mlt_frame_push_audio( mlt_frame self, void *that );
+extern void *mlt_frame_pop_audio( mlt_frame self );
+extern mlt_deque mlt_frame_service_stack( mlt_frame self );
+extern mlt_producer mlt_frame_get_original_producer( mlt_frame self );
+extern void mlt_frame_close( mlt_frame self );
+
+/* convenience functions */
+extern int mlt_convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, unsigned int total );
+extern int mlt_convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha );
+extern int mlt_convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv );
+extern int mlt_convert_bgr24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha );
+extern int mlt_convert_argb_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha );
+extern int mlt_convert_bgr24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv );
+extern int mlt_convert_yuv420p_to_yuv422( uint8_t *yuv420p, int width, int height, int stride, uint8_t *yuv );
+extern uint8_t *mlt_frame_resize_yuv422( mlt_frame self, int owidth, int oheight );
+extern uint8_t *mlt_frame_rescale_yuv422( mlt_frame self, int owidth, int oheight );
+extern void mlt_resize_yuv422( uint8_t *output, int owidth, int oheight, uint8_t *input, int iwidth, int iheight );
+extern int mlt_frame_mix_audio( mlt_frame self, mlt_frame that, float weight_start, float weight_end, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+extern int mlt_frame_combine_audio( mlt_frame self, mlt_frame that, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+extern int mlt_sample_calculator( float fps, int frequency, int64_t position );
+extern int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t position );
+
+/** This macro scales rgb into the yuv gamut - y is scaled by 219/255 and uv by 224/255. */
+#define RGB2YUV(r, g, b, y, u, v)\
+ y = ((263*r + 516*g + 100*b) >> 10) + 16;\
+ u = ((-152*r - 298*g + 450*b) >> 10) + 128;\
+ v = ((450*r - 377*g - 73*b) >> 10) + 128;
+
+/** This macro assumes the user has already scaled their rgb down into the broadcast limits. **/
+#define RGB2YUV_UNSCALED(r, g, b, y, u, v)\
+ y = (299*r + 587*g + 114*b) >> 10;\
+ u = ((-169*r - 331*g + 500*b) >> 10) + 128;\
+ v = ((500*r - 419*g - 81*b) >> 10) + 128;\
+ y = y < 16 ? 16 : y;\
+ u = u < 16 ? 16 : u;\
+ v = v < 16 ? 16 : v;\
+ y = y > 235 ? 235 : y;\
+ u = u > 240 ? 240 : u;\
+ v = v > 240 ? 240 : v
+
+/** This macro converts a YUV value to the RGB color space. */
+#define YUV2RGB( y, u, v, r, g, b ) \
+ r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \
+ g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 400 * ( u - 128 ) ) >> 10 ); \
+ b = ((1192 * ( y - 16 ) + 2066 * ( u - 128 ) ) >> 10 ); \
+ r = r < 0 ? 0 : r > 255 ? 255 : r; \
+ g = g < 0 ? 0 : g > 255 ? 255 : g; \
+ b = b < 0 ? 0 : b > 255 ? 255 : b;
+
+#endif
--- /dev/null
+/*
+ * mlt_geometry.c -- provides the geometry API
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_geometry.h"
+#include "mlt_tokeniser.h"
+#include "mlt_factory.h"
+#include "mlt_profile.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+typedef struct geometry_item_s
+{
+ struct mlt_geometry_item_s data;
+ struct geometry_item_s *next, *prev;
+}
+*geometry_item;
+
+typedef struct
+{
+ char *data;
+ int length;
+ int nw;
+ int nh;
+ geometry_item item;
+}
+geometry_s, *geometry;
+
+// Create a new geometry structure
+mlt_geometry mlt_geometry_init( )
+{
+ mlt_geometry this = calloc( 1, sizeof( struct mlt_geometry_s ) );
+ if ( this != NULL )
+ {
+ this->local = calloc( 1, sizeof( geometry_s ) );
+ if ( this->local != NULL )
+ {
+ geometry self = this->local;
+ self->nw = 720;
+ self->nh = 576;
+ }
+ else
+ {
+ free( this );
+ this = NULL;
+ }
+ }
+ return this;
+}
+
+/** A linear step
+*/
+
+static inline double linearstep( double start, double end, double position, int length )
+{
+ double o = ( end - start ) / length;
+ return start + position * o;
+}
+
+static void mlt_geometry_virtual_refresh( mlt_geometry this )
+{
+ geometry self = this->local;
+
+ // Parse of all items to ensure unspecified keys are calculated correctly
+ if ( self->item != NULL )
+ {
+ int i = 0;
+ for ( i = 0; i < 5; i ++ )
+ {
+ geometry_item current = self->item;
+ while( current != NULL )
+ {
+ int fixed = current->data.f[ i ];
+ if ( !fixed )
+ {
+ geometry_item prev = current->prev;
+ geometry_item next = current->next;
+
+ double prev_value = 0;
+ double next_value = 0;
+ double value = 0;
+
+ while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
+ while( next != NULL && !next->data.f[ i ] ) next = next->next;
+
+ switch( i )
+ {
+ case 0:
+ if ( prev ) prev_value = prev->data.x;
+ if ( next ) next_value = next->data.x;
+ break;
+ case 1:
+ if ( prev ) prev_value = prev->data.y;
+ if ( next ) next_value = next->data.y;
+ break;
+ case 2:
+ if ( prev ) prev_value = prev->data.w;
+ if ( next ) next_value = next->data.w;
+ break;
+ case 3:
+ if ( prev ) prev_value = prev->data.h;
+ if ( next ) next_value = next->data.h;
+ break;
+ case 4:
+ if ( prev ) prev_value = prev->data.mix;
+ if ( next ) next_value = next->data.mix;
+ break;
+ }
+
+ // This should never happen
+ if ( prev == NULL )
+ current->data.f[ i ] = 1;
+ else if ( next == NULL )
+ value = prev_value;
+ else
+ value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
+
+ switch( i )
+ {
+ case 0: current->data.x = value; break;
+ case 1: current->data.y = value; break;
+ case 2: current->data.w = value; break;
+ case 3: current->data.h = value; break;
+ case 4: current->data.mix = value; break;
+ }
+ }
+
+ // Move to the next item
+ current = current->next;
+ }
+ }
+ }
+}
+
+static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
+{
+ geometry self = this->local;
+
+ if ( item == self->item )
+ {
+ self->item = item->next;
+ if ( self->item != NULL )
+ self->item->prev = NULL;
+ // To ensure correct seeding, ensure all values are fixed
+ if ( self->item != NULL )
+ {
+ self->item->data.f[0] = 1;
+ self->item->data.f[1] = 1;
+ self->item->data.f[2] = 1;
+ self->item->data.f[3] = 1;
+ self->item->data.f[4] = 1;
+ }
+ }
+ else if ( item->next != NULL && item->prev != NULL )
+ {
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+ }
+ else if ( item->next != NULL )
+ {
+ item->next->prev = item->prev;
+ }
+ else if ( item->prev != NULL )
+ {
+ item->prev->next = item->next;
+ }
+
+ free( item );
+
+ return 0;
+}
+
+static void mlt_geometry_clean( mlt_geometry this )
+{
+ geometry self = this->local;
+ free( self->data );
+ self->data = NULL;
+ while( self->item )
+ mlt_geometry_drop( this, self->item );
+}
+
+// Parse the geometry specification for a given length and normalised width/height (-1 for default)
+// data is constructed as: [frame=]X,Y:WxH[:mix][;[frame=]X,Y:WxH[:mix]]*
+// and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
+int mlt_geometry_parse( mlt_geometry this, char *data, int length, int nw, int nh )
+{
+ int i = 0;
+
+ // Create a tokeniser
+ mlt_tokeniser tokens = mlt_tokeniser_init( );
+
+ // Get the local/private structure
+ geometry self = this->local;
+
+ // Clean the existing geometry
+ mlt_geometry_clean( this );
+
+ // Update the info on the data
+ if ( length != -1 )
+ self->length = length;
+ if ( nw != -1 )
+ self->nw = nw;
+ if ( nh != -1 )
+ self->nh = nh;
+ if ( data != NULL )
+ self->data = strdup( data );
+
+ // Tokenise
+ if ( data != NULL )
+ mlt_tokeniser_parse_new( tokens, data, ";" );
+
+ // Iterate through each token
+ for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
+ {
+ struct mlt_geometry_item_s item;
+ char *value = mlt_tokeniser_get_string( tokens, i );
+
+ // Set item to 0
+ memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
+
+ // Now parse the item
+ mlt_geometry_parse_item( this, &item, value );
+
+ // Now insert into place
+ mlt_geometry_insert( this, &item );
+ }
+
+ // Remove the tokeniser
+ mlt_tokeniser_close( tokens );
+
+ // ???
+ return 0;
+}
+
+// Conditionally refresh in case of a change
+int mlt_geometry_refresh( mlt_geometry this, char *data, int length, int nw, int nh )
+{
+ geometry self = this->local;
+ int changed = ( length != -1 && length != self->length );
+ changed = changed || ( nw != -1 && nw != self->nw );
+ changed = changed || ( nh != -1 && nh != self->nh );
+ changed = changed || ( data != NULL && ( self->data == NULL || strcmp( data, self->data ) ) );
+ if ( changed )
+ return mlt_geometry_parse( this, data, length, nw, nh );
+ return -1;
+}
+
+int mlt_geometry_get_length( mlt_geometry this )
+{
+ // Get the local/private structure
+ geometry self = this->local;
+
+ // return the length
+ return self->length;
+}
+
+void mlt_geometry_set_length( mlt_geometry this, int length )
+{
+ // Get the local/private structure
+ geometry self = this->local;
+
+ // set the length
+ self->length = length;
+}
+
+int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *value )
+{
+ int ret = 0;
+
+ // Get the local/private structure
+ geometry self = this->local;
+
+ if ( value != NULL && strcmp( value, "" ) )
+ {
+ char *p = strchr( value, '=' );
+ int count = 0;
+ double temp;
+
+ // Determine if a position has been specified
+ if ( p != NULL )
+ {
+ temp = atof( value );
+ if ( temp > -1 && temp < 1 )
+ item->frame = temp * self->length;
+ else
+ item->frame = temp;
+ value = p + 1;
+ }
+
+ // Special case - frame < 0
+ if ( item->frame < 0 )
+ item->frame += self->length;
+
+ // Obtain the current value at this position - this allows new
+ // frames to be created which don't specify all values
+ mlt_geometry_fetch( this, item, item->frame );
+
+ // Special case - when an empty string is specified, all values are fixed
+ // TODO: Check if this is logical - it's convenient, but it's also odd...
+ if ( !*value )
+ {
+ item->f[0] = 1;
+ item->f[1] = 1;
+ item->f[2] = 1;
+ item->f[3] = 1;
+ item->f[4] = 1;
+ }
+
+ // Iterate through the remainder of value
+ while( *value )
+ {
+ // Get the value
+ temp = strtod( value, &p );
+
+ // Check if a value was specified
+ if ( p != value )
+ {
+ // Handle the % case
+ if ( *p == '%' )
+ {
+ if ( count == 0 || count == 2 )
+ temp *= self->nw / 100.0;
+ else if ( count == 1 || count == 3 )
+ temp *= self->nh / 100.0;
+ p ++;
+ }
+
+ // Special case - distort token
+ if ( *p == '!' || *p == '*' )
+ {
+ p ++;
+ item->distort = 1;
+ }
+
+ // Actually, we don't care about the delimiter at all..
+ if ( *p ) p ++;
+
+ // Assign to the item
+ switch( count )
+ {
+ case 0: item->x = temp; item->f[0] = 1; break;
+ case 1: item->y = temp; item->f[1] = 1; break;
+ case 2: item->w = temp; item->f[2] = 1; break;
+ case 3: item->h = temp; item->f[3] = 1; break;
+ case 4: item->mix = temp; item->f[4] = 1; break;
+ }
+ }
+ else
+ {
+ p ++;
+ }
+
+ // Update the value pointer
+ value = p;
+ count ++;
+ }
+ }
+ else
+ {
+ ret = 1;
+ }
+
+ return ret;
+}
+
+// Fetch a geometry item for an absolute position
+int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float position )
+{
+ // Get the local geometry
+ geometry self = this->local;
+
+ // Need to find the nearest key to the position specifed
+ geometry_item key = self->item;
+
+ // Iterate through the keys until we reach last or have
+ while( key != NULL && key->next != NULL && position >= key->next->data.frame )
+ key = key->next;
+
+ if ( key != NULL )
+ {
+ // Position is situated before the first key - all zeroes
+ if ( position < key->data.frame )
+ {
+ memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+ item->mix = 100;
+ }
+ // Position is a key itself - no iterpolation need
+ else if ( position == key->data.frame )
+ {
+ memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
+ }
+ // Position is after the last key - no interpolation, but not a key frame
+ else if ( key->next == NULL )
+ {
+ memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
+ item->key = 0;
+ item->f[ 0 ] = 0;
+ item->f[ 1 ] = 0;
+ item->f[ 2 ] = 0;
+ item->f[ 3 ] = 0;
+ item->f[ 4 ] = 0;
+ }
+ // Interpolation is needed - position > key and there is a following key
+ else
+ {
+ item->key = 0;
+ item->frame = position;
+ position -= key->data.frame;
+ item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
+ item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
+ item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
+ item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
+ item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
+ item->distort = key->data.distort;
+ position += key->data.frame;
+ }
+
+ item->frame = position;
+ }
+ else
+ {
+ memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+ item->frame = position;
+ item->mix = 100;
+ }
+
+ return key == NULL;
+}
+
+// Specify a geometry item at an absolute position
+int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
+{
+ // Get the local/private geometry structure
+ geometry self = this->local;
+
+ // Create a new local item (this may be removed if a key already exists at this position)
+ geometry_item new = calloc( 1, sizeof( struct geometry_item_s ) );
+ memcpy( &new->data, item, sizeof( struct mlt_geometry_item_s ) );
+ new->data.key = 1;
+
+ // Determine if we need to insert or append to the list, or if it's a new list
+ if ( self->item != NULL )
+ {
+ // Get the first item
+ geometry_item place = self->item;
+
+ // Locate an existing nearby item
+ while ( place->next != NULL && item->frame > place->data.frame )
+ place = place->next;
+
+ if ( item->frame < place->data.frame )
+ {
+ if ( place == self->item )
+ self->item = new;
+ if ( place->prev )
+ place->prev->next = new;
+ new->next = place;
+ new->prev = place->prev;
+ place->prev = new;
+ }
+ else if ( item->frame > place->data.frame )
+ {
+ if ( place->next )
+ place->next->prev = new;
+ new->next = place->next;
+ new->prev = place;
+ place->next = new;
+ }
+ else
+ {
+ memcpy( &place->data, &new->data, sizeof( struct mlt_geometry_item_s ) );
+ free( new );
+ }
+ }
+ else
+ {
+ // Set the first item
+ self->item = new;
+
+ // To ensure correct seeding, ensure all values are fixed
+ self->item->data.f[0] = 1;
+ self->item->data.f[1] = 1;
+ self->item->data.f[2] = 1;
+ self->item->data.f[3] = 1;
+ self->item->data.f[4] = 1;
+ }
+
+ // Refresh all geometries
+ mlt_geometry_virtual_refresh( this );
+
+ // TODO: Error checking
+ return 0;
+}
+
+// Remove the key at the specified position
+int mlt_geometry_remove( mlt_geometry this, int position )
+{
+ int ret = 1;
+
+ // Get the local/private geometry structure
+ geometry self = this->local;
+
+ // Get the first item
+ geometry_item place = self->item;
+
+ while( place != NULL && position != place->data.frame )
+ place = place->next;
+
+ if ( place != NULL && position == place->data.frame )
+ ret = mlt_geometry_drop( this, place );
+
+ // Refresh all geometries
+ mlt_geometry_virtual_refresh( this );
+
+ return ret;
+}
+
+// Get the key at the position or the next following
+int mlt_geometry_next_key( mlt_geometry this, mlt_geometry_item item, int position )
+{
+ // Get the local/private geometry structure
+ geometry self = this->local;
+
+ // Get the first item
+ geometry_item place = self->item;
+
+ while( place != NULL && position > place->data.frame )
+ place = place->next;
+
+ if ( place != NULL )
+ memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
+
+ return place == NULL;
+}
+
+// Get the key at the position or the previous key
+int mlt_geometry_prev_key( mlt_geometry this, mlt_geometry_item item, int position )
+{
+ // Get the local/private geometry structure
+ geometry self = this->local;
+
+ // Get the first item
+ geometry_item place = self->item;
+
+ while( place != NULL && place->next != NULL && position >= place->next->data.frame )
+ place = place->next;
+
+ if ( place != NULL )
+ memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
+
+ return place == NULL;
+}
+
+char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
+{
+ geometry self = this->local;
+ struct mlt_geometry_item_s item;
+ char *ret = malloc( 1000 );
+ int used = 0;
+ int size = 1000;
+
+ if ( in == -1 )
+ in = 0;
+ if ( out == -1 )
+ out = mlt_geometry_get_length( this );
+
+ if ( ret != NULL )
+ {
+ char temp[ 100 ];
+
+ strcpy( ret, "" );
+
+ item.frame = in;
+
+ while( 1 )
+ {
+ strcpy( temp, "" );
+
+ // If it's the first frame, then it's not necessarily a key
+ if ( item.frame == in )
+ {
+ if ( mlt_geometry_fetch( this, &item, item.frame ) )
+ break;
+
+ // If the first key is larger than the current position
+ // then do nothing here
+ if ( self->item->data.frame > item.frame )
+ {
+ item.frame ++;
+ continue;
+ }
+
+ // To ensure correct seeding, ensure all values are fixed
+ item.f[0] = 1;
+ item.f[1] = 1;
+ item.f[2] = 1;
+ item.f[3] = 1;
+ item.f[4] = 1;
+ }
+ // Typically, we move from key to key
+ else if ( item.frame < out )
+ {
+ if ( mlt_geometry_next_key( this, &item, item.frame ) )
+ break;
+
+ // Special case - crop at the out point
+ if ( item.frame > out )
+ mlt_geometry_fetch( this, &item, out );
+ }
+ // We've handled the last key
+ else
+ {
+ break;
+ }
+
+ if ( item.frame - in != 0 )
+ sprintf( temp, "%d=", item.frame - in );
+
+ if ( item.f[0] )
+ sprintf( temp + strlen( temp ), "%.0f", item.x );
+ strcat( temp, "," );
+ if ( item.f[1] )
+ sprintf( temp + strlen( temp ), "%.0f", item.y );
+ strcat( temp, ":" );
+ if ( item.f[2] )
+ sprintf( temp + strlen( temp ), "%.0f", item.w );
+ strcat( temp, "x" );
+ if ( item.f[3] )
+ sprintf( temp + strlen( temp ), "%.0f", item.h );
+ if ( item.f[4] )
+ sprintf( temp + strlen( temp ), ":%.0f", item.mix );
+
+ if ( used + strlen( temp ) > size )
+ {
+ size += 1000;
+ ret = realloc( ret, size );
+ }
+
+ if ( ret != NULL && used != 0 )
+ {
+ used ++;
+ strcat( ret, ";" );
+ }
+ if ( ret != NULL )
+ {
+ used += strlen( temp );
+ strcat( ret, temp );
+ }
+
+ item.frame ++;
+ }
+ }
+
+ return ret;
+}
+
+// Serialise the current geometry
+char *mlt_geometry_serialise( mlt_geometry this )
+{
+ geometry self = this->local;
+ char *ret = mlt_geometry_serialise_cut( this, 0, self->length );
+ if ( ret )
+ {
+ free( self->data );
+ self->data = ret;
+ }
+ return ret;
+}
+
+// Close the geometry
+void mlt_geometry_close( mlt_geometry this )
+{
+ if ( this != NULL )
+ {
+ mlt_geometry_clean( this );
+ free( this->local );
+ free( this );
+ }
+}
+
+
--- /dev/null
+/*
+ * mlt_geometry.h -- provides the geometry API
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_GEOMETRY_H
+#define _MLT_GEOMETRY_H
+
+#include "mlt_types.h"
+
+struct mlt_geometry_item_s
+{
+ /* Will be 1 when this is a key frame */
+ int key;
+ /* The actual frame this corresponds to */
+ int frame;
+ /* Distort */
+ int distort;
+ /* x,y are upper left */
+ float x, y, w, h, mix;
+ /* Indicates which values are fixed */
+ int f[ 5 ];
+};
+
+struct mlt_geometry_s
+{
+ void *local;
+};
+
+/* Create a new geometry structure */
+extern mlt_geometry mlt_geometry_init( );
+/* Parse the geometry specification for a given length and normalised width/height (-1 for default) */
+extern int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh );
+/* Conditionally refresh the geometry if it's modified */
+extern int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh );
+/* Get and set the length */
+extern int mlt_geometry_get_length( mlt_geometry self );
+extern void mlt_geometry_set_length( mlt_geometry self, int length );
+/* Parse an item - doesn't affect the geometry itself but uses current information for evaluation */
+/* (item->frame should be specified if not included in the data itself) */
+extern int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *data );
+/* Fetch a geometry item for an absolute position */
+extern int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position );
+/* Specify a geometry item at an absolute position */
+extern int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item );
+/* Remove the key at the specified position */
+extern int mlt_geometry_remove( mlt_geometry self, int position );
+/* Get the key at the position or the next following */
+extern int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position );
+extern int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position );
+/* Serialise the current geometry */
+extern char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out );
+extern char *mlt_geometry_serialise( mlt_geometry self );
+/* Close the geometry */
+extern void mlt_geometry_close( mlt_geometry self );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_log.c
+ * \brief logging functions
+ *
+ * Copyright (c) 2003 Michel Bardiaux
+ *
+ * This file was a part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg is distributed in the hope that 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "mlt_log.h"
+#include "mlt_service.h"
+
+#include <string.h>
+
+static int log_level = MLT_LOG_INFO;
+
+void default_callback( void* ptr, int level, const char* fmt, va_list vl )
+{
+ static int print_prefix = 1;
+ mlt_properties properties = ptr ? MLT_SERVICE_PROPERTIES( ( mlt_service )ptr ) : NULL;
+
+ if ( level > log_level )
+ return;
+ if ( print_prefix && properties )
+ {
+ char *mlt_type = mlt_properties_get( properties, "mlt_type" );
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ if ( resource && *resource && resource[0] == '<' && resource[ strlen(resource) - 1 ] == '>' )
+ mlt_type = resource;
+ fprintf( stderr, "[%s @ %p]", mlt_type, ptr );
+ }
+ print_prefix = strstr( fmt, "\n" ) != NULL;
+ vfprintf( stderr, fmt, vl );
+}
+
+static void ( *callback )( void*, int, const char*, va_list ) = default_callback;
+
+void mlt_log( void* service, int level, const char *fmt, ...)
+{
+ va_list vl;
+
+ va_start( vl, fmt );
+ mlt_vlog( service, level, fmt, vl );
+ va_end( vl );
+}
+
+void mlt_vlog( void* service, int level, const char *fmt, va_list vl )
+{
+ if ( callback ) callback( service, level, fmt, vl );
+}
+
+int mlt_log_get_level( void )
+{
+ return log_level;
+}
+
+void mlt_log_set_level( int level )
+{
+ log_level = level;
+}
+
+void mlt_log_set_callback( void (*new_callback)( void*, int, const char*, va_list ) )
+{
+ callback = new_callback;
+}
--- /dev/null
+/**
+ * \file mlt_log.h
+ * \brief logging functions
+ *
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file was a part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg is distributed in the hope that 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MLT_LOG_H
+#define MLT_LOG_H
+
+#include <stdarg.h>
+
+#define MLT_LOG_QUIET -8
+
+/**
+ * something went really wrong and we will crash now
+ */
+#define MLT_LOG_PANIC 0
+
+/**
+ * something went wrong and recovery is not possible
+ * like no header in a format which depends on it or a combination
+ * of parameters which are not allowed
+ */
+#define MLT_LOG_FATAL 8
+
+/**
+ * something went wrong and cannot losslessly be recovered
+ * but not all future data is affected
+ */
+#define MLT_LOG_ERROR 16
+
+/**
+ * something somehow does not look correct / something which may or may not
+ * lead to some problems
+ */
+#define MLT_LOG_WARNING 24
+
+#define MLT_LOG_INFO 32
+#define MLT_LOG_VERBOSE 40
+
+/**
+ * stuff which is only useful for MLT developers
+ */
+#define MLT_LOG_DEBUG 48
+
+/**
+ * Send the specified message to the log if the level is less than or equal to
+ * the current logging level. By default, all logging messages are sent to
+ * stderr. This behavior can be altered by setting a different mlt_vlog callback
+ * function.
+ *
+ * \param service An optional pointer to a \p mlt_service_s.
+ * \param level The importance level of the message, lower values signifying
+ * higher importance.
+ * \param fmt The format string (printf-compatible) that specifies how
+ * subsequent arguments are converted to output.
+ * \see mlt_vlog
+ */
+#ifdef __GNUC__
+void mlt_log( void *service, int level, const char *fmt, ... ) __attribute__ ((__format__ (__printf__, 3, 4)));
+#else
+void mlt_log( void *service, int level, const char *fmt, ... );
+#endif
+
+#define mlt_log_panic(service, format, args...) mlt_log((service), MLT_LOG_PANIC, (format), ## args)
+#define mlt_log_fatal(service, format, args...) mlt_log((service), MLT_LOG_FATAL, (format), ## args)
+#define mlt_log_error(service, format, args...) mlt_log((service), MLT_LOG_ERROR, (format), ## args)
+#define mlt_log_warning(service, format, args...) mlt_log((service), MLT_LOG_WARNING, (format), ## args)
+#define mlt_log_info(service, format, args...) mlt_log((service), MLT_LOG_INFO, (format), ## args)
+#define mlt_log_verbose(service, format, args...) mlt_log((service), MLT_LOG_VERBOSE, (format), ## args)
+#define mlt_log_debug(service, format, args...) mlt_log((service), MLT_LOG_DEBUG, (format), ## args)
+
+void mlt_vlog( void *service, int level, const char *fmt, va_list );
+int mlt_log_get_level( void );
+void mlt_log_set_level( int );
+void mlt_log_set_callback( void (*)( void*, int, const char*, va_list ) );
+
+#endif /* MLT_LOG_H */
--- /dev/null
+/**
+ * \file mlt_multitrack.c
+ * \brief multitrack service class
+ * \see mlt_multitrack_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_multitrack.h"
+#include "mlt_playlist.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Forward reference. */
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+
+/** Construct and initialize a new multitrack.
+ *
+ * Sets the resource property to "<multitrack>".
+ *
+ * \public \memberof mlt_multitrack_s
+ * \return a new multitrack
+ */
+
+mlt_multitrack mlt_multitrack_init( )
+{
+ // Allocate the multitrack object
+ mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 );
+
+ if ( this != NULL )
+ {
+ mlt_producer producer = &this->parent;
+ if ( mlt_producer_init( producer, this ) == 0 )
+ {
+ mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this );
+ producer->get_frame = producer_get_frame;
+ mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
+ mlt_properties_set( properties, "log_id", "multitrack" );
+ mlt_properties_set( properties, "resource", "<multitrack>" );
+ mlt_properties_set_int( properties, "in", 0 );
+ mlt_properties_set_int( properties, "out", -1 );
+ mlt_properties_set_int( properties, "length", 0 );
+ producer->close = ( mlt_destructor )mlt_multitrack_close;
+ }
+ else
+ {
+ free( this );
+ this = NULL;
+ }
+ }
+
+ return this;
+}
+
+/** Get the producer associated to this multitrack.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the producer object
+ * \see MLT_MULTITRACK_PRODUCER
+ */
+
+mlt_producer mlt_multitrack_producer( mlt_multitrack this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the service associated this multitrack.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the service object
+ * \see MLT_MULTITRACK_SERVICE
+ */
+
+mlt_service mlt_multitrack_service( mlt_multitrack this )
+{
+ return MLT_MULTITRACK_SERVICE( this );
+}
+
+/** Get the properties associated this multitrack.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the multitrack's property list
+ * \see MLT_MULTITRACK_PROPERTIES
+ */
+
+mlt_properties mlt_multitrack_properties( mlt_multitrack this )
+{
+ return MLT_MULTITRACK_PROPERTIES( this );
+}
+
+/** Initialize position related information.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ */
+
+void mlt_multitrack_refresh( mlt_multitrack this )
+{
+ int i = 0;
+
+ // Obtain the properties of this multitrack
+ mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this );
+
+ // We need to ensure that the multitrack reports the longest track as its length
+ mlt_position length = 0;
+
+ // Obtain stats on all connected services
+ for ( i = 0; i < this->count; i ++ )
+ {
+ // Get the producer from this index
+ mlt_track track = this->list[ i ];
+ mlt_producer producer = track->producer;
+
+ // If it's allocated then, update our stats
+ if ( producer != NULL )
+ {
+ // If we have more than 1 track, we must be in continue mode
+ if ( this->count > 1 )
+ mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "continue" );
+
+ // Determine the longest length
+ //if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) )
+ length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
+ }
+ }
+
+ // Update multitrack properties now - we'll not destroy the in point here
+ mlt_events_block( properties, properties );
+ mlt_properties_set_position( properties, "length", length );
+ mlt_events_unblock( properties, properties );
+ mlt_properties_set_position( properties, "out", length - 1 );
+}
+
+/** Listener for producers on the playlist.
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param producer a producer
+ * \param this a multitrack
+ */
+
+static void mlt_multitrack_listener( mlt_producer producer, mlt_multitrack this )
+{
+ mlt_multitrack_refresh( this );
+}
+
+/** Connect a producer to a given track.
+ *
+ * Note that any producer can be connected here, but see special case treatment
+ * of playlist in clip point determination below.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \param producer the producer to connect to the multitrack producer
+ * \param track the 0-based index of the track on which to connect the multitrack
+ * \return true on error
+ */
+
+int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
+{
+ // Connect to the producer to ourselves at the specified track
+ int result = mlt_service_connect_producer( MLT_MULTITRACK_SERVICE( this ), MLT_PRODUCER_SERVICE( producer ), track );
+
+ if ( result == 0 )
+ {
+ // Resize the producer list if need be
+ if ( track >= this->size )
+ {
+ int i;
+ this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_track ) );
+ for ( i = this->size; i < track + 10; i ++ )
+ this->list[ i ] = NULL;
+ this->size = track + 10;
+ }
+
+ if ( this->list[ track ] != NULL )
+ {
+ mlt_event_close( this->list[ track ]->event );
+ mlt_producer_close( this->list[ track ]->producer );
+ }
+ else
+ {
+ this->list[ track ] = malloc( sizeof( struct mlt_track_s ) );
+ }
+
+ // Assign the track in our list here
+ this->list[ track ]->producer = producer;
+ this->list[ track ]->event = mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer ), this,
+ "producer-changed", ( mlt_listener )mlt_multitrack_listener );
+ mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
+ mlt_event_inc_ref( this->list[ track ]->event );
+
+ // Increment the track count if need be
+ if ( track >= this->count )
+ this->count = track + 1;
+
+ // Refresh our stats
+ mlt_multitrack_refresh( this );
+ }
+
+ return result;
+}
+
+/** Get the number of tracks.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the number of tracks
+ */
+
+int mlt_multitrack_count( mlt_multitrack this )
+{
+ return this->count;
+}
+
+/** Get an individual track as a producer.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \param track the 0-based index of the producer to get
+ * \return the producer or NULL if not valid
+ */
+
+mlt_producer mlt_multitrack_track( mlt_multitrack this, int track )
+{
+ mlt_producer producer = NULL;
+
+ if ( this->list != NULL && track < this->count )
+ producer = this->list[ track ]->producer;
+
+ return producer;
+}
+
+/** Position comparison function for sorting.
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param p1 a position
+ * \param p2 another position
+ * \return <0 if \p p1 is less than \p p2, 0 if equal, >0 if greater
+ */
+
+static int position_compare( const void *p1, const void *p2 )
+{
+ return *( const mlt_position * )p1 - *( const mlt_position * )p2;
+}
+
+/** Add a position to a set.
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param array an array of positions (the set)
+ * \param size the current number of positions in the array (not the capacity of the array)
+ * \param position the position to add
+ * \return the new size of the array
+ */
+
+static int add_unique( mlt_position *array, int size, mlt_position position )
+{
+ int i = 0;
+ for ( i = 0; i < size; i ++ )
+ if ( array[ i ] == position )
+ break;
+ if ( i == size )
+ array[ size ++ ] = position;
+ return size;
+}
+
+/** Determine the clip point.
+ *
+ * <pre>
+ * Special case here: a 'producer' has no concept of multiple clips - only the
+ * playlist and multitrack producers have clip functionality. Further to that a
+ * multitrack determines clip information from any connected tracks that happen
+ * to be playlists.
+ *
+ * Additionally, it must locate clips in the correct order, for example, consider
+ * the following track arrangement:
+ *
+ * playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
+ * playlist2 |b1.0 |1.0 |b1.1 |1.1 |
+ *
+ * Note - b clips represent blanks. They are also reported as clip positions.
+ *
+ * When extracting clip positions from these playlists, we should get a sequence of:
+ *
+ * 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
+ * </pre>
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \param whence from where to extract
+ * \param index the 0-based index of which clip to extract
+ * \return the position of clip \p index relative to \p whence
+ */
+
+mlt_position mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
+{
+ mlt_position position = 0;
+ int i = 0;
+ int j = 0;
+ mlt_position *map = malloc( 1000 * sizeof( mlt_position ) );
+ int count = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ // Get the producer for this track
+ mlt_producer producer = this->list[ i ]->producer;
+
+ // If it's assigned and not a hidden track
+ if ( producer != NULL )
+ {
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Determine if it's a playlist
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+
+ // Special case consideration of playlists
+ if ( playlist != NULL )
+ {
+ for ( j = 0; j < mlt_playlist_count( playlist ); j ++ )
+ count = add_unique( map, count, mlt_playlist_clip( playlist, mlt_whence_relative_start, j ) );
+ count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
+ }
+ else
+ {
+ count = add_unique( map, count, 0 );
+ count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
+ }
+ }
+ }
+
+ // Now sort the map
+ qsort( map, count, sizeof( mlt_position ), position_compare );
+
+ // Now locate the requested index
+ switch( whence )
+ {
+ case mlt_whence_relative_start:
+ if ( index < count )
+ position = map[ index ];
+ else
+ position = map[ count - 1 ];
+ break;
+
+ case mlt_whence_relative_current:
+ position = mlt_producer_position( MLT_MULTITRACK_PRODUCER( this ) );
+ for ( i = 0; i < count - 2; i ++ )
+ if ( position >= map[ i ] && position < map[ i + 1 ] )
+ break;
+ index += i;
+ if ( index >= 0 && index < count )
+ position = map[ index ];
+ else if ( index < 0 )
+ position = map[ 0 ];
+ else
+ position = map[ count - 1 ];
+ break;
+
+ case mlt_whence_relative_end:
+ if ( index < count )
+ position = map[ count - index - 1 ];
+ else
+ position = map[ 0 ];
+ break;
+ }
+
+ // Free the map
+ free( map );
+
+ return position;
+}
+
+/** Get frame method.
+ *
+ * <pre>
+ * Special case here: The multitrack must be used in a conjunction with a downstream
+ * tractor-type service, ie:
+ *
+ * Producer1 \
+ * Producer2 - multitrack - { filters/transitions } - tractor - consumer
+ * Producer3 /
+ *
+ * The get_frame of a tractor pulls frames from it's connected service on all tracks and
+ * will terminate as soon as it receives a test card with a last_track property. The
+ * important case here is that the mulitrack does not move to the next frame until all
+ * tracks have been pulled.
+ *
+ * Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
+ * that all producers are positioned on the same frame. It uses the 'last track' logic
+ * to determine when to move to the next frame.
+ *
+ * Flaw: if a transition is configured to read from a b-track which happens to trigger
+ * the last frame logic (ie: it's configured incorrectly), then things are going to go
+ * out of sync.
+ *
+ * See playlist logic too.
+ * </pre>
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param parent the producer interface to a mulitrack
+ * \param[out] frame a frame by reference
+ * \param index the 0-based track index
+ * \return true if there was an error
+ */
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
+{
+ // Get the mutiltrack object
+ mlt_multitrack this = parent->child;
+
+ // Check if we have a track for this index
+ if ( index < this->count && this->list[ index ] != NULL )
+ {
+ // Get the producer for this track
+ mlt_producer producer = this->list[ index ]->producer;
+
+ // Get the track hide property
+ int hide = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) ), "hide" );
+
+ // Obtain the current position
+ mlt_position position = mlt_producer_frame( parent );
+
+ // Get the parent properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( parent );
+
+ // Get the speed
+ double speed = mlt_properties_get_double( producer_properties, "_speed" );
+
+ // Make sure we're at the same point
+ mlt_producer_seek( producer, position );
+
+ // Get the frame from the producer
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), frame, 0 );
+
+ // Indicate speed of this producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+ mlt_properties_set_double( properties, "_speed", speed );
+ mlt_properties_set_position( properties, "_position", position );
+ mlt_properties_set_int( properties, "hide", hide );
+ }
+ else
+ {
+ // Generate a test frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+
+ // Update position on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( parent ) );
+
+ // Move on to the next frame
+ if ( index >= this->count )
+ {
+ // Let tractor know if we've reached the end
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "last_track", 1 );
+
+ // Move to the next frame
+ mlt_producer_prepare_next( parent );
+ }
+ }
+
+ return 0;
+}
+
+/** Close this instance and free its resources.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ */
+
+void mlt_multitrack_close( mlt_multitrack this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( this ) ) <= 0 )
+ {
+ int i = 0;
+ for ( i = 0; i < this->count; i ++ )
+ {
+ if ( this->list[ i ] != NULL )
+ {
+ mlt_event_close( this->list[ i ]->event );
+ mlt_producer_close( this->list[ i ]->producer );
+ free( this->list[ i ] );
+ }
+ }
+
+ // Close the producer
+ this->parent.close = NULL;
+ mlt_producer_close( &this->parent );
+
+ // Free the list
+ free( this->list );
+
+ // Free the object
+ free( this );
+ }
+}
--- /dev/null
+/**
+ * \file mlt_multitrack.h
+ * \brief multitrack service class
+ * \see mlt_multitrack_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_MULITRACK_H_
+#define _MLT_MULITRACK_H_
+
+#include "mlt_producer.h"
+
+/** \brief Track class used by mlt_multitrack_s
+ */
+
+struct mlt_track_s
+{
+ mlt_producer producer;
+ mlt_event event;
+};
+
+typedef struct mlt_track_s *mlt_track;
+
+/** \brief Multitrack class
+ *
+ * A multitrack is a parallel container of producers that acts a single producer.
+ *
+ * \extends mlt_producer_s
+ * \properties \em log_id not currently used, but sets it to "mulitrack"
+ */
+
+struct mlt_multitrack_s
+{
+ /** We're extending producer here */
+ struct mlt_producer_s parent;
+ mlt_track *list;
+ int size;
+ int count;
+};
+
+#define MLT_MULTITRACK_PRODUCER( multitrack ) ( &( multitrack )->parent )
+#define MLT_MULTITRACK_SERVICE( multitrack ) MLT_PRODUCER_SERVICE( MLT_MULTITRACK_PRODUCER( multitrack ) )
+#define MLT_MULTITRACK_PROPERTIES( multitrack ) MLT_SERVICE_PROPERTIES( MLT_MULTITRACK_SERVICE( multitrack ) )
+
+extern mlt_multitrack mlt_multitrack_init( );
+extern mlt_producer mlt_multitrack_producer( mlt_multitrack self );
+extern mlt_service mlt_multitrack_service( mlt_multitrack self );
+extern mlt_properties mlt_multitrack_properties( mlt_multitrack self );
+extern int mlt_multitrack_connect( mlt_multitrack self, mlt_producer producer, int track );
+extern mlt_position mlt_multitrack_clip( mlt_multitrack self, mlt_whence whence, int index );
+extern void mlt_multitrack_close( mlt_multitrack self );
+extern int mlt_multitrack_count( mlt_multitrack self );
+extern void mlt_multitrack_refresh( mlt_multitrack self );
+extern mlt_producer mlt_multitrack_track( mlt_multitrack self, int track );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_parser.c
+ * \brief service parsing functionality
+ * \see mlt_parser_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt.h"
+#include <stdlib.h>
+
+static int on_invalid( mlt_parser this, mlt_service object )
+{
+ return 0;
+}
+
+static int on_unknown( mlt_parser this, mlt_service object )
+{
+ return 0;
+}
+
+static int on_start_producer( mlt_parser this, mlt_producer object )
+{
+ return 0;
+}
+
+static int on_end_producer( mlt_parser this, mlt_producer object )
+{
+ return 0;
+}
+
+static int on_start_playlist( mlt_parser this, mlt_playlist object )
+{
+ return 0;
+}
+
+static int on_end_playlist( mlt_parser this, mlt_playlist object )
+{
+ return 0;
+}
+
+static int on_start_tractor( mlt_parser this, mlt_tractor object )
+{
+ return 0;
+}
+
+static int on_end_tractor( mlt_parser this, mlt_tractor object )
+{
+ return 0;
+}
+
+static int on_start_multitrack( mlt_parser this, mlt_multitrack object )
+{
+ return 0;
+}
+
+static int on_end_multitrack( mlt_parser this, mlt_multitrack object )
+{
+ return 0;
+}
+
+static int on_start_track( mlt_parser this )
+{
+ return 0;
+}
+
+static int on_end_track( mlt_parser this )
+{
+ return 0;
+}
+
+static int on_start_filter( mlt_parser this, mlt_filter object )
+{
+ return 0;
+}
+
+static int on_end_filter( mlt_parser this, mlt_filter object )
+{
+ return 0;
+}
+
+static int on_start_transition( mlt_parser this, mlt_transition object )
+{
+ return 0;
+}
+
+static int on_end_transition( mlt_parser this, mlt_transition object )
+{
+ return 0;
+}
+
+mlt_parser mlt_parser_new( )
+{
+ mlt_parser this = calloc( 1, sizeof( struct mlt_parser_s ) );
+ if ( this != NULL && mlt_properties_init( &this->parent, this ) == 0 )
+ {
+ this->on_invalid = on_invalid;
+ this->on_unknown = on_unknown;
+ this->on_start_producer = on_start_producer;
+ this->on_end_producer = on_end_producer;
+ this->on_start_playlist = on_start_playlist;
+ this->on_end_playlist = on_end_playlist;
+ this->on_start_tractor = on_start_tractor;
+ this->on_end_tractor = on_end_tractor;
+ this->on_start_multitrack = on_start_multitrack;
+ this->on_end_multitrack = on_end_multitrack;
+ this->on_start_track = on_start_track;
+ this->on_end_track = on_end_track;
+ this->on_start_filter = on_start_filter;
+ this->on_end_filter = on_end_filter;
+ this->on_start_transition = on_start_transition;
+ this->on_end_transition = on_end_transition;
+ }
+ return this;
+}
+
+mlt_properties mlt_parser_properties( mlt_parser this )
+{
+ return &this->parent;
+}
+
+int mlt_parser_start( mlt_parser this, mlt_service object )
+{
+ int error = 0;
+ mlt_service_type type = mlt_service_identify( object );
+ switch( type )
+ {
+ case invalid_type:
+ error = this->on_invalid( this, object );
+ break;
+ case unknown_type:
+ error = this->on_unknown( this, object );
+ break;
+ case producer_type:
+ if ( mlt_producer_is_cut( ( mlt_producer )object ) )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_cut_parent( ( mlt_producer )object ) );
+ error = this->on_start_producer( this, ( mlt_producer )object );
+ if ( error == 0 )
+ {
+ int i = 0;
+ while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+ }
+ error = this->on_end_producer( this, ( mlt_producer )object );
+ break;
+ case playlist_type:
+ error = this->on_start_playlist( this, ( mlt_playlist )object );
+ if ( error == 0 )
+ {
+ int i = 0;
+ while ( error == 0 && i < mlt_playlist_count( ( mlt_playlist )object ) )
+ mlt_parser_start( this, ( mlt_service )mlt_playlist_get_clip( ( mlt_playlist )object, i ++ ) );
+ i = 0;
+ while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+ }
+ error = this->on_end_playlist( this, ( mlt_playlist )object );
+ break;
+ case tractor_type:
+ error = this->on_start_tractor( this, ( mlt_tractor )object );
+ if ( error == 0 )
+ {
+ int i = 0;
+ mlt_service next = mlt_service_producer( object );
+ mlt_parser_start( this, ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) );
+ while ( next != ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) )
+ {
+ mlt_parser_start( this, next );
+ next = mlt_service_producer( next );
+ }
+ while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+ }
+ error = this->on_end_tractor( this, ( mlt_tractor )object );
+ break;
+ case multitrack_type:
+ error = this->on_start_multitrack( this, ( mlt_multitrack )object );
+ if ( error == 0 )
+ {
+ int i = 0;
+ while ( i < mlt_multitrack_count( ( mlt_multitrack )object ) )
+ {
+ this->on_start_track( this );
+ mlt_parser_start( this, ( mlt_service )mlt_multitrack_track( ( mlt_multitrack )object , i ++ ) );
+ this->on_end_track( this );
+ }
+ i = 0;
+ while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+ }
+ error = this->on_end_multitrack( this, ( mlt_multitrack )object );
+ break;
+ case filter_type:
+ error = this->on_start_filter( this, ( mlt_filter )object );
+ if ( error == 0 )
+ {
+ int i = 0;
+ while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+ }
+ error = this->on_end_filter( this, ( mlt_filter )object );
+ break;
+ case transition_type:
+ error = this->on_start_transition( this, ( mlt_transition )object );
+ if ( error == 0 )
+ {
+ int i = 0;
+ while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+ error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+ }
+ error = this->on_end_transition( this, ( mlt_transition )object );
+ break;
+ case field_type:
+ break;
+ case consumer_type:
+ break;
+ }
+ return error;
+}
+
+void mlt_parser_close( mlt_parser this )
+{
+ if ( this != NULL )
+ {
+ mlt_properties_close( &this->parent );
+ free( this );
+ }
+}
+
+
--- /dev/null
+/**
+ * \file mlt_parser.h
+ * \brief service parsing functionality
+ * \see mlt_parser_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_PARSER_H_
+#define _MLT_PARSER_H_
+
+#include "mlt_types.h"
+
+/** \brief Parser class
+ *
+ * \extends mlt_properties_s
+ */
+
+struct mlt_parser_s
+{
+ struct mlt_properties_s parent;
+ int ( *on_invalid )( mlt_parser self, mlt_service object );
+ int ( *on_unknown )( mlt_parser self, mlt_service object );
+ int ( *on_start_producer )( mlt_parser self, mlt_producer object );
+ int ( *on_end_producer )( mlt_parser self, mlt_producer object );
+ int ( *on_start_playlist )( mlt_parser self, mlt_playlist object );
+ int ( *on_end_playlist )( mlt_parser self, mlt_playlist object );
+ int ( *on_start_tractor )( mlt_parser self, mlt_tractor object );
+ int ( *on_end_tractor )( mlt_parser self, mlt_tractor object );
+ int ( *on_start_multitrack )( mlt_parser self, mlt_multitrack object );
+ int ( *on_end_multitrack )( mlt_parser self, mlt_multitrack object );
+ int ( *on_start_track )( mlt_parser self );
+ int ( *on_end_track )( mlt_parser self );
+ int ( *on_start_filter )( mlt_parser self, mlt_filter object );
+ int ( *on_end_filter )( mlt_parser self, mlt_filter object );
+ int ( *on_start_transition )( mlt_parser self, mlt_transition object );
+ int ( *on_end_transition )( mlt_parser self, mlt_transition object );
+};
+
+extern mlt_parser mlt_parser_new( );
+extern mlt_properties mlt_parser_properties( mlt_parser self );
+extern int mlt_parser_start( mlt_parser self, mlt_service object );
+extern void mlt_parser_close( mlt_parser self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_playlist.c
+ * \brief playlist service class
+ * \see mlt_playlist_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_playlist.h"
+#include "mlt_tractor.h"
+#include "mlt_multitrack.h"
+#include "mlt_field.h"
+#include "mlt_frame.h"
+#include "mlt_transition.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief Virtual playlist entry used by mlt_playlist_s
+*/
+
+struct playlist_entry_s
+{
+ mlt_producer producer;
+ mlt_position frame_in;
+ mlt_position frame_out;
+ mlt_position frame_count;
+ int repeat;
+ mlt_position producer_length;
+ mlt_event event;
+ int preservation_hack;
+};
+
+/* Forward declarations
+*/
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+static int mlt_playlist_unmix( mlt_playlist this, int clip );
+static int mlt_playlist_resize_mix( mlt_playlist this, int clip, int in, int out );
+
+/** Construct a playlist.
+ *
+ * Sets the resource property to "<playlist>".
+ * Set the mlt_type to property to "mlt_producer".
+ * \public \memberof mlt_playlist_s
+ * \return a new playlist
+ */
+
+mlt_playlist mlt_playlist_init( )
+{
+ mlt_playlist this = calloc( sizeof( struct mlt_playlist_s ), 1 );
+ if ( this != NULL )
+ {
+ mlt_producer producer = &this->parent;
+
+ // Construct the producer
+ mlt_producer_init( producer, this );
+
+ // Override the producer get_frame
+ producer->get_frame = producer_get_frame;
+
+ // Define the destructor
+ producer->close = ( mlt_destructor )mlt_playlist_close;
+ producer->close_object = this;
+
+ // Initialise blank
+ mlt_producer_init( &this->blank, NULL );
+ mlt_properties_set( MLT_PRODUCER_PROPERTIES( &this->blank ), "mlt_service", "blank" );
+ mlt_properties_set( MLT_PRODUCER_PROPERTIES( &this->blank ), "resource", "blank" );
+
+ // Indicate that this producer is a playlist
+ mlt_properties_set_data( MLT_PLAYLIST_PROPERTIES( this ), "playlist", this, 0, NULL, NULL );
+
+ // Specify the eof condition
+ mlt_properties_set( MLT_PLAYLIST_PROPERTIES( this ), "eof", "pause" );
+ mlt_properties_set( MLT_PLAYLIST_PROPERTIES( this ), "resource", "<playlist>" );
+ mlt_properties_set( MLT_PLAYLIST_PROPERTIES( this ), "mlt_type", "mlt_producer" );
+ mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( this ), "in", 0 );
+ mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( this ), "out", -1 );
+ mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( this ), "length", 0 );
+
+ this->size = 10;
+ this->list = malloc( this->size * sizeof( playlist_entry * ) );
+ }
+
+ return this;
+}
+
+/** Get the producer associated to this playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the producer interface
+ * \see MLT_PLAYLIST_PRODUCER
+ */
+
+mlt_producer mlt_playlist_producer( mlt_playlist this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the service associated to this playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the service interface
+ * \see MLT_PLAYLIST_SERVICE
+ */
+
+mlt_service mlt_playlist_service( mlt_playlist this )
+{
+ return MLT_PRODUCER_SERVICE( &this->parent );
+}
+
+/** Get the properties associated to this playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the playlist's properties list
+ * \see MLT_PLAYLIST_PROPERTIES
+ */
+
+mlt_properties mlt_playlist_properties( mlt_playlist this )
+{
+ return MLT_PRODUCER_PROPERTIES( &this->parent );
+}
+
+/** Refresh the playlist after a clip has been changed.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return false
+ */
+
+static int mlt_playlist_virtual_refresh( mlt_playlist this )
+{
+ // Obtain the properties
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+ int i = 0;
+ mlt_position frame_count = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ // Get the producer
+ mlt_producer producer = this->list[ i ]->producer;
+ if ( producer )
+ {
+ int current_length = mlt_producer_get_out( producer ) - mlt_producer_get_in( producer ) + 1;
+
+ // Check if the length of the producer has changed
+ if ( this->list[ i ]->frame_in != mlt_producer_get_in( producer ) ||
+ this->list[ i ]->frame_out != mlt_producer_get_out( producer ) )
+ {
+ // This clip should be removed...
+ if ( current_length < 1 )
+ {
+ this->list[ i ]->frame_in = 0;
+ this->list[ i ]->frame_out = -1;
+ this->list[ i ]->frame_count = 0;
+ }
+ else
+ {
+ this->list[ i ]->frame_in = mlt_producer_get_in( producer );
+ this->list[ i ]->frame_out = mlt_producer_get_out( producer );
+ this->list[ i ]->frame_count = current_length;
+ }
+
+ // Update the producer_length
+ this->list[ i ]->producer_length = current_length;
+ }
+ }
+
+ // Calculate the frame_count
+ this->list[ i ]->frame_count = ( this->list[ i ]->frame_out - this->list[ i ]->frame_in + 1 ) * this->list[ i ]->repeat;
+
+ // Update the frame_count for this clip
+ frame_count += this->list[ i ]->frame_count;
+ }
+
+ // Refresh all properties
+ mlt_events_block( properties, properties );
+ mlt_properties_set_position( properties, "length", frame_count );
+ mlt_events_unblock( properties, properties );
+ mlt_properties_set_position( properties, "out", frame_count - 1 );
+
+ return 0;
+}
+
+/** Listener for producers on the playlist.
+ *
+ * Refreshes the playlist whenever an entry receives producer-changed.
+ * \private \memberof mlt_playlist_s
+ * \param producer a producer
+ * \param this a playlist
+ */
+
+static void mlt_playlist_listener( mlt_producer producer, mlt_playlist this )
+{
+ mlt_playlist_virtual_refresh( this );
+}
+
+/** Append to the virtual playlist.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param source a producer
+ * \param in the producer's starting time
+ * \param out the producer's ending time
+ * \return true if there was an error
+ */
+
+static int mlt_playlist_virtual_append( mlt_playlist this, mlt_producer source, mlt_position in, mlt_position out )
+{
+ mlt_producer producer = NULL;
+ mlt_properties properties = NULL;
+ mlt_properties parent = NULL;
+
+ // If we have a cut, then use the in/out points from the cut
+ if ( mlt_producer_is_blank( source ) )
+ {
+ // Make sure the blank is long enough to accomodate the length specified
+ if ( out - in + 1 > mlt_producer_get_length( &this->blank ) )
+ {
+ mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &this->blank );
+ mlt_events_block( blank_props, blank_props );
+ mlt_producer_set_in_and_out( &this->blank, in, out );
+ mlt_events_unblock( blank_props, blank_props );
+ }
+
+ // Now make sure the cut comes from this this->blank
+ if ( source == NULL )
+ {
+ producer = mlt_producer_cut( &this->blank, in, out );
+ }
+ else if ( !mlt_producer_is_cut( source ) || mlt_producer_cut_parent( source ) != &this->blank )
+ {
+ producer = mlt_producer_cut( &this->blank, in, out );
+ }
+ else
+ {
+ producer = source;
+ mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
+ }
+
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+ else if ( mlt_producer_is_cut( source ) )
+ {
+ producer = source;
+ if ( in == -1 )
+ in = mlt_producer_get_in( producer );
+ if ( out == -1 || out > mlt_producer_get_out( producer ) )
+ out = mlt_producer_get_out( producer );
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ mlt_properties_inc_ref( properties );
+ }
+ else
+ {
+ producer = mlt_producer_cut( source, in, out );
+ if ( in == -1 || in < mlt_producer_get_in( producer ) )
+ in = mlt_producer_get_in( producer );
+ if ( out == -1 || out > mlt_producer_get_out( producer ) )
+ out = mlt_producer_get_out( producer );
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+
+ // Fetch the cuts parent properties
+ parent = MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) );
+
+ // Remove fezzik normalisers for fx cuts
+ if ( mlt_properties_get_int( parent, "meta.fx_cut" ) )
+ {
+ mlt_service service = MLT_PRODUCER_SERVICE( mlt_producer_cut_parent( producer ) );
+ mlt_filter filter = mlt_service_filter( service, 0 );
+ while ( filter != NULL && mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "_fezzik" ) )
+ {
+ mlt_service_detach( service, filter );
+ filter = mlt_service_filter( service, 0 );
+ }
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "meta.fx_cut", 1 );
+ }
+
+ // Check that we have room
+ if ( this->count >= this->size )
+ {
+ int i;
+ this->list = realloc( this->list, ( this->size + 10 ) * sizeof( playlist_entry * ) );
+ for ( i = this->size; i < this->size + 10; i ++ ) this->list[ i ] = NULL;
+ this->size += 10;
+ }
+
+ // Create the entry
+ this->list[ this->count ] = calloc( sizeof( playlist_entry ), 1 );
+ if ( this->list[ this->count ] != NULL )
+ {
+ this->list[ this->count ]->producer = producer;
+ this->list[ this->count ]->frame_in = in;
+ this->list[ this->count ]->frame_out = out;
+ this->list[ this->count ]->frame_count = out - in + 1;
+ this->list[ this->count ]->repeat = 1;
+ this->list[ this->count ]->producer_length = mlt_producer_get_out( producer ) - mlt_producer_get_in( producer ) + 1;
+ this->list[ this->count ]->event = mlt_events_listen( parent, this, "producer-changed", ( mlt_listener )mlt_playlist_listener );
+ mlt_event_inc_ref( this->list[ this->count ]->event );
+ mlt_properties_set( properties, "eof", "pause" );
+ mlt_producer_set_speed( producer, 0 );
+ this->count ++;
+ }
+
+ return mlt_playlist_virtual_refresh( this );
+}
+
+/** Locate a producer by index.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param[in, out] position the time at which to locate the producer, returns the time relative to the producer's starting point
+ * \param[out] clip the index of the playlist entry
+ * \param[out] total the duration of the playlist up to and including this producer
+ * \return a producer or NULL if not found
+ */
+
+static mlt_producer mlt_playlist_locate( mlt_playlist this, mlt_position *position, int *clip, int *total )
+{
+ // Default producer to NULL
+ mlt_producer producer = NULL;
+
+ // Loop for each producer until found
+ for ( *clip = 0; *clip < this->count; *clip += 1 )
+ {
+ // Increment the total
+ *total += this->list[ *clip ]->frame_count;
+
+ // Check if the position indicates that we have found the clip
+ // Note that 0 length clips get skipped automatically
+ if ( *position < this->list[ *clip ]->frame_count )
+ {
+ // Found it, now break
+ producer = this->list[ *clip ]->producer;
+ break;
+ }
+ else
+ {
+ // Decrement position by length of this entry
+ *position -= this->list[ *clip ]->frame_count;
+ }
+ }
+
+ return producer;
+}
+
+/** Seek in the virtual playlist.
+ *
+ * This gets the producer at the current position and seeks on the producer
+ * while doing repeat and end-of-file handling. This is also responsible for
+ * closing producers previous to the preceding playlist if the autoclose
+ * property is set.
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param[out] progressive true if the producer should be displayed progressively
+ * \return the service interface of the producer at the play head
+ * \see producer_get_frame
+ */
+
+static mlt_service mlt_playlist_virtual_seek( mlt_playlist this, int *progressive )
+{
+ // Map playlist position to real producer in virtual playlist
+ mlt_position position = mlt_producer_frame( &this->parent );
+
+ // Keep the original position since we change it while iterating through the list
+ mlt_position original = position;
+
+ // Clip index and total
+ int i = 0;
+ int total = 0;
+
+ // Locate the producer for the position
+ mlt_producer producer = mlt_playlist_locate( this, &position, &i, &total );
+
+ // Get the properties
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+
+ // Automatically close previous producers if requested
+ if ( i > 1 // keep immediate previous in case app wants to get info about what just finished
+ && position < 2 // tolerate off-by-one error on going to next clip
+ && mlt_properties_get_int( properties, "autoclose" ) )
+ {
+ int j;
+ // They might have jumped ahead!
+ for ( j = 0; j < i - 1; j++ )
+ {
+ mlt_service_lock( MLT_PRODUCER_SERVICE( this->list[ j ]->producer ) );
+ mlt_producer p = this->list[ j ]->producer;
+ if ( p )
+ {
+ this->list[ j ]->producer = NULL;
+ mlt_service_unlock( MLT_PRODUCER_SERVICE( p ) );
+ mlt_producer_close( p );
+ }
+ // If p is null, the lock will not have been "taken"
+ }
+ }
+
+ // Get the eof handling
+ char *eof = mlt_properties_get( properties, "eof" );
+
+ // Seek in real producer to relative position
+ if ( producer != NULL )
+ {
+ int count = this->list[ i ]->frame_count / this->list[ i ]->repeat;
+ *progressive = count == 1;
+ mlt_producer_seek( producer, (int)position % count );
+ }
+ else if ( !strcmp( eof, "pause" ) && total > 0 )
+ {
+ playlist_entry *entry = this->list[ this->count - 1 ];
+ int count = entry->frame_count / entry->repeat;
+ mlt_producer this_producer = MLT_PLAYLIST_PRODUCER( this );
+ mlt_producer_seek( this_producer, original - 1 );
+ producer = entry->producer;
+ mlt_producer_seek( producer, (int)entry->frame_out % count );
+ mlt_producer_set_speed( this_producer, 0 );
+ mlt_producer_set_speed( producer, 0 );
+ *progressive = count == 1;
+ }
+ else if ( !strcmp( eof, "loop" ) && total > 0 )
+ {
+ playlist_entry *entry = this->list[ 0 ];
+ mlt_producer this_producer = MLT_PLAYLIST_PRODUCER( this );
+ mlt_producer_seek( this_producer, 0 );
+ producer = entry->producer;
+ mlt_producer_seek( producer, 0 );
+ }
+ else
+ {
+ producer = &this->blank;
+ }
+
+ return MLT_PRODUCER_SERVICE( producer );
+}
+
+/** Invoked when a producer indicates that it has prematurely reached its end.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return a producer
+ * \see producer_get_frame
+ */
+
+static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist this )
+{
+ // Default producer to blank
+ mlt_producer producer = &this->blank;
+
+ // Map playlist position to real producer in virtual playlist
+ mlt_position position = mlt_producer_frame( &this->parent );
+
+ // Loop through the virtual playlist
+ int i = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ if ( position < this->list[ i ]->frame_count )
+ {
+ // Found it, now break
+ producer = this->list[ i ]->producer;
+ break;
+ }
+ else
+ {
+ // Decrement position by length of this entry
+ position -= this->list[ i ]->frame_count;
+ }
+ }
+
+ // Seek in real producer to relative position
+ if ( i < this->count && this->list[ i ]->frame_out != position )
+ {
+ // Update the frame_count for the changed clip (hmmm)
+ this->list[ i ]->frame_out = position;
+ this->list[ i ]->frame_count = this->list[ i ]->frame_out - this->list[ i ]->frame_in + 1;
+
+ // Refresh the playlist
+ mlt_playlist_virtual_refresh( this );
+ }
+
+ return producer;
+}
+
+/** Obtain the current clips index.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the index of the playlist entry at the current position
+ */
+
+int mlt_playlist_current_clip( mlt_playlist this )
+{
+ // Map playlist position to real producer in virtual playlist
+ mlt_position position = mlt_producer_frame( &this->parent );
+
+ // Loop through the virtual playlist
+ int i = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ if ( position < this->list[ i ]->frame_count )
+ {
+ // Found it, now break
+ break;
+ }
+ else
+ {
+ // Decrement position by length of this entry
+ position -= this->list[ i ]->frame_count;
+ }
+ }
+
+ return i;
+}
+
+/** Obtain the current clips producer.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the producer at the current position
+ */
+
+mlt_producer mlt_playlist_current( mlt_playlist this )
+{
+ int i = mlt_playlist_current_clip( this );
+ if ( i < this->count )
+ return this->list[ i ]->producer;
+ else
+ return &this->blank;
+}
+
+/** Get the position which corresponds to the start of the next clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param whence the location from which to make the index relative:
+ * start of playlist, end of playlist, or current position
+ * \param index the playlist entry index relative to whence
+ * \return the time at which the referenced clip starts
+ */
+
+mlt_position mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
+{
+ mlt_position position = 0;
+ int absolute_clip = index;
+ int i = 0;
+
+ // Determine the absolute clip
+ switch ( whence )
+ {
+ case mlt_whence_relative_start:
+ absolute_clip = index;
+ break;
+
+ case mlt_whence_relative_current:
+ absolute_clip = mlt_playlist_current_clip( this ) + index;
+ break;
+
+ case mlt_whence_relative_end:
+ absolute_clip = this->count - index;
+ break;
+ }
+
+ // Check that we're in a valid range
+ if ( absolute_clip < 0 )
+ absolute_clip = 0;
+ else if ( absolute_clip > this->count )
+ absolute_clip = this->count;
+
+ // Now determine the position
+ for ( i = 0; i < absolute_clip; i ++ )
+ position += this->list[ i ]->frame_count;
+
+ return position;
+}
+
+/** Get all the info about the clip specified.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param info a clip info struct
+ * \param index a playlist entry index
+ * \return true if there was an error
+ */
+
+int mlt_playlist_get_clip_info( mlt_playlist this, mlt_playlist_clip_info *info, int index )
+{
+ int error = index < 0 || index >= this->count || this->list[ index ]->producer == NULL;
+ memset( info, 0, sizeof( mlt_playlist_clip_info ) );
+ if ( !error )
+ {
+ mlt_producer producer = mlt_producer_cut_parent( this->list[ index ]->producer );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ info->clip = index;
+ info->producer = producer;
+ info->cut = this->list[ index ]->producer;
+ info->start = mlt_playlist_clip( this, mlt_whence_relative_start, index );
+ info->resource = mlt_properties_get( properties, "resource" );
+ info->frame_in = this->list[ index ]->frame_in;
+ info->frame_out = this->list[ index ]->frame_out;
+ info->frame_count = this->list[ index ]->frame_count;
+ info->repeat = this->list[ index ]->repeat;
+ info->length = mlt_producer_get_length( producer );
+ info->fps = mlt_producer_get_fps( producer );
+ }
+
+ return error;
+}
+
+/** Get number of clips in the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the number of playlist entries
+ */
+
+int mlt_playlist_count( mlt_playlist this )
+{
+ return this->count;
+}
+
+/** Clear the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return true if there was an error
+ */
+
+int mlt_playlist_clear( mlt_playlist this )
+{
+ int i;
+ for ( i = 0; i < this->count; i ++ )
+ {
+ mlt_event_close( this->list[ i ]->event );
+ mlt_producer_close( this->list[ i ]->producer );
+ }
+ this->count = 0;
+ return mlt_playlist_virtual_refresh( this );
+}
+
+/** Append a producer to the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param producer the producer to append
+ * \return true if there was an error
+ */
+
+int mlt_playlist_append( mlt_playlist this, mlt_producer producer )
+{
+ // Append to virtual list
+ return mlt_playlist_virtual_append( this, producer, 0, mlt_producer_get_playtime( producer ) - 1 );
+}
+
+/** Append a producer to the playlist with in/out points.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param producer the producer to append
+ * \param in the starting point on the producer
+ * \param out the ending point on the producer
+ * \return true if there was an error
+ */
+
+int mlt_playlist_append_io( mlt_playlist this, mlt_producer producer, mlt_position in, mlt_position out )
+{
+ // Append to virtual list
+ if ( in != -1 && out != -1 )
+ return mlt_playlist_virtual_append( this, producer, in, out );
+ else
+ return mlt_playlist_append( this, producer );
+}
+
+/** Append a blank to the playlist of a given length.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param length the ending time of the blank entry, not its duration
+ * \return true if there was an error
+ */
+
+int mlt_playlist_blank( mlt_playlist this, mlt_position length )
+{
+ // Append to the virtual list
+ if (length >= 0)
+ return mlt_playlist_virtual_append( this, &this->blank, 0, length );
+ else
+ return 1;
+}
+
+/** Insert a producer into the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param producer the producer to insert
+ * \param where the producer's playlist entry index
+ * \param in the starting point on the producer
+ * \param out the ending point on the producer
+ * \return true if there was an error
+ */
+
+int mlt_playlist_insert( mlt_playlist this, mlt_producer producer, int where, mlt_position in, mlt_position out )
+{
+ // Append to end
+ mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_append_io( this, producer, in, out );
+
+ // Move to the position specified
+ mlt_playlist_move( this, this->count - 1, where );
+ mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+ return mlt_playlist_virtual_refresh( this );
+}
+
+/** Remove an entry in the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param where the playlist entry index
+ * \return true if there was an error
+ */
+
+int mlt_playlist_remove( mlt_playlist this, int where )
+{
+ int error = where < 0 || where >= this->count;
+ if ( error == 0 && mlt_playlist_unmix( this, where ) != 0 )
+ {
+ // We need to know the current clip and the position within the playlist
+ int current = mlt_playlist_current_clip( this );
+ mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( this ) );
+
+ // We need all the details about the clip we're removing
+ mlt_playlist_clip_info where_info;
+ playlist_entry *entry = this->list[ where ];
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( entry->producer );
+
+ // Loop variable
+ int i = 0;
+
+ // Get the clip info
+ mlt_playlist_get_clip_info( this, &where_info, where );
+
+ // Make sure the clip to be removed is valid and correct if necessary
+ if ( where < 0 )
+ where = 0;
+ if ( where >= this->count )
+ where = this->count - 1;
+
+ // Reorganise the list
+ for ( i = where + 1; i < this->count; i ++ )
+ this->list[ i - 1 ] = this->list[ i ];
+ this->count --;
+
+ if ( entry->preservation_hack == 0 )
+ {
+ // Decouple from mix_in/out if necessary
+ if ( mlt_properties_get_data( properties, "mix_in", NULL ) != NULL )
+ {
+ mlt_properties mix = mlt_properties_get_data( properties, "mix_in", NULL );
+ mlt_properties_set_data( mix, "mix_out", NULL, 0, NULL, NULL );
+ }
+ if ( mlt_properties_get_data( properties, "mix_out", NULL ) != NULL )
+ {
+ mlt_properties mix = mlt_properties_get_data( properties, "mix_out", NULL );
+ mlt_properties_set_data( mix, "mix_in", NULL, 0, NULL, NULL );
+ }
+
+ if ( mlt_properties_ref_count( MLT_PRODUCER_PROPERTIES( entry->producer ) ) == 1 )
+ mlt_producer_clear( entry->producer );
+ }
+
+ // Close the producer associated to the clip info
+ mlt_event_close( entry->event );
+ mlt_producer_close( entry->producer );
+
+ // Correct position
+ if ( where == current )
+ mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), where_info.start );
+ else if ( where < current && this->count > 0 )
+ mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), position - where_info.frame_count );
+ else if ( this->count == 0 )
+ mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), 0 );
+
+ // Free the entry
+ free( entry );
+
+ // Refresh the playlist
+ mlt_playlist_virtual_refresh( this );
+ }
+
+ return error;
+}
+
+/** Move an entry in the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param src an entry index
+ * \param dest an entry index
+ * \return false
+ */
+
+int mlt_playlist_move( mlt_playlist this, int src, int dest )
+{
+ int i;
+
+ /* We need to ensure that the requested indexes are valid and correct it as necessary */
+ if ( src < 0 )
+ src = 0;
+ if ( src >= this->count )
+ src = this->count - 1;
+
+ if ( dest < 0 )
+ dest = 0;
+ if ( dest >= this->count )
+ dest = this->count - 1;
+
+ if ( src != dest && this->count > 1 )
+ {
+ int current = mlt_playlist_current_clip( this );
+ mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( this ) );
+ playlist_entry *src_entry = NULL;
+
+ // We need all the details about the current clip
+ mlt_playlist_clip_info current_info;
+
+ mlt_playlist_get_clip_info( this, ¤t_info, current );
+ position -= current_info.start;
+
+ if ( current == src )
+ current = dest;
+ else if ( current > src && current < dest )
+ current ++;
+ else if ( current == dest )
+ current = src;
+
+ src_entry = this->list[ src ];
+ if ( src > dest )
+ {
+ for ( i = src; i > dest; i -- )
+ this->list[ i ] = this->list[ i - 1 ];
+ }
+ else
+ {
+ for ( i = src; i < dest; i ++ )
+ this->list[ i ] = this->list[ i + 1 ];
+ }
+ this->list[ dest ] = src_entry;
+
+ mlt_playlist_get_clip_info( this, ¤t_info, current );
+ mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), current_info.start + position );
+ mlt_playlist_virtual_refresh( this );
+ }
+
+ return 0;
+}
+
+/** Repeat the specified clip n times.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip a playlist entry index
+ * \param repeat the number of times to repeat the clip
+ * \return true if there was an error
+ */
+
+int mlt_playlist_repeat_clip( mlt_playlist this, int clip, int repeat )
+{
+ int error = repeat < 1 || clip < 0 || clip >= this->count;
+ if ( error == 0 )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ entry->repeat = repeat;
+ mlt_playlist_virtual_refresh( this );
+ }
+ return error;
+}
+
+/** Resize the specified clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param in the new starting time on the clip's producer
+ * \param out the new ending time on the clip's producer
+ * \return true if there was an error
+ */
+
+int mlt_playlist_resize_clip( mlt_playlist this, int clip, mlt_position in, mlt_position out )
+{
+ int error = clip < 0 || clip >= this->count;
+ if ( error == 0 && mlt_playlist_resize_mix( this, clip, in, out ) != 0 )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ mlt_producer producer = entry->producer;
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+
+ mlt_events_block( properties, properties );
+
+ if ( mlt_producer_is_blank( producer ) )
+ {
+ // Make sure the blank is long enough to accomodate the length specified
+ if ( out - in + 1 > mlt_producer_get_length( &this->blank ) )
+ {
+ mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &this->blank );
+ mlt_properties_set_int( blank_props, "length", out - in + 1 );
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "length", out - in + 1 );
+ mlt_producer_set_in_and_out( &this->blank, 0, out - in );
+ }
+ }
+
+ if ( in <= -1 )
+ in = 0;
+ if ( out <= -1 || out >= mlt_producer_get_length( producer ) )
+ out = mlt_producer_get_length( producer ) - 1;
+
+ if ( out < in )
+ {
+ mlt_position t = in;
+ in = out;
+ out = t;
+ }
+
+ mlt_producer_set_in_and_out( producer, in, out );
+ mlt_events_unblock( properties, properties );
+ mlt_playlist_virtual_refresh( this );
+ }
+ return error;
+}
+
+/** Split a clip on the playlist at the given position.
+ *
+ * This splits after the specified frame.
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param position the time at which to split relative to the beginning of the clip or its end if negative
+ * \return true if there was an error
+ */
+
+int mlt_playlist_split( mlt_playlist this, int clip, mlt_position position )
+{
+ int error = clip < 0 || clip >= this->count;
+ if ( error == 0 )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ position = position < 0 ? entry->frame_count + position - 1 : position;
+ if ( position >= 0 && position < entry->frame_count - 1 )
+ {
+ int in = entry->frame_in;
+ int out = entry->frame_out;
+ mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_resize_clip( this, clip, in, in + position );
+ if ( !mlt_producer_is_blank( entry->producer ) )
+ {
+ int i = 0;
+ mlt_properties entry_properties = MLT_PRODUCER_PROPERTIES( entry->producer );
+ mlt_producer split = mlt_producer_cut( entry->producer, in + position + 1, out );
+ mlt_properties split_properties = MLT_PRODUCER_PROPERTIES( split );
+ mlt_playlist_insert( this, split, clip + 1, 0, -1 );
+ for ( i = 0; i < mlt_properties_count( entry_properties ); i ++ )
+ {
+ char *name = mlt_properties_get_name( entry_properties, i );
+ if ( name != NULL && !strncmp( name, "meta.", 5 ) )
+ mlt_properties_set( split_properties, name, mlt_properties_get_value( entry_properties, i ) );
+ }
+ mlt_producer_close( split );
+ }
+ else
+ {
+ mlt_playlist_insert( this, &this->blank, clip + 1, 0, out - position - 1 );
+ }
+ mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_virtual_refresh( this );
+ }
+ else
+ {
+ error = 1;
+ }
+ }
+ return error;
+}
+
+/** Split the playlist at the absolute position.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the time at which to split relative to the beginning of the clip
+ * \param left true to split before the frame starting at position
+ * \return true if there was an error
+ */
+
+int mlt_playlist_split_at( mlt_playlist this, mlt_position position, int left )
+{
+ int result = this == NULL ? -1 : 0;
+ if ( !result )
+ {
+ if ( position >= 0 && position < mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) ) )
+ {
+ int clip = mlt_playlist_get_clip_index_at( this, position );
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( this, &info, clip );
+ if ( left && position != info.start )
+ mlt_playlist_split( this, clip, position - info.start - 1 );
+ else if ( !left )
+ mlt_playlist_split( this, clip, position - info.start );
+ result = position;
+ }
+ else if ( position <= 0 )
+ {
+ result = 0;
+ }
+ else
+ {
+ result = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) );
+ }
+ }
+ return result;
+}
+
+/** Join 1 or more consecutive clips.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the starting playlist entry index
+ * \param count the number of entries to merge
+ * \param merge ignored
+ * \return true if there was an error
+ */
+
+int mlt_playlist_join( mlt_playlist this, int clip, int count, int merge )
+{
+ int error = clip < 0 || clip >= this->count;
+ if ( error == 0 )
+ {
+ int i = clip;
+ mlt_playlist new_clip = mlt_playlist_init( );
+ mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+ if ( clip + count >= this->count )
+ count = this->count - clip - 1;
+ for ( i = 0; i <= count; i ++ )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ mlt_playlist_append( new_clip, entry->producer );
+ mlt_playlist_repeat_clip( new_clip, i, entry->repeat );
+ entry->preservation_hack = 1;
+ mlt_playlist_remove( this, clip );
+ }
+ mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_insert( this, MLT_PLAYLIST_PRODUCER( new_clip ), clip, 0, -1 );
+ mlt_playlist_close( new_clip );
+ }
+ return error;
+}
+
+/** Mix consecutive clips for a specified length and apply transition if specified.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param length the number of frames over which to create the mix
+ * \param transition the transition to use for the mix
+ * \return true if there was an error
+ */
+
+int mlt_playlist_mix( mlt_playlist this, int clip, int length, mlt_transition transition )
+{
+ int error = ( clip < 0 || clip + 1 >= this->count );
+ if ( error == 0 )
+ {
+ playlist_entry *clip_a = this->list[ clip ];
+ playlist_entry *clip_b = this->list[ clip + 1 ];
+ mlt_producer track_a = NULL;
+ mlt_producer track_b = NULL;
+ mlt_tractor tractor = mlt_tractor_new( );
+ mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+ // Check length is valid for both clips and resize if necessary.
+ int max_size = clip_a->frame_count > clip_b->frame_count ? clip_a->frame_count : clip_b->frame_count;
+ length = length > max_size ? max_size : length;
+
+ // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches
+ if ( length != clip_a->frame_count )
+ track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out );
+ else
+ track_a = clip_a->producer;
+
+ if ( length != clip_b->frame_count )
+ track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1 );
+ else
+ track_b = clip_b->producer;
+
+ // Set the tracks on the tractor
+ mlt_tractor_set_track( tractor, track_a, 0 );
+ mlt_tractor_set_track( tractor, track_b, 1 );
+
+ // Insert the mix object into the playlist
+ mlt_playlist_insert( this, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 );
+ mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL );
+
+ // Attach the transition
+ if ( transition != NULL )
+ {
+ mlt_field field = mlt_tractor_field( tractor );
+ mlt_field_plant_transition( field, transition, 0, 1 );
+ mlt_transition_set_in_and_out( transition, 0, length - 1 );
+ }
+
+ // Close our references to the tracks if we created new cuts above (the tracks can still be used here)
+ if ( track_a != clip_a->producer )
+ mlt_producer_close( track_a );
+ if ( track_b != clip_b->producer )
+ mlt_producer_close( track_b );
+
+ // Check if we have anything left on the right hand clip
+ if ( track_b == clip_b->producer )
+ {
+ clip_b->preservation_hack = 1;
+ mlt_playlist_remove( this, clip + 2 );
+ }
+ else if ( clip_b->frame_out - clip_b->frame_in > length )
+ {
+ mlt_playlist_resize_clip( this, clip + 2, clip_b->frame_in + length, clip_b->frame_out );
+ mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL );
+ mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL );
+ }
+ else
+ {
+ mlt_producer_clear( clip_b->producer );
+ mlt_playlist_remove( this, clip + 2 );
+ }
+
+ // Check if we have anything left on the left hand clip
+ if ( track_a == clip_a->producer )
+ {
+ clip_a->preservation_hack = 1;
+ mlt_playlist_remove( this, clip );
+ }
+ else if ( clip_a->frame_out - clip_a->frame_in > length )
+ {
+ mlt_playlist_resize_clip( this, clip, clip_a->frame_in, clip_a->frame_out - length );
+ mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL );
+ mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL );
+ }
+ else
+ {
+ mlt_producer_clear( clip_a->producer );
+ mlt_playlist_remove( this, clip );
+ }
+
+ // Unblock and force a fire off of change events to listeners
+ mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_virtual_refresh( this );
+ mlt_tractor_close( tractor );
+ }
+ return error;
+}
+
+/** Add a transition to an existing mix.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param transition a transition
+ * \return true if there was an error
+ */
+
+int mlt_playlist_mix_add( mlt_playlist this, int clip, mlt_transition transition )
+{
+ mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( this, clip ) );
+ mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES( producer ) : NULL;
+ mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
+ int error = transition == NULL || tractor == NULL;
+ if ( error == 0 )
+ {
+ mlt_field field = mlt_tractor_field( tractor );
+ mlt_field_plant_transition( field, transition, 0, 1 );
+ mlt_transition_set_in_and_out( transition, 0, this->list[ clip ]->frame_count - 1 );
+ }
+ return error;
+}
+
+/** Return the clip at the clip index.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of a playlist entry
+ * \return a producer or NULL if there was an error
+ */
+
+mlt_producer mlt_playlist_get_clip( mlt_playlist this, int clip )
+{
+ if ( clip >= 0 && clip < this->count )
+ return this->list[ clip ]->producer;
+ return NULL;
+}
+
+/** Return the clip at the specified position.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position a time relative to the beginning of the playlist
+ * \return a producer or NULL if not found
+ */
+
+mlt_producer mlt_playlist_get_clip_at( mlt_playlist this, mlt_position position )
+{
+ int index = 0, total = 0;
+ return mlt_playlist_locate( this, &position, &index, &total );
+}
+
+/** Return the clip index of the specified position.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position a time relative to the beginning of the playlist
+ * \return the index of the playlist entry
+ */
+
+int mlt_playlist_get_clip_index_at( mlt_playlist this, mlt_position position )
+{
+ int index = 0, total = 0;
+ mlt_playlist_locate( this, &position, &index, &total );
+ return index;
+}
+
+/** Determine if the clip is a mix.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return true if the producer is a mix
+ */
+
+int mlt_playlist_clip_is_mix( mlt_playlist this, int clip )
+{
+ mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( this, clip ) );
+ mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES( producer ) : NULL;
+ mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
+ return tractor != NULL;
+}
+
+/** Remove a mixed clip - ensure that the cuts included in the mix find their way
+ * back correctly on to the playlist.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return true if there was an error
+ */
+
+static int mlt_playlist_unmix( mlt_playlist this, int clip )
+{
+ int error = ( clip < 0 || clip >= this->count );
+
+ // Ensure that the clip request is actually a mix
+ if ( error == 0 )
+ {
+ mlt_producer producer = mlt_producer_cut_parent( this->list[ clip ]->producer );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL ||
+ this->list[ clip ]->preservation_hack;
+ }
+
+ if ( error == 0 )
+ {
+ playlist_entry *mix = this->list[ clip ];
+ mlt_tractor tractor = ( mlt_tractor )mlt_producer_cut_parent( mix->producer );
+ mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
+ mlt_producer clip_a = mlt_properties_get_data( properties, "mix_in", NULL );
+ mlt_producer clip_b = mlt_properties_get_data( properties, "mix_out", NULL );
+ int length = mlt_producer_get_playtime( MLT_TRACTOR_PRODUCER( tractor ) );
+ mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+ if ( clip_a != NULL )
+ {
+ mlt_producer_set_in_and_out( clip_a, mlt_producer_get_in( clip_a ), mlt_producer_get_out( clip_a ) + length );
+ }
+ else
+ {
+ mlt_producer cut = mlt_tractor_get_track( tractor, 0 );
+ mlt_playlist_insert( this, cut, clip, -1, -1 );
+ clip ++;
+ }
+
+ if ( clip_b != NULL )
+ {
+ mlt_producer_set_in_and_out( clip_b, mlt_producer_get_in( clip_b ) - length, mlt_producer_get_out( clip_b ) );
+ }
+ else
+ {
+ mlt_producer cut = mlt_tractor_get_track( tractor, 1 );
+ mlt_playlist_insert( this, cut, clip + 1, -1, -1 );
+ }
+
+ mlt_properties_set_data( properties, "mlt_mix", NULL, 0, NULL, NULL );
+ mlt_playlist_remove( this, clip );
+ mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_virtual_refresh( this );
+ }
+ return error;
+}
+
+/** Resize a mix clip.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param in the new starting point
+ * \param out the new ending point
+ * \return true if there was an error
+ */
+
+static int mlt_playlist_resize_mix( mlt_playlist this, int clip, int in, int out )
+{
+ int error = ( clip < 0 || clip >= this->count );
+
+ // Ensure that the clip request is actually a mix
+ if ( error == 0 )
+ {
+ mlt_producer producer = mlt_producer_cut_parent( this->list[ clip ]->producer );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL;
+ }
+
+ if ( error == 0 )
+ {
+ playlist_entry *mix = this->list[ clip ];
+ mlt_tractor tractor = ( mlt_tractor )mlt_producer_cut_parent( mix->producer );
+ mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
+ mlt_producer clip_a = mlt_properties_get_data( properties, "mix_in", NULL );
+ mlt_producer clip_b = mlt_properties_get_data( properties, "mix_out", NULL );
+ mlt_producer track_a = mlt_tractor_get_track( tractor, 0 );
+ mlt_producer track_b = mlt_tractor_get_track( tractor, 1 );
+ int length = out - in + 1;
+ int length_diff = length - mlt_producer_get_playtime( MLT_TRACTOR_PRODUCER( tractor ) );
+ mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+ if ( clip_a != NULL )
+ mlt_producer_set_in_and_out( clip_a, mlt_producer_get_in( clip_a ), mlt_producer_get_out( clip_a ) - length_diff );
+
+ if ( clip_b != NULL )
+ mlt_producer_set_in_and_out( clip_b, mlt_producer_get_in( clip_b ) + length_diff, mlt_producer_get_out( clip_b ) );
+
+ mlt_producer_set_in_and_out( track_a, mlt_producer_get_in( track_a ) - length_diff, mlt_producer_get_out( track_a ) );
+ mlt_producer_set_in_and_out( track_b, mlt_producer_get_in( track_b ), mlt_producer_get_out( track_b ) + length_diff );
+ mlt_producer_set_in_and_out( MLT_MULTITRACK_PRODUCER( mlt_tractor_multitrack( tractor ) ), in, out );
+ mlt_producer_set_in_and_out( MLT_TRACTOR_PRODUCER( tractor ), in, out );
+ mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( mix->producer ), "length", out - in + 1 );
+ mlt_producer_set_in_and_out( mix->producer, in, out );
+
+ mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+ mlt_playlist_virtual_refresh( this );
+ }
+ return error;
+}
+
+/** Consolidate adjacent blank producers.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param keep_length set false to remove the last entry if it is blank
+ */
+
+void mlt_playlist_consolidate_blanks( mlt_playlist this, int keep_length )
+{
+ if ( this != NULL )
+ {
+ int i = 0;
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+
+ mlt_events_block( properties, properties );
+ for ( i = 1; i < this->count; i ++ )
+ {
+ playlist_entry *left = this->list[ i - 1 ];
+ playlist_entry *right = this->list[ i ];
+
+ if ( mlt_producer_is_blank( left->producer ) && mlt_producer_is_blank( right->producer ) )
+ {
+ mlt_playlist_resize_clip( this, i - 1, 0, left->frame_count + right->frame_count - 1 );
+ mlt_playlist_remove( this, i -- );
+ }
+ }
+
+ if ( !keep_length && this->count > 0 )
+ {
+ playlist_entry *last = this->list[ this->count - 1 ];
+ if ( mlt_producer_is_blank( last->producer ) )
+ mlt_playlist_remove( this, this->count - 1 );
+ }
+
+ mlt_events_unblock( properties, properties );
+ mlt_playlist_virtual_refresh( this );
+ }
+}
+
+/** Determine if the specified clip index is a blank.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return true if there was an error
+ */
+
+int mlt_playlist_is_blank( mlt_playlist this, int clip )
+{
+ return this == NULL || mlt_producer_is_blank( mlt_playlist_get_clip( this, clip ) );
+}
+
+/** Determine if the specified position is a blank.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position a time relative to the start or end (negative) of the playlist
+ * \return true if there was an error
+ */
+
+int mlt_playlist_is_blank_at( mlt_playlist this, mlt_position position )
+{
+ return this == NULL || mlt_producer_is_blank( mlt_playlist_get_clip_at( this, position ) );
+}
+
+/** Replace the specified clip with a blank and return the clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return a producer or NULL if there was an error
+ */
+
+mlt_producer mlt_playlist_replace_with_blank( mlt_playlist this, int clip )
+{
+ mlt_producer producer = NULL;
+ if ( !mlt_playlist_is_blank( this, clip ) )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ int in = entry->frame_in;
+ int out = entry->frame_out;
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+ producer = entry->producer;
+ mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
+ mlt_events_block( properties, properties );
+ mlt_playlist_remove( this, clip );
+ mlt_playlist_blank( this, out - in );
+ mlt_playlist_move( this, this->count - 1, clip );
+ mlt_events_unblock( properties, properties );
+ mlt_playlist_virtual_refresh( this );
+ mlt_producer_set_in_and_out( producer, in, out );
+ }
+ return producer;
+}
+
+/** Insert blank space.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the new blank section
+ * \param length the ending time of the new blank section (duration - 1)
+ */
+
+void mlt_playlist_insert_blank( mlt_playlist this, int clip, int length )
+{
+ if ( this != NULL && length >= 0 )
+ {
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+ mlt_events_block( properties, properties );
+ mlt_playlist_blank( this, length );
+ mlt_playlist_move( this, this->count - 1, clip );
+ mlt_events_unblock( properties, properties );
+ mlt_playlist_virtual_refresh( this );
+ }
+}
+
+/** Resize a blank entry.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the time at which the blank entry exists relative to the start or end (negative) of the playlist.
+ * \param length the additional amount of blank frames to add
+ * \param find true to fist locate the blank after the clip at position
+ */
+void mlt_playlist_pad_blanks( mlt_playlist this, mlt_position position, int length, int find )
+{
+ if ( this != NULL && length != 0 )
+ {
+ int clip = mlt_playlist_get_clip_index_at( this, position );
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+ mlt_events_block( properties, properties );
+ if ( find && clip < this->count && !mlt_playlist_is_blank( this, clip ) )
+ clip ++;
+ if ( clip < this->count && mlt_playlist_is_blank( this, clip ) )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( this, &info, clip );
+ if ( info.frame_out + length > info.frame_in )
+ mlt_playlist_resize_clip( this, clip, info.frame_in, info.frame_out + length );
+ else
+ mlt_playlist_remove( this, clip );
+ }
+ else if ( find && clip < this->count && length > 0 )
+ {
+ mlt_playlist_insert_blank( this, clip, length );
+ }
+ mlt_events_unblock( properties, properties );
+ mlt_playlist_virtual_refresh( this );
+ }
+}
+
+/** Insert a clip at a specific time.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the time at which to insert
+ * \param producer the producer to insert
+ * \param mode true if you want to overwrite any blank section
+ * \return true if there was an error
+ */
+
+int mlt_playlist_insert_at( mlt_playlist this, mlt_position position, mlt_producer producer, int mode )
+{
+ int ret = this == NULL || position < 0 || producer == NULL;
+ if ( ret == 0 )
+ {
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+ int length = mlt_producer_get_playtime( producer );
+ int clip = mlt_playlist_get_clip_index_at( this, position );
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( this, &info, clip );
+ mlt_events_block( properties, this );
+ if ( clip < this->count && mlt_playlist_is_blank( this, clip ) )
+ {
+ // Split and move to new clip if need be
+ if ( position != info.start && mlt_playlist_split( this, clip, position - info.start - 1 ) == 0 )
+ mlt_playlist_get_clip_info( this, &info, ++ clip );
+
+ // Split again if need be
+ if ( length < info.frame_count )
+ mlt_playlist_split( this, clip, length - 1 );
+
+ // Remove
+ mlt_playlist_remove( this, clip );
+
+ // Insert
+ mlt_playlist_insert( this, producer, clip, -1, -1 );
+ ret = clip;
+ }
+ else if ( clip < this->count )
+ {
+ if ( position > info.start + info.frame_count / 2 )
+ clip ++;
+ if ( mode == 1 && clip < this->count && mlt_playlist_is_blank( this, clip ) )
+ {
+ mlt_playlist_get_clip_info( this, &info, clip );
+ if ( length < info.frame_count )
+ mlt_playlist_split( this, clip, length );
+ mlt_playlist_remove( this, clip );
+ }
+ mlt_playlist_insert( this, producer, clip, -1, -1 );
+ ret = clip;
+ }
+ else
+ {
+ if ( mode == 1 ) {
+ if ( position == info.start )
+ mlt_playlist_remove( this, clip );
+ else
+ mlt_playlist_blank( this, position - mlt_properties_get_int( properties, "length" ) - 1 );
+ }
+ mlt_playlist_append( this, producer );
+ ret = this->count - 1;
+ }
+ mlt_events_unblock( properties, this );
+ mlt_playlist_virtual_refresh( this );
+ }
+ else
+ {
+ ret = -1;
+ }
+ return ret;
+}
+
+/** Get the time at which the clip starts relative to the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return the starting time
+ */
+
+int mlt_playlist_clip_start( mlt_playlist this, int clip )
+{
+ mlt_playlist_clip_info info;
+ if ( mlt_playlist_get_clip_info( this, &info, clip ) == 0 )
+ return info.start;
+ return clip < 0 ? 0 : mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) );
+}
+
+/** Get the playable duration of the clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return the duration of the playlist entry
+ */
+
+int mlt_playlist_clip_length( mlt_playlist this, int clip )
+{
+ mlt_playlist_clip_info info;
+ if ( mlt_playlist_get_clip_info( this, &info, clip ) == 0 )
+ return info.frame_count;
+ return 0;
+}
+
+/** Get the duration of a blank space.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param bounded the maximum number of blank entries or 0 for all
+ * \return the duration of a blank section
+ */
+
+int mlt_playlist_blanks_from( mlt_playlist this, int clip, int bounded )
+{
+ int count = 0;
+ mlt_playlist_clip_info info;
+ if ( this != NULL && clip < this->count )
+ {
+ mlt_playlist_get_clip_info( this, &info, clip );
+ if ( mlt_playlist_is_blank( this, clip ) )
+ count += info.frame_count;
+ if ( bounded == 0 )
+ bounded = this->count;
+ for ( clip ++; clip < this->count && bounded >= 0; clip ++ )
+ {
+ mlt_playlist_get_clip_info( this, &info, clip );
+ if ( mlt_playlist_is_blank( this, clip ) )
+ count += info.frame_count;
+ else
+ bounded --;
+ }
+ }
+ return count;
+}
+
+/** Remove a portion of the playlist by time.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the starting time
+ * \param length the duration of time to remove
+ * \return the new entry index at the position
+ */
+
+int mlt_playlist_remove_region( mlt_playlist this, mlt_position position, int length )
+{
+ int index = mlt_playlist_get_clip_index_at( this, position );
+ if ( index >= 0 && index < this->count )
+ {
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+ int clip_start = mlt_playlist_clip_start( this, index );
+ int clip_length = mlt_playlist_clip_length( this, index );
+ int list_length = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) );
+ mlt_events_block( properties, this );
+
+ if ( position + length > list_length )
+ length -= ( position + length - list_length );
+
+ if ( clip_start < position )
+ {
+ mlt_playlist_split( this, index ++, position - clip_start );
+ clip_length -= position - clip_start;
+ }
+
+ while( length > 0 )
+ {
+ if ( mlt_playlist_clip_length( this, index ) > length )
+ mlt_playlist_split( this, index, length );
+ length -= mlt_playlist_clip_length( this, index );
+ mlt_playlist_remove( this, index );
+ }
+
+ mlt_playlist_consolidate_blanks( this, 0 );
+ mlt_events_unblock( properties, this );
+ mlt_playlist_virtual_refresh( this );
+
+ // Just to be sure, we'll get the clip index again...
+ index = mlt_playlist_get_clip_index_at( this, position );
+ }
+ return index;
+}
+
+/** Not implemented
+ *
+ * \deprecated not implemented
+ * \public \memberof mlt_playlist_s
+ * \param this
+ * \param position
+ * \param length
+ * \param new_position
+ * \return
+ */
+
+int mlt_playlist_move_region( mlt_playlist this, mlt_position position, int length, int new_position )
+{
+ if ( this != NULL )
+ {
+ }
+ return 0;
+}
+
+/** Get the current frame.
+ *
+ * The implementation of the get_frame virtual function.
+ * \private \memberof mlt_playlist_s
+ * \param producer a producer
+ * \param frame a frame by reference
+ * \param index the time at which to get the frame
+ * \return false
+ */
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Check that we have a producer
+ if ( producer == NULL )
+ {
+ *frame = NULL;
+ return -1;
+ }
+
+ // Get this mlt_playlist
+ mlt_playlist this = producer->child;
+
+ // Need to ensure the frame is deinterlaced when repeating 1 frame
+ int progressive = 0;
+
+ // Get the real producer
+ mlt_service real = mlt_playlist_virtual_seek( this, &progressive );
+
+ // Check that we have a producer
+ if ( real == NULL )
+ {
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+ return 0;
+ }
+
+ // Get the frame
+ if ( !mlt_properties_get_int( MLT_SERVICE_PROPERTIES( real ), "meta.fx_cut" ) )
+ {
+ mlt_service_get_frame( real, frame, index );
+ }
+ else
+ {
+ mlt_producer parent = mlt_producer_cut_parent( ( mlt_producer )real );
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "fx_cut", 1 );
+ mlt_frame_push_service( *frame, NULL );
+ mlt_frame_push_audio( *frame, NULL );
+ mlt_service_apply_filters( MLT_PRODUCER_SERVICE( parent ), *frame, 0 );
+ mlt_service_apply_filters( real, *frame, 0 );
+ mlt_deque_pop_front( MLT_FRAME_IMAGE_STACK( *frame ) );
+ mlt_deque_pop_front( MLT_FRAME_AUDIO_STACK( *frame ) );
+ }
+
+ // Check if we're at the end of the clip
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+ if ( mlt_properties_get_int( properties, "end_of_clip" ) )
+ mlt_playlist_virtual_set_out( this );
+
+ // Set the consumer progressive property
+ if ( progressive )
+ {
+ mlt_properties_set_int( properties, "consumer_deinterlace", progressive );
+ mlt_properties_set_int( properties, "test_audio", 1 );
+ }
+
+ // Check for notifier and call with appropriate argument
+ mlt_properties playlist_properties = MLT_PRODUCER_PROPERTIES( producer );
+ void ( *notifier )( void * ) = mlt_properties_get_data( playlist_properties, "notifier", NULL );
+ if ( notifier != NULL )
+ {
+ void *argument = mlt_properties_get_data( playlist_properties, "notifier_arg", NULL );
+ notifier( argument );
+ }
+
+ // Update position on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_frame( producer ) );
+
+ // Position ourselves on the next frame
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+/** Close the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ */
+
+void mlt_playlist_close( mlt_playlist this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_PLAYLIST_PROPERTIES( this ) ) <= 0 )
+ {
+ int i = 0;
+ this->parent.close = NULL;
+ for ( i = 0; i < this->count; i ++ )
+ {
+ mlt_event_close( this->list[ i ]->event );
+ mlt_producer_close( this->list[ i ]->producer );
+ free( this->list[ i ] );
+ }
+ mlt_producer_close( &this->blank );
+ mlt_producer_close( &this->parent );
+ free( this->list );
+ free( this );
+ }
+}
--- /dev/null
+/**
+ * \file mlt_playlist.h
+ * \brief playlist service class
+ * \see mlt_playlist_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_PLAYLIST_H_
+#define _MLT_PLAYLIST_H_
+
+#include "mlt_producer.h"
+
+/** \brief structure for returning clip information from a playlist entry
+ */
+
+typedef struct
+{
+ int clip; /**< the index of the clip within the playlist */
+ mlt_producer producer; /**< the clip's producer (or parent producer of a cut) */
+ mlt_producer cut; /**< the clips' cut producer */
+ mlt_position start; /**< the time this begins relative to the beginning of the playlist */
+ char *resource; /**< the file name or address of the clip */
+ mlt_position frame_in; /**< the clip's in point */
+ mlt_position frame_out; /**< the clip's out point */
+ mlt_position frame_count; /**< the duration of the clip */
+ mlt_position length; /**< the unedited duration of the clip */
+ float fps; /**< the frame rate of the clip */
+ int repeat; /**< the number of times the clip is repeated */
+}
+mlt_playlist_clip_info;
+
+/** Playlist Entry
+*/
+
+typedef struct playlist_entry_s playlist_entry;
+
+/** \brief Playlist class
+ *
+ * A playlist is a sequential container of producers and blank spaces. The class provides all
+ * sorts of playlist assembly and manipulation routines. A playlist is also a producer within
+ * the framework.
+ *
+ * \extends mlt_producer_s
+ * \properties \em autoclose Set this true if you are doing sequential processing and want to
+ * automatically close producers as they are finished being used to free resources.
+ * \properties \em meta.fx_cut Set true on a producer to indicate that it is a "fx_cut,"
+ * which is a way to add filters as a playlist entry - useful only in a multitrack. See FxCut on the wiki.
+ * \properties \em mix_in
+ * \properties \em mix_out
+ */
+
+struct mlt_playlist_s
+{
+ struct mlt_producer_s parent;
+ struct mlt_producer_s blank;
+
+ int size;
+ int count;
+ playlist_entry **list;
+};
+
+#define MLT_PLAYLIST_PRODUCER( playlist ) ( &( playlist )->parent )
+#define MLT_PLAYLIST_SERVICE( playlist ) MLT_PRODUCER_SERVICE( MLT_PLAYLIST_PRODUCER( playlist ) )
+#define MLT_PLAYLIST_PROPERTIES( playlist ) MLT_SERVICE_PROPERTIES( MLT_PLAYLIST_SERVICE( playlist ) )
+
+extern mlt_playlist mlt_playlist_init( );
+extern mlt_producer mlt_playlist_producer( mlt_playlist self );
+extern mlt_service mlt_playlist_service( mlt_playlist self );
+extern mlt_properties mlt_playlist_properties( mlt_playlist self );
+extern int mlt_playlist_count( mlt_playlist self );
+extern int mlt_playlist_clear( mlt_playlist self );
+extern int mlt_playlist_append( mlt_playlist self, mlt_producer producer );
+extern int mlt_playlist_append_io( mlt_playlist self, mlt_producer producer, mlt_position in, mlt_position out );
+extern int mlt_playlist_blank( mlt_playlist self, mlt_position length );
+extern mlt_position mlt_playlist_clip( mlt_playlist self, mlt_whence whence, int index );
+extern int mlt_playlist_current_clip( mlt_playlist self );
+extern mlt_producer mlt_playlist_current( mlt_playlist self );
+extern int mlt_playlist_get_clip_info( mlt_playlist self, mlt_playlist_clip_info *info, int index );
+extern int mlt_playlist_insert( mlt_playlist self, mlt_producer producer, int where, mlt_position in, mlt_position out );
+extern int mlt_playlist_remove( mlt_playlist self, int where );
+extern int mlt_playlist_move( mlt_playlist self, int from, int to );
+extern int mlt_playlist_resize_clip( mlt_playlist self, int clip, mlt_position in, mlt_position out );
+extern int mlt_playlist_repeat_clip( mlt_playlist self, int clip, int repeat );
+extern int mlt_playlist_split( mlt_playlist self, int clip, mlt_position position );
+extern int mlt_playlist_split_at( mlt_playlist self, mlt_position position, int left );
+extern int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge );
+extern int mlt_playlist_mix( mlt_playlist self, int clip, int length, mlt_transition transition );
+extern int mlt_playlist_mix_add( mlt_playlist self, int clip, mlt_transition transition );
+extern mlt_producer mlt_playlist_get_clip( mlt_playlist self, int clip );
+extern mlt_producer mlt_playlist_get_clip_at( mlt_playlist self, mlt_position position );
+extern int mlt_playlist_get_clip_index_at( mlt_playlist self, mlt_position position );
+extern int mlt_playlist_clip_is_mix( mlt_playlist self, int clip );
+extern void mlt_playlist_consolidate_blanks( mlt_playlist self, int keep_length );
+extern int mlt_playlist_is_blank( mlt_playlist self, int clip );
+extern int mlt_playlist_is_blank_at( mlt_playlist self, mlt_position position );
+extern void mlt_playlist_insert_blank( mlt_playlist self, int clip, int length );
+extern void mlt_playlist_pad_blanks( mlt_playlist self, mlt_position position, int length, int find );
+extern mlt_producer mlt_playlist_replace_with_blank( mlt_playlist self, int clip );
+extern int mlt_playlist_insert_at( mlt_playlist self, mlt_position position, mlt_producer producer, int mode );
+extern int mlt_playlist_clip_start( mlt_playlist self, int clip );
+extern int mlt_playlist_clip_length( mlt_playlist self, int clip );
+extern int mlt_playlist_blanks_from( mlt_playlist self, int clip, int bounded );
+extern int mlt_playlist_remove_region( mlt_playlist self, mlt_position position, int length );
+extern int mlt_playlist_move_region( mlt_playlist self, mlt_position position, int length, int new_position );
+extern void mlt_playlist_close( mlt_playlist self );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_pool.c
+ * \brief memory pooling functionality
+ * \see mlt_pool_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_properties.h"
+#include "mlt_deque.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+// Not nice - memalign is defined here apparently?
+#ifdef linux
+#include <malloc.h>
+#endif
+
+/** global singleton for tracking pools */
+
+static mlt_properties pools = NULL;
+
+/** \brief Pool (memory) class
+ */
+
+typedef struct mlt_pool_s
+{
+ pthread_mutex_t lock; ///< lock to prevent race conditions
+ mlt_deque stack; ///< a stack of addresses to memory blocks
+ int size; ///< the size of the memory block as a power of 2
+ int count; ///< the number of blocks in the pool
+}
+*mlt_pool;
+
+/** \brief private to mlt_pool_s, for tracking items to release
+ */
+
+typedef struct mlt_release_s
+{
+ mlt_pool pool;
+ int references;
+}
+*mlt_release;
+
+/** Create a pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param size the size of the memory blocks to hold as some power of two
+ * \return a new pool object
+ */
+
+static mlt_pool pool_init( int size )
+{
+ // Create the pool
+ mlt_pool this = calloc( 1, sizeof( struct mlt_pool_s ) );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Initialise the mutex
+ pthread_mutex_init( &this->lock, NULL );
+
+ // Create the stack
+ this->stack = mlt_deque_init( );
+
+ // Assign the size
+ this->size = size;
+ }
+
+ // Return it
+ return this;
+}
+
+/** Get an item from the pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param this a pool
+ * \return an opaque pointer
+ */
+
+static void *pool_fetch( mlt_pool this )
+{
+ // We will generate a release object
+ void *ptr = NULL;
+
+ // Sanity check
+ if ( this != NULL )
+ {
+ // Lock the pool
+ pthread_mutex_lock( &this->lock );
+
+ // Check if the stack is empty
+ if ( mlt_deque_count( this->stack ) != 0 )
+ {
+ // Pop the top of the stack
+ ptr = mlt_deque_pop_back( this->stack );
+
+ // Assign the reference
+ ( ( mlt_release )ptr )->references = 1;
+ }
+ else
+ {
+ // We need to generate a release item
+#ifdef linux
+ mlt_release release = memalign( 16, this->size );
+#else
+ mlt_release release = malloc( this->size );
+#endif
+
+ // Initialise it
+ if ( release != NULL )
+ {
+ // Increment the number of items allocated to this pool
+ this->count ++;
+
+ // Assign the pool
+ release->pool = this;
+
+ // Assign the reference
+ release->references = 1;
+
+ // Determine the ptr
+ ptr = ( char * )release + sizeof( struct mlt_release_s );
+ }
+ }
+
+ // Unlock the pool
+ pthread_mutex_unlock( &this->lock );
+ }
+
+ // Return the generated release object
+ return ptr;
+}
+
+/** Return an item to the pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param ptr an opaque pointer
+ */
+
+static void pool_return( void *ptr )
+{
+ // Sanity checks
+ if ( ptr != NULL )
+ {
+ // Get the release pointer
+ mlt_release that = ( void * )(( char * )ptr - sizeof( struct mlt_release_s ));
+
+ // Get the pool
+ mlt_pool this = that->pool;
+
+ if ( this != NULL )
+ {
+ // Lock the pool
+ pthread_mutex_lock( &this->lock );
+
+ // Push the that back back on to the stack
+ mlt_deque_push_back( this->stack, ptr );
+
+ // Unlock the pool
+ pthread_mutex_unlock( &this->lock );
+
+ // Ensure that we don't clean up
+ ptr = NULL;
+ }
+ }
+
+ // Tidy up - this will only occur if the returned item is incorrect
+ if ( ptr != NULL )
+ {
+ // Free the release itself
+ free( ( char * )ptr - sizeof( struct mlt_release_s ) );
+ }
+}
+
+/** Destroy a pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param this a pool
+ */
+
+static void pool_close( mlt_pool this )
+{
+ if ( this != NULL )
+ {
+ // We need to free up all items in the pool
+ void *release = NULL;
+
+ // Iterate through the stack until depleted
+ while ( ( release = mlt_deque_pop_back( this->stack ) ) != NULL )
+ {
+ // We'll free this item now
+ free( ( char * )release - sizeof( struct mlt_release_s ) );
+ }
+
+ // We can now close the stack
+ mlt_deque_close( this->stack );
+
+ // Destroy the mutex
+ pthread_mutex_destroy( &this->lock );
+
+ // Close the pool
+ free( this );
+ }
+}
+
+/** Initialise the global pool.
+ *
+ * \public \memberof mlt_pool_s
+ */
+
+void mlt_pool_init( )
+{
+ // Loop variable used to create the pools
+ int i = 0;
+
+ // Create the pools
+ pools = mlt_properties_new( );
+
+ // Create the pools
+ for ( i = 8; i < 31; i ++ )
+ {
+ // Each properties item needs a name
+ char name[ 32 ];
+
+ // Construct a pool
+ mlt_pool pool = pool_init( 1 << i );
+
+ // Generate a name
+ sprintf( name, "%d", i );
+
+ // Register with properties
+ mlt_properties_set_data( pools, name, pool, 0, ( mlt_destructor )pool_close, NULL );
+ }
+}
+
+/** Allocate size bytes from the pool.
+ *
+ * \public \memberof mlt_pool_s
+ * \param size the number of bytes
+ */
+
+void *mlt_pool_alloc( int size )
+{
+ // This will be used to obtain the pool to use
+ mlt_pool pool = NULL;
+
+ // Determines the index of the pool to use
+ int index = 8;
+
+ // Minimum size pooled is 256 bytes
+ size = size + sizeof( mlt_release );
+ while ( ( 1 << index ) < size )
+ index ++;
+
+ // Now get the pool at the index
+ pool = mlt_properties_get_data_at( pools, index - 8, NULL );
+
+ // Now get the real item
+ return pool_fetch( pool );
+}
+
+/** Allocate size bytes from the pool.
+ *
+ * \public \memberof mlt_pool_s
+ * \param ptr an opaque pointer - can be in the pool or a new block to allocate
+ * \param size the number of bytes
+ */
+
+void *mlt_pool_realloc( void *ptr, int size )
+{
+ // Result to return
+ void *result = NULL;
+
+ // Check if we actually have an address
+ if ( ptr != NULL )
+ {
+ // Get the release pointer
+ mlt_release that = ( void * )(( char * )ptr - sizeof( struct mlt_release_s ));
+
+ // If the current pool this ptr belongs to is big enough
+ if ( size > that->pool->size - sizeof( struct mlt_release_s ) )
+ {
+ // Allocate
+ result = mlt_pool_alloc( size );
+
+ // Copy
+ memcpy( result, ptr, that->pool->size - sizeof( struct mlt_release_s ) );
+
+ // Release
+ mlt_pool_release( ptr );
+ }
+ else
+ {
+ // Nothing to do
+ result = ptr;
+ }
+ }
+ else
+ {
+ // Simply allocate
+ result = mlt_pool_alloc( size );
+ }
+
+ return result;
+}
+
+/** Purge unused items in the pool.
+ *
+ * A form of garbage collection.
+ * \public \memberof mlt_pool_s
+ */
+
+void mlt_pool_purge( )
+{
+ int i = 0;
+
+ // For each pool
+ for ( i = 0; i < mlt_properties_count( pools ); i ++ )
+ {
+ // Get the pool
+ mlt_pool this = mlt_properties_get_data_at( pools, i, NULL );
+
+ // Pointer to unused memory
+ void *release = NULL;
+
+ // Lock the pool
+ pthread_mutex_lock( &this->lock );
+
+ // We'll free all unused items now
+ while ( ( release = mlt_deque_pop_back( this->stack ) ) != NULL )
+ free( ( char * )release - sizeof( struct mlt_release_s ) );
+
+ // Unlock the pool
+ pthread_mutex_unlock( &this->lock );
+ }
+}
+
+/** Release the allocated memory.
+ *
+ * \public \memberof mlt_pool_s
+ * \param release an opaque pointer of a block in the pool
+ */
+
+void mlt_pool_release( void *release )
+{
+ // Return to the pool
+ pool_return( release );
+}
+
+/** Close the pool.
+ *
+ * \public \memberof mlt_pool_s
+ */
+
+void mlt_pool_close( )
+{
+#ifdef _MLT_POOL_CHECKS_
+ // Stats dump on close
+ int i = 0;
+ for ( i = 0; i < mlt_properties_count( pools ); i ++ )
+ {
+ mlt_pool pool = mlt_properties_get_data_at( pools, i, NULL );
+ if ( pool->count )
+ mlt_log( NULL, MLT_LOG_DEBUG, "%s: size %d allocated %d returned %d %c\n", __FUNCTION__,
+ pool->size, pool->count, mlt_deque_count( pool->stack ),
+ pool->count != mlt_deque_count( pool->stack ) ? '*' : ' ' );
+ }
+#endif
+
+ // Close the properties
+ mlt_properties_close( pools );
+}
+
--- /dev/null
+/**
+ * \file mlt_pool.h
+ * \brief memory pooling functionality
+ * \see mlt_pool_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_POOL_H
+#define _MLT_POOL_H
+
+extern void mlt_pool_init( );
+extern void *mlt_pool_alloc( int size );
+extern void *mlt_pool_realloc( void *ptr, int size );
+extern void mlt_pool_release( void *release );
+extern void mlt_pool_purge( );
+extern void mlt_pool_close( );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_producer.c
+ * \brief abstraction for all producer services
+ * \see mlt_producer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_producer.h"
+#include "mlt_factory.h"
+#include "mlt_frame.h"
+#include "mlt_parser.h"
+#include "mlt_profile.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Forward references. */
+
+static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+static void mlt_producer_property_changed( mlt_service owner, mlt_producer this, char *name );
+static void mlt_producer_service_changed( mlt_service owner, mlt_producer this );
+
+/* for debugging */
+//#define _MLT_PRODUCER_CHECKS_ 1
+#ifdef _MLT_PRODUCER_CHECKS_
+static int producers_created = 0;
+static int producers_destroyed = 0;
+#endif
+
+/** Initialize a producer service.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this the producer structure to initialize
+ * \param child a pointer to the child object for the subclass
+ * \return true if there was an error
+ */
+
+int mlt_producer_init( mlt_producer this, void *child )
+{
+ // Check that we haven't received NULL
+ int error = this == NULL;
+
+ // Continue if no error
+ if ( error == 0 )
+ {
+#ifdef _MLT_PRODUCER_CHECKS_
+ producers_created ++;
+#endif
+
+ // Initialise the producer
+ memset( this, 0, sizeof( struct mlt_producer_s ) );
+
+ // Associate with the child
+ this->child = child;
+
+ // Initialise the service
+ if ( mlt_service_init( &this->parent, this ) == 0 )
+ {
+ // The parent is the service
+ mlt_service parent = &this->parent;
+
+ // Define the parent close
+ parent->close = ( mlt_destructor )mlt_producer_close;
+ parent->close_object = this;
+
+ // For convenience, we'll assume the close_object is this
+ this->close_object = this;
+
+ // Get the properties of the parent
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
+
+ // Set the default properties
+ mlt_properties_set( properties, "mlt_type", "mlt_producer" );
+ mlt_properties_set_position( properties, "_position", 0.0 );
+ mlt_properties_set_double( properties, "_frame", 0 );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( NULL ) );
+ mlt_properties_set_double( properties, "_speed", 1.0 );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", 14999 );
+ mlt_properties_set_position( properties, "length", 15000 );
+ mlt_properties_set( properties, "eof", "pause" );
+ mlt_properties_set( properties, "resource", "<producer>" );
+
+ // Override service get_frame
+ parent->get_frame = producer_get_frame;
+
+ mlt_events_listen( properties, this, "service-changed", ( mlt_listener )mlt_producer_service_changed );
+ mlt_events_listen( properties, this, "property-changed", ( mlt_listener )mlt_producer_property_changed );
+ mlt_events_register( properties, "producer-changed", NULL );
+ }
+ }
+
+ return error;
+}
+
+/** Listener for property changes.
+ *
+ * If the in, out, or length properties changed, fire a "producer-changed" event.
+ *
+ * \private \memberof mlt_producer_s
+ * \param owner a service (ignored)
+ * \param this the producer
+ * \param name the property that changed
+ */
+
+static void mlt_producer_property_changed( mlt_service owner, mlt_producer this, char *name )
+{
+ if ( !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "length" ) )
+ mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), "producer-changed", NULL );
+}
+
+/** Listener for service changes.
+ *
+ * Fires the "producer-changed" event.
+ *
+ * \private \memberof mlt_producer_s
+ * \param owner a service (ignored)
+ * \param this the producer
+ */
+
+static void mlt_producer_service_changed( mlt_service owner, mlt_producer this )
+{
+ mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), "producer-changed", NULL );
+}
+
+/** Create and initialize a new producer.
+ *
+ * \public \memberof mlt_producer_s
+ * \return the new producer
+ */
+
+mlt_producer mlt_producer_new( )
+{
+ mlt_producer this = malloc( sizeof( struct mlt_producer_s ) );
+ mlt_producer_init( this, NULL );
+ return this;
+}
+
+/** Determine if producer is a cut.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if \p this is a "cut" producer
+ * \see mlt_producer_cut
+ */
+
+int mlt_producer_is_cut( mlt_producer this )
+{
+ return mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( this ), "_cut" );
+}
+
+/** Determine if producer is a mix.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if this is a "mix" producer
+ * \todo Define a mix producer.
+ */
+
+int mlt_producer_is_mix( mlt_producer this )
+{
+ mlt_properties properties = this != NULL ? MLT_PRODUCER_PROPERTIES( this ) : NULL;
+ mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
+ return tractor != NULL;
+}
+
+/** Determine if the producer is a blank.
+ *
+ * Blank producers should only appear as an item in a playlist.
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if this is a "blank" producer
+ * \see mlt_playlist_insert_blank
+ */
+
+int mlt_producer_is_blank( mlt_producer this )
+{
+ return this == NULL || !strcmp( mlt_properties_get( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), "resource" ), "blank" );
+}
+
+/** Obtain the parent producer.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return either the parent producer if this is a "cut" producer or \p this otherwise.
+ */
+
+mlt_producer mlt_producer_cut_parent( mlt_producer this )
+{
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+ if ( mlt_producer_is_cut( this ) )
+ return mlt_properties_get_data( properties, "_cut_parent", NULL );
+ else
+ return this;
+}
+
+/** Create a cut of this producer.
+ *
+ * A "cut" is a portion of another (parent) producer.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param in the beginning
+ * \param out the end
+ * \return the new producer
+ * \todo Expand on the value of a cut.
+ */
+
+mlt_producer mlt_producer_cut( mlt_producer this, int in, int out )
+{
+ mlt_producer result = mlt_producer_new( );
+ mlt_producer parent = mlt_producer_cut_parent( this );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( result );
+ mlt_properties parent_props = MLT_PRODUCER_PROPERTIES( parent );
+
+ mlt_events_block( MLT_PRODUCER_PROPERTIES( result ), MLT_PRODUCER_PROPERTIES( result ) );
+ // Special case - allow for a cut of the entire producer (this will squeeze all other cuts to 0)
+ if ( in <= 0 )
+ in = 0;
+ if ( ( out < 0 || out >= mlt_producer_get_length( parent ) ) && !mlt_producer_is_blank( this ) )
+ out = mlt_producer_get_length( parent ) - 1;
+
+ mlt_properties_inc_ref( parent_props );
+ mlt_properties_set_int( properties, "_cut", 1 );
+ mlt_properties_set_data( properties, "_cut_parent", parent, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ mlt_properties_set_position( properties, "length", mlt_properties_get_position( parent_props, "length" ) );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( parent_props, "aspect_ratio" ) );
+ mlt_producer_set_in_and_out( result, in, out );
+
+ return result;
+}
+
+/** Get the parent service object.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the service parent class
+ * \see MLT_PRODUCER_SERVICE
+ */
+
+mlt_service mlt_producer_service( mlt_producer this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the producer properties.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the producer's property list
+ * \see MLT_PRODUCER_PROPERTIES
+ */
+
+mlt_properties mlt_producer_properties( mlt_producer this )
+{
+ return MLT_SERVICE_PROPERTIES( &this->parent );
+}
+
+/** Seek to a specified position.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param position set the "play head" position of the producer
+ * \return false
+ * \todo Document how the properties affect behavior.
+ */
+
+int mlt_producer_seek( mlt_producer this, mlt_position position )
+{
+ // Determine eof handling
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+ char *eof = mlt_properties_get( properties, "eof" );
+ int use_points = 1 - mlt_properties_get_int( properties, "ignore_points" );
+
+ // Recursive behaviour for cuts - repositions parent and then repositions cut
+ // hence no return on this condition
+ if ( mlt_producer_is_cut( this ) )
+ mlt_producer_seek( mlt_producer_cut_parent( this ), position + mlt_producer_get_in( this ) );
+
+ // Check bounds
+ if ( position < 0 || mlt_producer_get_playtime( this ) == 0 )
+ {
+ position = 0;
+ }
+ else if ( use_points && ( eof == NULL || !strcmp( eof, "pause" ) ) && position >= mlt_producer_get_playtime( this ) )
+ {
+ mlt_producer_set_speed( this, 0 );
+ position = mlt_producer_get_playtime( this ) - 1;
+ }
+ else if ( use_points && !strcmp( eof, "loop" ) && position >= mlt_producer_get_playtime( this ) )
+ {
+ position = (int)position % (int)mlt_producer_get_playtime( this );
+ }
+
+ // Set the position
+ mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( this ), "_position", position );
+
+ // Calculate the absolute frame
+ mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( this ), "_frame", use_points * mlt_producer_get_in( this ) + position );
+
+ return 0;
+}
+
+/** Get the current position (relative to in point).
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the position of the "play head" relative to its beginning
+ */
+
+mlt_position mlt_producer_position( mlt_producer this )
+{
+ return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "_position" );
+}
+
+/** Get the current position (relative to start of producer).
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the position of the "play head" regardless of the in point
+ */
+
+mlt_position mlt_producer_frame( mlt_producer this )
+{
+ return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "_frame" );
+}
+
+/** Set the playing speed.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param speed the new speed as a relative factor (1.0 = normal)
+ * \return
+ */
+
+int mlt_producer_set_speed( mlt_producer this, double speed )
+{
+ return mlt_properties_set_double( MLT_PRODUCER_PROPERTIES( this ), "_speed", speed );
+}
+
+/** Get the playing speed.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the speed as a relative factor (1.0 = normal)
+ */
+
+double mlt_producer_get_speed( mlt_producer this )
+{
+ return mlt_properties_get_double( MLT_PRODUCER_PROPERTIES( this ), "_speed" );
+}
+
+/** Get the frames per second.
+ *
+ * This is determined by the producer's profile.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the video refresh rate
+ */
+
+double mlt_producer_get_fps( mlt_producer this )
+{
+ mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) );
+ return mlt_profile_fps( profile );
+}
+
+/** Set the in and out points.
+ *
+ * The in point is where play out should start relative to the natural start
+ * of the underlying file. The out point is where play out should end, also
+ * relative to the start of the underlying file. If the underlying resource is
+ * a live stream, then the in point is an offset relative to first usable
+ * sample.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param in the relative starting time
+ * \param out the relative ending time
+ * \return false
+ */
+
+int mlt_producer_set_in_and_out( mlt_producer this, mlt_position in, mlt_position out )
+{
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Correct ins and outs if necessary
+ if ( in < 0 )
+ in = 0;
+ else if ( in >= mlt_producer_get_length( this ) )
+ in = mlt_producer_get_length( this ) - 1;
+
+ if ( out < 0 )
+ out = 0;
+ else if ( out >= mlt_producer_get_length( this ) && !mlt_producer_is_blank( this ) )
+ out = mlt_producer_get_length( this ) - 1;
+ else if ( out >= mlt_producer_get_length( this ) && mlt_producer_is_blank( this ) )
+ mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( this ), "length", out + 1 );
+
+ // Swap ins and outs if wrong
+ if ( out < in )
+ {
+ mlt_position t = in;
+ in = out;
+ out = t;
+ }
+
+ // Set the values
+ mlt_events_block( properties, properties );
+ mlt_properties_set_position( properties, "in", in );
+ mlt_events_unblock( properties, properties );
+ mlt_properties_set_position( properties, "out", out );
+
+ return 0;
+}
+
+/** Physically reduce the producer (typically a cut) to a 0 length.
+ * Essentially, all 0 length cuts should be immediately removed by containers.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return false
+ */
+
+int mlt_producer_clear( mlt_producer this )
+{
+ if ( this != NULL )
+ {
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+ mlt_events_block( properties, properties );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_events_unblock( properties, properties );
+ mlt_properties_set_position( properties, "out", -1 );
+ }
+ return 0;
+}
+
+/** Get the in point.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the in point
+ */
+
+mlt_position mlt_producer_get_in( mlt_producer this )
+{
+ return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "in" );
+}
+
+/** Get the out point.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the out point
+ */
+
+mlt_position mlt_producer_get_out( mlt_producer this )
+{
+ return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "out" );
+}
+
+/** Get the total play time.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the playable (based on in and out points) duration
+ */
+
+mlt_position mlt_producer_get_playtime( mlt_producer this )
+{
+ return mlt_producer_get_out( this ) - mlt_producer_get_in( this ) + 1;
+}
+
+/** Get the total, unedited length of the producer.
+ *
+ * The value returned by a live streaming producer is unknown.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the duration of the producer regardless of in and out points
+ */
+
+mlt_position mlt_producer_get_length( mlt_producer this )
+{
+ return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "length" );
+}
+
+/** Prepare for next frame.
+ *
+ * Advance the play out position. If the speed is less than zero, it will
+ * move the play out position in the reverse direction.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ */
+
+void mlt_producer_prepare_next( mlt_producer this )
+{
+ if ( mlt_producer_get_speed( this ) != 0 )
+ mlt_producer_seek( this, mlt_producer_position( this ) + mlt_producer_get_speed( this ) );
+}
+
+/** Get a frame.
+ *
+ * This is the implementation of the \p get_frame virtual function.
+ * It requests a new frame object from the actual producer for the current
+ * play out position. The producer and its filters can add information and
+ * operations to the frame object in their get_frame handlers.
+ *
+ * \private \memberof mlt_producer_s
+ * \param service a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the actual producer
+ * \return true if there was an error
+ * \todo Learn more about the details and document how certain properties affect
+ * its behavior.
+ */
+
+static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
+{
+ int result = 1;
+ mlt_producer this = service != NULL ? service->child : NULL;
+
+ if ( this != NULL && !mlt_producer_is_cut( this ) )
+ {
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Determine eof handling
+ char *eof = mlt_properties_get( MLT_PRODUCER_PROPERTIES( this ), "eof" );
+
+ // Get the speed of the producer
+ double speed = mlt_producer_get_speed( this );
+
+ // We need to use the clone if it's specified
+ mlt_producer clone = mlt_properties_get_data( properties, "use_clone", NULL );
+
+ // If no clone is specified, use this
+ clone = clone == NULL ? this : clone;
+
+ // A properly instatiated producer will have a get_frame method...
+ if ( this->get_frame == NULL || ( !strcmp( eof, "continue" ) && mlt_producer_position( this ) > mlt_producer_get_out( this ) ) )
+ {
+ // Generate a test frame
+ *frame = mlt_frame_init( service );
+
+ // Set the position
+ result = mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+ // Mark as a test card
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 1 );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", 1 );
+
+ // Calculate the next position
+ mlt_producer_prepare_next( this );
+ }
+ else
+ {
+ // Get the frame from the implementation
+ result = this->get_frame( clone, frame, index );
+ }
+
+ // Copy the fps and speed of the producer onto the frame
+ properties = MLT_FRAME_PROPERTIES( *frame );
+ mlt_properties_set_double( properties, "_speed", speed );
+ mlt_properties_set_int( properties, "test_audio", mlt_frame_is_test_audio( *frame ) );
+ mlt_properties_set_int( properties, "test_image", mlt_frame_is_test_card( *frame ) );
+ if ( mlt_properties_get_data( properties, "_producer", NULL ) == NULL )
+ mlt_properties_set_data( properties, "_producer", service, 0, NULL, NULL );
+ }
+ else if ( this != NULL )
+ {
+ // Get the speed of the cut
+ double speed = mlt_producer_get_speed( this );
+
+ // Get the parent of this cut
+ mlt_producer parent = mlt_producer_cut_parent( this );
+
+ // Get the properties of the parent
+ mlt_properties parent_properties = MLT_PRODUCER_PROPERTIES( parent );
+
+ // Get the properties of the cut
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Determine the clone index
+ int clone_index = mlt_properties_get_int( properties, "_clone" );
+
+ // Determine the clone to use
+ mlt_producer clone = this;
+
+ if ( clone_index > 0 )
+ {
+ char key[ 25 ];
+ sprintf( key, "_clone.%d", clone_index - 1 );
+ clone = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), key, NULL );
+ if ( clone == NULL ) mlt_log( service, MLT_LOG_ERROR, "requested clone doesn't exist %d\n", clone_index );
+ clone = clone == NULL ? this : clone;
+ }
+ else
+ {
+ clone = parent;
+ }
+
+ // We need to seek to the correct position in the clone
+ mlt_producer_seek( clone, mlt_producer_get_in( this ) + mlt_properties_get_int( properties, "_position" ) );
+
+ // Assign the clone property to the parent
+ mlt_properties_set_data( parent_properties, "use_clone", clone, 0, NULL, NULL );
+
+ // Now get the frame from the parents service
+ result = mlt_service_get_frame( MLT_PRODUCER_SERVICE( parent ), frame, index );
+
+ // We're done with the clone now
+ mlt_properties_set_data( parent_properties, "use_clone", NULL, 0, NULL, NULL );
+
+ // This is useful and required by always_active transitions to determine in/out points of the cut
+ if ( mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", NULL ) == MLT_PRODUCER_SERVICE( parent ) )
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", this, 0, NULL, NULL );
+
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "_speed", speed );
+ mlt_producer_prepare_next( this );
+ }
+ else
+ {
+ *frame = mlt_frame_init( service );
+ result = 0;
+ }
+
+ // Pass on all meta properties from the producer/cut on to the frame
+ if ( *frame != NULL && this != NULL )
+ {
+ int i = 0;
+ mlt_properties p_props = MLT_PRODUCER_PROPERTIES( this );
+ mlt_properties f_props = MLT_FRAME_PROPERTIES( *frame );
+ int count = mlt_properties_count( p_props );
+ for ( i = 0; i < count; i ++ )
+ {
+ char *name = mlt_properties_get_name( p_props, i );
+ if ( !strncmp( name, "meta.", 5 ) )
+ mlt_properties_set( f_props, name, mlt_properties_get( p_props, name ) );
+ else if ( !strncmp( name, "set.", 4 ) )
+ mlt_properties_set( f_props, name + 4, mlt_properties_get( p_props, name ) );
+ }
+ }
+
+ return result;
+}
+
+/** Attach a filter.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param filter the filter to attach
+ * \return true if there was an error
+ */
+
+int mlt_producer_attach( mlt_producer this, mlt_filter filter )
+{
+ return mlt_service_attach( MLT_PRODUCER_SERVICE( this ), filter );
+}
+
+/** Detach a filter.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a service
+ * \param filter the filter to detach
+ * \return true if there was an error
+ */
+
+int mlt_producer_detach( mlt_producer this, mlt_filter filter )
+{
+ return mlt_service_detach( MLT_PRODUCER_SERVICE( this ), filter );
+}
+
+/** Retrieve a filter.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a service
+ * \param index which filter to retrieve
+ * \return the filter or null if there was an error
+ */
+
+mlt_filter mlt_producer_filter( mlt_producer this, int index )
+{
+ return mlt_service_filter( MLT_PRODUCER_SERVICE( this ), index );
+}
+
+/** Clone this producer.
+ *
+ * \private \memberof mlt_producer_s
+ * \param this a producer
+ * \return a new producer that is a copy of \p this
+ * \see mlt_producer_set_clones
+ */
+
+static mlt_producer mlt_producer_clone( mlt_producer this )
+{
+ mlt_producer clone = NULL;
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+ char *resource = mlt_properties_get( properties, "resource" );
+ char *service = mlt_properties_get( properties, "mlt_service" );
+ mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) );
+
+ mlt_events_block( mlt_factory_event_object( ), mlt_factory_event_object( ) );
+
+ if ( service != NULL )
+ clone = mlt_factory_producer( profile, service, resource );
+
+ if ( clone == NULL && resource != NULL )
+ clone = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), resource );
+
+ if ( clone != NULL )
+ mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( clone ), properties );
+
+ mlt_events_unblock( mlt_factory_event_object( ), mlt_factory_event_object( ) );
+
+ return clone;
+}
+
+/** Create clones.
+ *
+ * \private \memberof mlt_producer_s
+ * \param this a producer
+ * \param clones the number of copies to make
+ * \see mlt_producer_optimise
+ */
+
+static void mlt_producer_set_clones( mlt_producer this, int clones )
+{
+ mlt_producer parent = mlt_producer_cut_parent( this );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent );
+ int existing = mlt_properties_get_int( properties, "_clones" );
+ int i = 0;
+ char key[ 25 ];
+
+ // If the number of existing clones is different, then create/remove as necessary
+ if ( existing != clones )
+ {
+ if ( existing < clones )
+ {
+ for ( i = existing; i < clones; i ++ )
+ {
+ mlt_producer clone = mlt_producer_clone( parent );
+ sprintf( key, "_clone.%d", i );
+ mlt_properties_set_data( properties, key, clone, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ }
+ }
+ else
+ {
+ for ( i = clones; i < existing; i ++ )
+ {
+ sprintf( key, "_clone.%d", i );
+ mlt_properties_set_data( properties, key, NULL, 0, NULL, NULL );
+ }
+ }
+ }
+
+ // Ensure all properties on the parent are passed to the clones
+ for ( i = 0; i < clones; i ++ )
+ {
+ mlt_producer clone = NULL;
+ sprintf( key, "_clone.%d", i );
+ clone = mlt_properties_get_data( properties, key, NULL );
+ if ( clone != NULL )
+ mlt_properties_pass( MLT_PRODUCER_PROPERTIES( clone ), properties, "" );
+ }
+
+ // Update the number of clones on the properties
+ mlt_properties_set_int( properties, "_clones", clones );
+}
+
+/** \brief private to mlt_producer_s, used by mlt_producer_optimise() */
+
+typedef struct
+{
+ int multitrack;
+ int track;
+ int position;
+ int length;
+ int offset;
+}
+track_info;
+
+/** \brief private to mlt_producer_s, used by mlt_producer_optimise() */
+
+typedef struct
+{
+ mlt_producer cut;
+ int start;
+ int end;
+}
+clip_references;
+
+static int intersect( clip_references *a, clip_references *b )
+{
+ int diff = ( a->start - b->start ) + ( a->end - b->end );
+ return diff >= 0 && diff < ( a->end - a->start + 1 );
+}
+
+static int push( mlt_parser this, int multitrack, int track, int position )
+{
+ mlt_properties properties = mlt_parser_properties( this );
+ mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL );
+ track_info *info = malloc( sizeof( track_info ) );
+ info->multitrack = multitrack;
+ info->track = track;
+ info->position = position;
+ info->length = 0;
+ info->offset = 0;
+ return mlt_deque_push_back( stack, info );
+}
+
+static track_info *pop( mlt_parser this )
+{
+ mlt_properties properties = mlt_parser_properties( this );
+ mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL );
+ return mlt_deque_pop_back( stack );
+}
+
+static track_info *peek( mlt_parser this )
+{
+ mlt_properties properties = mlt_parser_properties( this );
+ mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL );
+ return mlt_deque_peek_back( stack );
+}
+
+static int on_start_multitrack( mlt_parser this, mlt_multitrack object )
+{
+ track_info *info = peek( this );
+ return push( this, info->multitrack ++, info->track, info->position );
+}
+
+static int on_start_track( mlt_parser this )
+{
+ track_info *info = peek( this );
+ info->position -= info->offset;
+ info->length -= info->offset;
+ return push( this, info->multitrack, info->track ++, info->position );
+}
+
+static int on_start_producer( mlt_parser this, mlt_producer object )
+{
+ mlt_properties properties = mlt_parser_properties( this );
+ mlt_properties producers = mlt_properties_get_data( properties, "producers", NULL );
+ mlt_producer parent = mlt_producer_cut_parent( object );
+ if ( mlt_service_identify( ( mlt_service )mlt_producer_cut_parent( object ) ) == producer_type && mlt_producer_is_cut( object ) )
+ {
+ int ref_count = 0;
+ clip_references *old_refs = NULL;
+ clip_references *refs = NULL;
+ char key[ 50 ];
+ int count = 0;
+ track_info *info = peek( this );
+ sprintf( key, "%p", parent );
+ mlt_properties_get_data( producers, key, &count );
+ mlt_properties_set_data( producers, key, parent, ++ count, NULL, NULL );
+ old_refs = mlt_properties_get_data( properties, key, &ref_count );
+ refs = malloc( ( ref_count + 1 ) * sizeof( clip_references ) );
+ if ( old_refs != NULL )
+ memcpy( refs, old_refs, ref_count * sizeof( clip_references ) );
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( object ), "_clone", -1 );
+ refs[ ref_count ].cut = object;
+ refs[ ref_count ].start = info->position;
+ refs[ ref_count ].end = info->position + mlt_producer_get_playtime( object ) - 1;
+ mlt_properties_set_data( properties, key, refs, ++ ref_count, free, NULL );
+ info->position += mlt_producer_get_playtime( object );
+ info->length += mlt_producer_get_playtime( object );
+ }
+ return 0;
+}
+
+static int on_end_track( mlt_parser this )
+{
+ track_info *track = pop( this );
+ track_info *multi = peek( this );
+ multi->length += track->length;
+ multi->position += track->length;
+ multi->offset = track->length;
+ free( track );
+ return 0;
+}
+
+static int on_end_multitrack( mlt_parser this, mlt_multitrack object )
+{
+ track_info *multi = pop( this );
+ track_info *track = peek( this );
+ track->position += multi->length;
+ track->length += multi->length;
+ free( multi );
+ return 0;
+}
+
+/** Optimise for overlapping cuts from the same clip.
+ *
+ * \todo learn more about this
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if there was an error
+ */
+
+int mlt_producer_optimise( mlt_producer this )
+{
+ int error = 1;
+ mlt_parser parser = mlt_parser_new( );
+ if ( parser != NULL )
+ {
+ int i = 0, j = 0, k = 0;
+ mlt_properties properties = mlt_parser_properties( parser );
+ mlt_properties producers = mlt_properties_new( );
+ mlt_deque stack = mlt_deque_init( );
+ mlt_properties_set_data( properties, "producers", producers, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_properties_set_data( properties, "stack", stack, 0, ( mlt_destructor )mlt_deque_close, NULL );
+ parser->on_start_producer = on_start_producer;
+ parser->on_start_track = on_start_track;
+ parser->on_end_track = on_end_track;
+ parser->on_start_multitrack = on_start_multitrack;
+ parser->on_end_multitrack = on_end_multitrack;
+ push( parser, 0, 0, 0 );
+ mlt_parser_start( parser, MLT_PRODUCER_SERVICE( this ) );
+ free( pop( parser ) );
+ for ( k = 0; k < mlt_properties_count( producers ); k ++ )
+ {
+ char *name = mlt_properties_get_name( producers, k );
+ int count = 0;
+ int clones = 0;
+ int max_clones = 0;
+ mlt_producer producer = mlt_properties_get_data( producers, name, &count );
+ if ( producer != NULL && count > 1 )
+ {
+ clip_references *refs = mlt_properties_get_data( properties, name, &count );
+ for ( i = 0; i < count; i ++ )
+ {
+ clones = 0;
+ for ( j = i + 1; j < count; j ++ )
+ {
+ if ( intersect( &refs[ i ], &refs[ j ] ) )
+ {
+ clones ++;
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( refs[ j ].cut ), "_clone", clones );
+ }
+ }
+ if ( clones > max_clones )
+ max_clones = clones;
+ }
+
+ for ( i = 0; i < count; i ++ )
+ {
+ mlt_producer cut = refs[ i ].cut;
+ if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( cut ), "_clone" ) == -1 )
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( cut ), "_clone", 0 );
+ }
+
+ mlt_producer_set_clones( producer, max_clones );
+ }
+ else if ( producer != NULL )
+ {
+ clip_references *refs = mlt_properties_get_data( properties, name, &count );
+ for ( i = 0; i < count; i ++ )
+ {
+ mlt_producer cut = refs[ i ].cut;
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( cut ), "_clone", 0 );
+ }
+ mlt_producer_set_clones( producer, 0 );
+ }
+ }
+ mlt_parser_close( parser );
+ }
+ return error;
+}
+
+/** Close the producer.
+ *
+ * Destroys the producer and deallocates its resources managed by its
+ * properties list. This will call the close virtual function. Therefore, a
+ * subclass that defines its own close function should set its virtual close
+ * function to NULL prior to calling this to avoid circular calls.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ */
+
+void mlt_producer_close( mlt_producer this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_PRODUCER_PROPERTIES( this ) ) <= 0 )
+ {
+ this->parent.close = NULL;
+
+ if ( this->close != NULL )
+ {
+ this->close( this->close_object );
+ }
+ else
+ {
+ int destroy = mlt_producer_is_cut( this );
+
+#if _MLT_PRODUCER_CHECKS_ == 1
+ // Show debug info
+ mlt_properties_debug( MLT_PRODUCER_PROPERTIES( this ), "Producer closing", stderr );
+#endif
+
+#ifdef _MLT_PRODUCER_CHECKS_
+ // Show current stats - these should match when the app is closed
+ mlt_log( MLT_PRODUCER_SERVICE( this ), MLT_LOG_DEBUG, "Producers created %d, destroyed %d\n", producers_created, ++producers_destroyed );
+#endif
+
+ mlt_service_close( &this->parent );
+
+ if ( destroy )
+ free( this );
+ }
+ }
+}
--- /dev/null
+/**
+ * \file mlt_producer.h
+ * \brief abstraction for all producer services
+ * \see mlt_producer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_PRODUCER_H_
+#define _MLT_PRODUCER_H_
+
+#include "mlt_service.h"
+#include "mlt_filter.h"
+
+/** \brief Producer abstract service class
+ *
+ * A producer is a service that generates audio, video, and metadata.
+ * Some day it may also generate text (subtitles). This is not to say
+ * a producer "synthesizes," rather that is an origin of data within the
+ * service network - that could be through synthesis or reading a stream.
+ *
+ * \extends mlt_service
+ * \event \em producer-changed
+ * \properties \em mlt_type the name of the service subclass, e.g. mlt_producer
+ * \properties \em mlt_service the name of a producer subclass
+ * \properties \em _position the current position of the play head, relative to the in point
+ * \properties \em _frame the current position of the play head, relative to the beginning of the resource
+ * \properties \em _speed the current speed factor, where 1.0 is normal
+ * \properties \em aspect_ratio sample aspect ratio
+ * \properties \em length the duration of the cut in frames
+ * \properties \em eof the end-of-file behavior, one of: pause, continue, loop
+ * \properties \em resource the file name, stream address, or the class name in angle brackets
+ * \properties \em _cut set if this producer is a "cut" producer
+ * \properties \em mlt_mix stores the data for a "mix" producer
+ * \properties \em _cut_parent holds a reference to the cut's parent producer
+ * \properties \em ignore_points Set this to temporarily disable the in and out points.
+ * \properties \em use_clone holds a reference to a clone's producer, as created by mlt_producer_optimise
+ * \properties \em _clone is the index of the clone in the list of clones stored on the clone's producer
+ * \properties \em _clones is the number of clones of the producer, as created by mlt_producer_optimise
+ * \properties \em _clone.{N} holds a reference to the N'th clone of the producer, as created by mlt_producer_optimise
+ * \properties \em meta.* holds metadata - there is a loose taxonomy to be defined
+ * \properties \em set.* holds properties to set on a frame produced
+ * \todo define the media metadata taxonomy
+ */
+
+struct mlt_producer_s
+{
+ /** A producer is a service. */
+ struct mlt_service_s parent;
+
+ /** Get a frame of data (virtual function).
+ *
+ * \param mlt_producer a producer
+ * \param mlt_frame_ptr a frame pointer by reference
+ * \param int an index
+ * \return true if there was an error
+ */
+ int ( *get_frame )( mlt_producer, mlt_frame_ptr, int );
+
+ /** the destructor virtual function */
+ mlt_destructor close;
+ void *close_object; /**< the object supplied to the close virtual function */
+
+ void *local; /**< \private instance object */
+ void *child; /**< \private the object of a subclass */
+};
+
+/*
+ * Public final methods
+ */
+
+#define MLT_PRODUCER_SERVICE( producer ) ( &( producer )->parent )
+#define MLT_PRODUCER_PROPERTIES( producer ) MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) )
+
+extern int mlt_producer_init( mlt_producer self, void *child );
+extern mlt_producer mlt_producer_new( );
+extern mlt_service mlt_producer_service( mlt_producer self );
+extern mlt_properties mlt_producer_properties( mlt_producer self );
+extern int mlt_producer_seek( mlt_producer self, mlt_position position );
+extern mlt_position mlt_producer_position( mlt_producer self );
+extern mlt_position mlt_producer_frame( mlt_producer self );
+extern int mlt_producer_set_speed( mlt_producer self, double speed );
+extern double mlt_producer_get_speed( mlt_producer self );
+extern double mlt_producer_get_fps( mlt_producer self );
+extern int mlt_producer_set_in_and_out( mlt_producer self, mlt_position in, mlt_position out );
+extern int mlt_producer_clear( mlt_producer self );
+extern mlt_position mlt_producer_get_in( mlt_producer self );
+extern mlt_position mlt_producer_get_out( mlt_producer self );
+extern mlt_position mlt_producer_get_playtime( mlt_producer self );
+extern mlt_position mlt_producer_get_length( mlt_producer self );
+extern void mlt_producer_prepare_next( mlt_producer self );
+extern int mlt_producer_attach( mlt_producer self, mlt_filter filter );
+extern int mlt_producer_detach( mlt_producer self, mlt_filter filter );
+extern mlt_filter mlt_producer_filter( mlt_producer self, int index );
+extern mlt_producer mlt_producer_cut( mlt_producer self, int in, int out );
+extern int mlt_producer_is_cut( mlt_producer self );
+extern int mlt_producer_is_mix( mlt_producer self );
+extern int mlt_producer_is_blank( mlt_producer self );
+extern mlt_producer mlt_producer_cut_parent( mlt_producer self );
+extern int mlt_producer_optimise( mlt_producer self );
+extern void mlt_producer_close( mlt_producer self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_profile.c
+ * \brief video output definition
+ * \see mlt_profile_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_profile.h"
+#include "mlt_factory.h"
+#include "mlt_properties.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+
+/** the default subdirectory of the prefix for holding profiles */
+#define PROFILES_DIR "/share/mlt/profiles/"
+
+/** Load a profile from the system folder.
+ *
+ * The environment variable MLT_PROFILES_PATH overrides the default \p PROFILES_DIR.
+ *
+ * \private \memberof mlt_profile_s
+ * \param name the name of a profile settings file located in the standard location or
+ * the full path name to a profile settings file
+ * \return a profile or NULL on error
+ */
+
+static mlt_profile mlt_profile_select( const char *name )
+{
+ char *filename = NULL;
+ const char *prefix = getenv( "MLT_PROFILES_PATH" );
+ mlt_properties properties = mlt_properties_load( name );
+ mlt_profile profile = NULL;
+
+ // Try to load from file specification
+ if ( properties && mlt_properties_get_int( properties, "width" ) )
+ {
+ filename = calloc( 1, strlen( name ) + 1 );
+ }
+ // Load from $prefix/share/mlt/profiles
+ else if ( prefix == NULL )
+ {
+ prefix = PREFIX;
+ filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + strlen( name ) + 2 );
+ strcpy( filename, prefix );
+ if ( filename[ strlen( filename ) - 1 ] != '/' )
+ filename[ strlen( filename ) ] = '/';
+ strcat( filename, PROFILES_DIR );
+ }
+ // Use environment variable instead
+ else
+ {
+ filename = calloc( 1, strlen( prefix ) + strlen( name ) + 2 );
+ strcpy( filename, prefix );
+ if ( filename[ strlen( filename ) - 1 ] != '/' )
+ filename[ strlen( filename ) ] = '/';
+ }
+
+ // Finish loading
+ strcat( filename, name );
+ profile = mlt_profile_load_file( filename );
+
+ // Cleanup
+ mlt_properties_close( properties );
+ free( filename );
+
+ return profile;
+}
+
+/** Construct a profile.
+ *
+ * This will never return NULL as it uses the dv_pal settings as hard-coded fallback default.
+ *
+ * \public \memberof mlt_profile_s
+ * @param name the name of a profile settings file located in the standard location or
+ * the full path name to a profile settings file
+ * @return a profile
+ */
+
+mlt_profile mlt_profile_init( const char *name )
+{
+ mlt_profile profile = NULL;
+
+ // Explicit profile by name gets priority over environment variables
+ if ( name )
+ profile = mlt_profile_select( name );
+
+ // Try to load by environment variable
+ if ( profile == NULL )
+ {
+ // MLT_PROFILE is preferred environment variable
+ if ( getenv( "MLT_PROFILE" ) )
+ profile = mlt_profile_select( getenv( "MLT_PROFILE" ) );
+ // MLT_NORMALISATION backwards compatibility
+ else if ( getenv( "MLT_NORMALISATION" ) && strcmp( getenv( "MLT_NORMALISATION" ), "PAL" ) )
+ profile = mlt_profile_select( "dv_ntsc" );
+ else
+ profile = mlt_profile_select( "dv_pal" );
+
+ // If still not loaded (no profile files), default to PAL
+ if ( profile == NULL )
+ {
+ profile = calloc( 1, sizeof( struct mlt_profile_s ) );
+ if ( profile )
+ {
+ mlt_environment_set( "MLT_PROFILE", "dv_pal" );
+ profile->description = strdup( "PAL 4:3 DV or DVD" );
+ profile->frame_rate_num = 25;
+ profile->frame_rate_den = 1;
+ profile->width = 720;
+ profile->height = 576;
+ profile->progressive = 0;
+ profile->sample_aspect_num = 16;
+ profile->sample_aspect_den = 15;
+ profile->display_aspect_num = 4;
+ profile->display_aspect_den = 3;
+ }
+ }
+ }
+ return profile;
+}
+
+/** Load a profile from specific file.
+ *
+ * \public \memberof mlt_profile_s
+ * @param file the full path name to a properties file
+ * @return a profile or NULL on error
+ */
+
+mlt_profile mlt_profile_load_file( const char *file )
+{
+ mlt_profile profile = NULL;
+
+ // Load the profile as properties
+ mlt_properties properties = mlt_properties_load( file );
+ if ( properties )
+ {
+ // Simple check if the profile is valid
+ if ( mlt_properties_get_int( properties, "width" ) )
+ {
+ profile = mlt_profile_load_properties( properties );
+
+ // Set MLT_PROFILE to basename
+ char *filename = strdup( file );
+ mlt_environment_set( "MLT_PROFILE", basename( filename ) );
+ free( filename );
+ }
+ mlt_properties_close( properties );
+ }
+
+ // Set MLT_NORMALISATION to appease legacy modules
+ char *profile_name = mlt_environment( "MLT_PROFILE" );
+ if ( profile_name )
+ {
+ if ( strstr( profile_name, "_ntsc" ) ||
+ strstr( profile_name, "_60" ) ||
+ strstr( profile_name, "_30" ) )
+ {
+ mlt_environment_set( "MLT_NORMALISATION", "NTSC" );
+ }
+ else if ( strstr( profile_name, "_pal" ) ||
+ strstr( profile_name, "_50" ) ||
+ strstr( profile_name, "_25" ) )
+ {
+ mlt_environment_set( "MLT_NORMALISATION", "PAL" );
+ }
+ }
+ return profile;
+}
+
+/** Load a profile from a properties object.
+ *
+ * \public \memberof mlt_profile_s
+ * @param properties a properties list
+ * @return a profile or NULL if out of memory
+ */
+
+mlt_profile mlt_profile_load_properties( mlt_properties properties )
+{
+ mlt_profile profile = calloc( 1, sizeof( struct mlt_profile_s ) );
+ if ( profile )
+ {
+ if ( mlt_properties_get( properties, "name" ) )
+ mlt_environment_set( "MLT_PROFILE", mlt_properties_get( properties, "name" ) );
+ if ( mlt_properties_get( properties, "description" ) )
+ profile->description = strdup( mlt_properties_get( properties, "description" ) );
+ profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
+ profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
+ profile->width = mlt_properties_get_int( properties, "width" );
+ profile->height = mlt_properties_get_int( properties, "height" );
+ profile->progressive = mlt_properties_get_int( properties, "progressive" );
+ profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
+ profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
+ profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
+ profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
+ }
+ return profile;
+}
+
+/** Load an anonymous profile from string.
+ *
+ * \public \memberof mlt_profile_s
+ * @param string a newline-delimited list of properties as name=value pairs
+ * @return a profile or NULL if out of memory
+ */
+
+mlt_profile mlt_profile_load_string( const char *string )
+{
+ mlt_properties properties = mlt_properties_new();
+ if ( properties )
+ {
+ const char *p = string;
+ while ( p )
+ {
+ if ( strcmp( p, "" ) && p[ 0 ] != '#' )
+ mlt_properties_parse( properties, p );
+ p = strchr( p, '\n' );
+ if ( p ) p++;
+ }
+ }
+ return mlt_profile_load_properties( properties );
+}
+
+/** Get the video frame rate as a floating point value.
+ *
+ * \public \memberof mlt_profile_s
+ * @param aprofile a profile
+ * @return the frame rate
+ */
+
+double mlt_profile_fps( mlt_profile aprofile )
+{
+ if ( aprofile )
+ return ( double ) aprofile->frame_rate_num / aprofile->frame_rate_den;
+ else
+ return 0;
+}
+
+/** Get the sample aspect ratio as a floating point value.
+ *
+ * \public \memberof mlt_profile_s
+ * @param aprofile a profile
+ * @return the pixel aspect ratio
+ */
+
+double mlt_profile_sar( mlt_profile aprofile )
+{
+ if ( aprofile )
+ return ( double ) aprofile->sample_aspect_num / aprofile->sample_aspect_den;
+ else
+ return 0;
+}
+
+/** Get the display aspect ratio as floating point value.
+ *
+ * \public \memberof mlt_profile_s
+ * @param aprofile a profile
+ * @return the image aspect ratio
+ */
+
+double mlt_profile_dar( mlt_profile aprofile )
+{
+ if ( aprofile )
+ return ( double ) aprofile->display_aspect_num / aprofile->display_aspect_den;
+ else
+ return 0;
+}
+
+/** Free up the global profile resources.
+ *
+ * \public \memberof mlt_profile_s
+ * @param profile a profile
+ */
+
+void mlt_profile_close( mlt_profile profile )
+{
+ if ( profile )
+ {
+ if ( profile->description )
+ free( profile->description );
+ profile->description = NULL;
+ free( profile );
+ profile = NULL;
+ }
+}
--- /dev/null
+/**
+ * \file mlt_profile.h
+ * \brief video output definition
+ * \see mlt_profile_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_PROFILE_H
+#define _MLT_PROFILE_H
+
+#include "mlt_types.h"
+
+/** \brief Profile class
+ *
+ */
+
+struct mlt_profile_s
+{
+ char* description; /**< a brief description suitable as a label in UI menu */
+ int frame_rate_num; /**< the numerator of the video frame rate */
+ int frame_rate_den; /**< the denominator of the video frame rate */
+ int width; /**< the horizontal resolution of the video */
+ int height; /**< the vertical resolution of the video */
+ int progressive; /**< a flag to indicate if the video is progressive scan, interlace if not set */
+ int sample_aspect_num; /**< the numerator of the pixel aspect ratio */
+ int sample_aspect_den; /**< the denominator of the pixel aspect ratio */
+ int display_aspect_num; /**< the numerator of the image aspect ratio in case it can not be simply derived (e.g. ITU-R 601) */
+ int display_aspect_den; /**< the denominator of the image aspect ratio in case it can not be simply derived (e.g. ITU-R 601) */
+};
+
+extern mlt_profile mlt_profile_init( const char *name );
+extern mlt_profile mlt_profile_load_file( const char *file );
+extern mlt_profile mlt_profile_load_properties( mlt_properties properties );
+extern mlt_profile mlt_profile_load_string( const char *string );
+extern double mlt_profile_fps( mlt_profile profile );
+extern double mlt_profile_sar( mlt_profile profile );
+extern double mlt_profile_dar( mlt_profile profile );
+extern void mlt_profile_close( mlt_profile profile );
+#endif
--- /dev/null
+/**
+ * \file mlt_properties.c
+ * \brief Properties class definition
+ * \see mlt_properties_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_properties.h"
+#include "mlt_property.h"
+#include "mlt_deque.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+
+/** \brief private implementation of the property list */
+
+typedef struct
+{
+ int hash[ 199 ];
+ char **name;
+ mlt_property *value;
+ int count;
+ int size;
+ mlt_properties mirror;
+ int ref_count;
+ pthread_mutex_t mutex;
+}
+property_list;
+
+/* Memory leak checks */
+
+//#define _MLT_PROPERTY_CHECKS_ 2
+#ifdef _MLT_PROPERTY_CHECKS_
+static int properties_created = 0;
+static int properties_destroyed = 0;
+#endif
+
+/** Initialize a properties object that was already allocated.
+ *
+ * This does allocate its ::property_list, and it adds a reference count.
+ * \public \memberof mlt_properties_s
+ * \param this the properties structure to initialize
+ * \param child an opaque pointer to a subclass object
+ * \return true if failed
+ */
+
+int mlt_properties_init( mlt_properties this, void *child )
+{
+ if ( this != NULL )
+ {
+#ifdef _MLT_PROPERTY_CHECKS_
+ // Increment number of properties created
+ properties_created ++;
+#endif
+
+ // NULL all methods
+ memset( this, 0, sizeof( struct mlt_properties_s ) );
+
+ // Assign the child of the object
+ this->child = child;
+
+ // Allocate the local structure
+ this->local = calloc( sizeof( property_list ), 1 );
+
+ // Increment the ref count
+ ( ( property_list * )this->local )->ref_count = 1;
+ pthread_mutex_init( &( ( property_list * )this->local )->mutex, NULL );;
+ }
+
+ // Check that initialisation was successful
+ return this != NULL && this->local == NULL;
+}
+
+/** Create a properties object.
+ *
+ * This allocates the properties structure and calls mlt_properties_init() on it.
+ * Free the properties object with mlt_properties_close().
+ * \public \memberof mlt_properties_s
+ * \return a new properties object
+ */
+
+mlt_properties mlt_properties_new( )
+{
+ // Construct a standalone properties object
+ mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
+
+ // Initialise this
+ mlt_properties_init( this, NULL );
+
+ // Return the pointer
+ return this;
+}
+
+/** Create a properties object by reading a .properties text file.
+ *
+ * Free the properties object with mlt_properties_close().
+ * \deprecated Please start using mlt_properties_parse_yaml().
+ * \public \memberof mlt_properties_s
+ * \param filename a string contain the absolute file name
+ * \return a new properties object
+ */
+
+mlt_properties mlt_properties_load( const char *filename )
+{
+ // Construct a standalone properties object
+ mlt_properties this = mlt_properties_new( );
+
+ if ( this != NULL )
+ {
+ // Open the file
+ FILE *file = fopen( filename, "r" );
+
+ // Load contents of file
+ if ( file != NULL )
+ {
+ // Temp string
+ char temp[ 1024 ];
+ char last[ 1024 ] = "";
+
+ // Read each string from the file
+ while( fgets( temp, 1024, file ) )
+ {
+ // Chomp the string
+ temp[ strlen( temp ) - 1 ] = '\0';
+
+ // Check if the line starts with a .
+ if ( temp[ 0 ] == '.' )
+ {
+ char temp2[ 1024 ];
+ sprintf( temp2, "%s%s", last, temp );
+ strcpy( temp, temp2 );
+ }
+ else if ( strchr( temp, '=' ) )
+ {
+ strcpy( last, temp );
+ *( strchr( last, '=' ) ) = '\0';
+ }
+
+ // Parse and set the property
+ if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
+ mlt_properties_parse( this, temp );
+ }
+
+ // Close the file
+ fclose( file );
+ }
+ }
+
+ // Return the pointer
+ return this;
+}
+
+/** Generate a hash key.
+ *
+ * \private \memberof mlt_properties_s
+ * \param name a string
+ * \return an integer
+ */
+
+static inline int generate_hash( const char *name )
+{
+ int hash = 0;
+ int i = 1;
+ while ( *name )
+ hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
+ return hash;
+}
+
+/** Copy a serializable property to properties list that is mirroring this one.
+ *
+ * Special case - when a container (such as fezzik) is protecting another
+ * producer, we need to ensure that properties are passed through to the
+ * real producer.
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the name of the property to copy
+ */
+
+static inline void mlt_properties_do_mirror( mlt_properties this, const char *name )
+{
+ property_list *list = this->local;
+ if ( list->mirror != NULL )
+ {
+ char *value = mlt_properties_get( this, name );
+ if ( value != NULL )
+ mlt_properties_set( list->mirror, name, value );
+ }
+}
+
+/** Increment the reference count.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the new reference count
+ */
+
+int mlt_properties_inc_ref( mlt_properties this )
+{
+ int result = 0;
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ pthread_mutex_lock( &list->mutex );
+ result = ++ list->ref_count;
+ pthread_mutex_unlock( &list->mutex );
+ }
+ return result;
+}
+
+/** Decrement the reference count.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the new reference count
+ */
+
+int mlt_properties_dec_ref( mlt_properties this )
+{
+ int result = 0;
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ pthread_mutex_lock( &list->mutex );
+ result = -- list->ref_count;
+ pthread_mutex_unlock( &list->mutex );
+ }
+ return result;
+}
+
+/** Get the reference count.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the current reference count
+ */
+
+int mlt_properties_ref_count( mlt_properties this )
+{
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ return list->ref_count;
+ }
+ return 0;
+}
+
+/** Set a properties list to be a mirror copy of another.
+ *
+ * Note that this does not copy all existing properties. Rather, you must
+ * call this before setting the properties that you wish to copy.
+ * \public \memberof mlt_properties_s
+ * \param that the properties which will receive copies of the properties as they are set.
+ * \param this the properties to mirror
+ */
+
+void mlt_properties_mirror( mlt_properties this, mlt_properties that )
+{
+ property_list *list = this->local;
+ list->mirror = that;
+}
+
+/** Copy all serializable properties to another properties list.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this The properties to copy to
+ * \param that The properties to copy from
+ * \return false
+ */
+
+int mlt_properties_inherit( mlt_properties this, mlt_properties that )
+{
+ int count = mlt_properties_count( that );
+ int i = 0;
+ for ( i = 0; i < count; i ++ )
+ {
+ char *value = mlt_properties_get_value( that, i );
+ if ( value != NULL )
+ {
+ char *name = mlt_properties_get_name( that, i );
+ mlt_properties_set( this, name, value );
+ }
+ }
+ return 0;
+}
+
+/** Pass all serializable properties that match a prefix to another properties object
+ *
+ * \public \memberof mlt_properties_s
+ * \param this the properties to copy to
+ * \param that The properties to copy from
+ * \param prefix the property names to match (required)
+ * \return false
+ */
+
+int mlt_properties_pass( mlt_properties this, mlt_properties that, const char *prefix )
+{
+ int count = mlt_properties_count( that );
+ int length = strlen( prefix );
+ int i = 0;
+ for ( i = 0; i < count; i ++ )
+ {
+ char *name = mlt_properties_get_name( that, i );
+ if ( !strncmp( name, prefix, length ) )
+ {
+ char *value = mlt_properties_get_value( that, i );
+ if ( value != NULL )
+ mlt_properties_set( this, name + length, value );
+ }
+ }
+ return 0;
+}
+
+/** Locate a property by name.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to lookup by name
+ * \return the property or NULL for failure
+ */
+
+static inline mlt_property mlt_properties_find( mlt_properties this, const char *name )
+{
+ property_list *list = this->local;
+ mlt_property value = NULL;
+ int key = generate_hash( name );
+ int i = list->hash[ key ] - 1;
+
+ if ( i >= 0 )
+ {
+ // Check if we're hashed
+ if ( list->count > 0 &&
+ name[ 0 ] == list->name[ i ][ 0 ] &&
+ !strcmp( list->name[ i ], name ) )
+ value = list->value[ i ];
+
+ // Locate the item
+ for ( i = list->count - 1; value == NULL && i >= 0; i -- )
+ if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
+ value = list->value[ i ];
+ }
+
+ return value;
+}
+
+/** Add a new property.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the name of the new property
+ * \return the new property
+ */
+
+static mlt_property mlt_properties_add( mlt_properties this, const char *name )
+{
+ property_list *list = this->local;
+ int key = generate_hash( name );
+
+ // Check that we have space and resize if necessary
+ if ( list->count == list->size )
+ {
+ list->size += 50;
+ list->name = realloc( list->name, list->size * sizeof( const char * ) );
+ list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
+ }
+
+ // Assign name/value pair
+ list->name[ list->count ] = strdup( name );
+ list->value[ list->count ] = mlt_property_init( );
+
+ // Assign to hash table
+ if ( list->hash[ key ] == 0 )
+ list->hash[ key ] = list->count + 1;
+
+ // Return and increment count accordingly
+ return list->value[ list->count ++ ];
+}
+
+/** Fetch a property by name and add one if not found.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to lookup or add
+ * \return the property
+ */
+
+static mlt_property mlt_properties_fetch( mlt_properties this, const char *name )
+{
+ // Try to find an existing property first
+ mlt_property property = mlt_properties_find( this, name );
+
+ // If it wasn't found, create one
+ if ( property == NULL )
+ property = mlt_properties_add( this, name );
+
+ // Return the property
+ return property;
+}
+
+/** Copy a property to another properties list.
+ *
+ * \public \memberof mlt_properties_s
+ * \author Zach <zachary.drew@gmail.com>
+ * \param this the properties to copy to
+ * \param that the properties to copy from
+ * \param name the name of the property to copy
+ */
+
+void mlt_properties_pass_property( mlt_properties this, mlt_properties that, const char *name )
+{
+ // Make sure the source property isn't null.
+ mlt_property that_prop = mlt_properties_find( that, name );
+ if( that_prop == NULL )
+ return;
+
+ mlt_property_pass( mlt_properties_fetch( this, name ), that_prop );
+}
+
+/** Copy all properties specified in a comma-separated list to another properties list.
+ *
+ * White space is also a delimiter.
+ * \public \memberof mlt_properties_s
+ * \author Zach <zachary.drew@gmail.com>
+ * \param this the properties to copy to
+ * \param that the properties to copy from
+ * \param list a delimited list of property names
+ * \return false
+ */
+
+
+int mlt_properties_pass_list( mlt_properties this, mlt_properties that, const char *list )
+{
+ char *props = strdup( list );
+ char *ptr = props;
+ const char *delim = " ,\t\n"; // Any combination of spaces, commas, tabs, and newlines
+ int count, done = 0;
+
+ while( !done )
+ {
+ count = strcspn( ptr, delim );
+
+ if( ptr[count] == '\0' )
+ done = 1;
+ else
+ ptr[count] = '\0'; // Make it a real string
+
+ mlt_properties_pass_property( this, that, ptr );
+
+ ptr += count + 1;
+ ptr += strspn( ptr, delim );
+ }
+
+ free( props );
+
+ return 0;
+}
+
+
+/** Set a property to a string.
+ *
+ * This makes a copy of the string value you supply.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the property's new value
+ * \return true if error
+ */
+
+int mlt_properties_set( mlt_properties this, const char *name, const char *value )
+{
+ int error = 1;
+
+ // Fetch the property to work with
+ mlt_property property = mlt_properties_fetch( this, name );
+
+ // Set it if not NULL
+ if ( property == NULL )
+ {
+ mlt_log( NULL, MLT_LOG_FATAL, "Whoops - %s not found (should never occur)\n", name );
+ }
+ else if ( value == NULL )
+ {
+ error = mlt_property_set_string( property, value );
+ mlt_properties_do_mirror( this, name );
+ }
+ else if ( *value != '@' )
+ {
+ error = mlt_property_set_string( property, value );
+ mlt_properties_do_mirror( this, name );
+ }
+ else if ( value[ 0 ] == '@' )
+ {
+ double total = 0;
+ double current = 0;
+ char id[ 255 ];
+ char op = '+';
+
+ value ++;
+
+ while ( *value != '\0' )
+ {
+ int length = strcspn( value, "+-*/" );
+
+ // Get the identifier
+ strncpy( id, value, length );
+ id[ length ] = '\0';
+ value += length;
+
+ // Determine the value
+ if ( isdigit( id[ 0 ] ) )
+ current = atof( id );
+ else
+ current = mlt_properties_get_double( this, id );
+
+ // Apply the operation
+ switch( op )
+ {
+ case '+':
+ total += current;
+ break;
+ case '-':
+ total -= current;
+ break;
+ case '*':
+ total *= current;
+ break;
+ case '/':
+ total = total / current;
+ break;
+ }
+
+ // Get the next op
+ op = *value != '\0' ? *value ++ : ' ';
+ }
+
+ error = mlt_property_set_double( property, total );
+ mlt_properties_do_mirror( this, name );
+ }
+
+ mlt_events_fire( this, "property-changed", name, NULL );
+
+ return error;
+}
+
+/** Set or default a property to a string.
+ *
+ * This makes a copy of the string value you supply.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the string value to set or NULL to use the default
+ * \param def the default string if value is NULL
+ * \return true if error
+ */
+
+int mlt_properties_set_or_default( mlt_properties this, const char *name, const char *value, const char *def )
+{
+ return mlt_properties_set( this, name, value == NULL ? def : value );
+}
+
+/** Get a string value by name.
+ *
+ * Do not free the returned string. It's lifetime is controlled by the property
+ * and this properties object.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the property's string value or NULL if it does not exist
+ */
+
+char *mlt_properties_get( mlt_properties this, const char *name )
+{
+ mlt_property value = mlt_properties_find( this, name );
+ return value == NULL ? NULL : mlt_property_get_string( value );
+}
+
+/** Get a property name by index.
+ *
+ * Do not free the returned string.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param index the numeric index of the property
+ * \return the name of the property or NULL if index is out of range
+ */
+
+char *mlt_properties_get_name( mlt_properties this, int index )
+{
+ property_list *list = this->local;
+ if ( index >= 0 && index < list->count )
+ return list->name[ index ];
+ return NULL;
+}
+
+/** Get a property's string value by index.
+ *
+ * Do not free the returned string.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param index the numeric index of the property
+ * \return the property value as a string or NULL if the index is out of range
+ */
+
+char *mlt_properties_get_value( mlt_properties this, int index )
+{
+ property_list *list = this->local;
+ if ( index >= 0 && index < list->count )
+ return mlt_property_get_string( list->value[ index ] );
+ return NULL;
+}
+
+/** Get a data value by index.
+ *
+ * Do not free the returned pointer if you supplied a destructor function when you
+ * set this property.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param index the numeric index of the property
+ * \param[out] size the size of the binary data in bytes or NULL if the index is out of range
+ */
+
+void *mlt_properties_get_data_at( mlt_properties this, int index, int *size )
+{
+ property_list *list = this->local;
+ if ( index >= 0 && index < list->count )
+ return mlt_property_get_data( list->value[ index ], size );
+ return NULL;
+}
+
+/** Return the number of items in the list.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the number of property objects
+ */
+
+int mlt_properties_count( mlt_properties this )
+{
+ property_list *list = this->local;
+ return list->count;
+}
+
+/** Set a value by parsing a name=value string.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param namevalue a string containing name and value delimited by '='
+ * \return true if there was an error
+ */
+
+int mlt_properties_parse( mlt_properties this, const char *namevalue )
+{
+ char *name = strdup( namevalue );
+ char *value = NULL;
+ int error = 0;
+ char *ptr = strchr( name, '=' );
+
+ if ( ptr )
+ {
+ *( ptr ++ ) = '\0';
+
+ if ( *ptr != '\"' )
+ {
+ value = strdup( ptr );
+ }
+ else
+ {
+ ptr ++;
+ value = strdup( ptr );
+ if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
+ value[ strlen( value ) - 1 ] = '\0';
+ }
+ }
+ else
+ {
+ value = strdup( "" );
+ }
+
+ error = mlt_properties_set( this, name, value );
+
+ free( name );
+ free( value );
+
+ return error;
+}
+
+/** Get an integer associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return The integer value, 0 if not found (which may also be a legitimate value)
+ */
+
+int mlt_properties_get_int( mlt_properties this, const char *name )
+{
+ mlt_property value = mlt_properties_find( this, name );
+ return value == NULL ? 0 : mlt_property_get_int( value );
+}
+
+/** Set a property to an integer value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the integer
+ * \return true if error
+ */
+
+int mlt_properties_set_int( mlt_properties this, const char *name, int value )
+{
+ int error = 1;
+
+ // Fetch the property to work with
+ mlt_property property = mlt_properties_fetch( this, name );
+
+ // Set it if not NULL
+ if ( property != NULL )
+ {
+ error = mlt_property_set_int( property, value );
+ mlt_properties_do_mirror( this, name );
+ }
+
+ mlt_events_fire( this, "property-changed", name, NULL );
+
+ return error;
+}
+
+/** Get a 64-bit integer associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the integer value, 0 if not found (which may also be a legitimate value)
+ */
+
+int64_t mlt_properties_get_int64( mlt_properties this, const char *name )
+{
+ mlt_property value = mlt_properties_find( this, name );
+ return value == NULL ? 0 : mlt_property_get_int64( value );
+}
+
+/** Set a property to a 64-bit integer value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the integer
+ * \return true if error
+ */
+
+int mlt_properties_set_int64( mlt_properties this, const char *name, int64_t value )
+{
+ int error = 1;
+
+ // Fetch the property to work with
+ mlt_property property = mlt_properties_fetch( this, name );
+
+ // Set it if not NULL
+ if ( property != NULL )
+ {
+ error = mlt_property_set_int64( property, value );
+ mlt_properties_do_mirror( this, name );
+ }
+
+ mlt_events_fire( this, "property-changed", name, NULL );
+
+ return error;
+}
+
+/** Get a floating point value associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the floating point, 0 if not found (which may also be a legitimate value)
+ */
+
+double mlt_properties_get_double( mlt_properties this, const char *name )
+{
+ mlt_property value = mlt_properties_find( this, name );
+ return value == NULL ? 0 : mlt_property_get_double( value );
+}
+
+/** Set a property to a floating point value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the floating point value
+ * \return true if error
+ */
+
+int mlt_properties_set_double( mlt_properties this, const char *name, double value )
+{
+ int error = 1;
+
+ // Fetch the property to work with
+ mlt_property property = mlt_properties_fetch( this, name );
+
+ // Set it if not NULL
+ if ( property != NULL )
+ {
+ error = mlt_property_set_double( property, value );
+ mlt_properties_do_mirror( this, name );
+ }
+
+ mlt_events_fire( this, "property-changed", name, NULL );
+
+ return error;
+}
+
+/** Get a position value associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the position, 0 if not found (which may also be a legitimate value)
+ */
+
+mlt_position mlt_properties_get_position( mlt_properties this, const char *name )
+{
+ mlt_property value = mlt_properties_find( this, name );
+ return value == NULL ? 0 : mlt_property_get_position( value );
+}
+
+/** Set a property to a position value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \param value the position
+ * \return true if error
+ */
+
+int mlt_properties_set_position( mlt_properties this, const char *name, mlt_position value )
+{
+ int error = 1;
+
+ // Fetch the property to work with
+ mlt_property property = mlt_properties_fetch( this, name );
+
+ // Set it if not NULL
+ if ( property != NULL )
+ {
+ error = mlt_property_set_position( property, value );
+ mlt_properties_do_mirror( this, name );
+ }
+
+ mlt_events_fire( this, "property-changed", name, NULL );
+
+ return error;
+}
+
+/** Get a binary data value associated to the name.
+ *
+ * Do not free the returned pointer if you supplied a destructor function
+ * when you set this property.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \param[out] length The size of the binary data in bytes, if available (often it is not, you should know)
+ */
+
+void *mlt_properties_get_data( mlt_properties this, const char *name, int *length )
+{
+ mlt_property value = mlt_properties_find( this, name );
+ return value == NULL ? NULL : mlt_property_get_data( value, length );
+}
+
+/** Store binary data as a property.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value an opaque pointer to binary data
+ * \param length the size of the binary data in bytes (optional)
+ * \param destroy a function to dellacate the binary data when the property is closed (optional)
+ * \param serialise a function that can serialize the binary data as text (optional)
+ * \return true if error
+ */
+
+int mlt_properties_set_data( mlt_properties this, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
+{
+ int error = 1;
+
+ // Fetch the property to work with
+ mlt_property property = mlt_properties_fetch( this, name );
+
+ // Set it if not NULL
+ if ( property != NULL )
+ error = mlt_property_set_data( property, value, length, destroy, serialise );
+
+ mlt_events_fire( this, "property-changed", name, NULL );
+
+ return error;
+}
+
+/** Rename a property.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param source the property to rename
+ * \param dest the new name
+ * \return true if the name is already in use
+ */
+
+int mlt_properties_rename( mlt_properties this, const char *source, const char *dest )
+{
+ mlt_property value = mlt_properties_find( this, dest );
+
+ if ( value == NULL )
+ {
+ property_list *list = this->local;
+ int i = 0;
+
+ // Locate the item
+ for ( i = 0; i < list->count; i ++ )
+ {
+ if ( !strcmp( list->name[ i ], source ) )
+ {
+ free( list->name[ i ] );
+ list->name[ i ] = strdup( dest );
+ list->hash[ generate_hash( dest ) ] = i + 1;
+ break;
+ }
+ }
+ }
+
+ return value != NULL;
+}
+
+/** Dump the properties to a file handle.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param output a file handle
+ */
+
+void mlt_properties_dump( mlt_properties this, FILE *output )
+{
+ property_list *list = this->local;
+ int i = 0;
+ for ( i = 0; i < list->count; i ++ )
+ if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
+ fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
+}
+
+/** Output the properties to a file handle.
+ *
+ * This version includes reference counts and does not put each property on a new line.
+ * \public \memberof mlt_properties_s
+ * \param this a properties pointer
+ * \param title a string to preface the output
+ * \param output a file handle
+ */
+void mlt_properties_debug( mlt_properties this, const char *title, FILE *output )
+{
+ if ( output == NULL ) output = stderr;
+ fprintf( output, "%s: ", title );
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ int i = 0;
+ fprintf( output, "[ ref=%d", list->ref_count );
+ for ( i = 0; i < list->count; i ++ )
+ if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
+ fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
+ else
+ fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( this, list->name[ i ], NULL ) );
+ fprintf( output, " ]" );
+ }
+ fprintf( output, "\n" );
+}
+
+/** Save the properties to a file by name.
+ *
+ * This uses the dump format - one line per property.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param filename the name of a file to create or overwrite
+ * \return true if there was an error
+ */
+
+int mlt_properties_save( mlt_properties this, const char *filename )
+{
+ int error = 1;
+ FILE *f = fopen( filename, "w" );
+ if ( f != NULL )
+ {
+ mlt_properties_dump( this, f );
+ fclose( f );
+ error = 0;
+ }
+ return error;
+}
+
+/* This is a very basic cross platform fnmatch replacement - it will fail in
+ * many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
+ */
+
+/** Test whether a filename or pathname matches a shell-style pattern.
+ *
+ * \private \memberof mlt_properties_s
+ * \param wild a string containing a wildcard pattern
+ * \param file the name of a file to test against
+ * \return true if the file name matches the wildcard pattern
+ */
+
+static int mlt_fnmatch( const char *wild, const char *file )
+{
+ int f = 0;
+ int w = 0;
+
+ while( f < strlen( file ) && w < strlen( wild ) )
+ {
+ if ( wild[ w ] == '*' )
+ {
+ w ++;
+ if ( w == strlen( wild ) )
+ f = strlen( file );
+ while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
+ f ++;
+ }
+ else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
+ {
+ f ++;
+ w ++;
+ }
+ else if ( wild[ 0 ] == '*' )
+ {
+ w = 0;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ return strlen( file ) == f && strlen( wild ) == w;
+}
+
+/** Compare the string or serialized value of two properties.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a property
+ * \param that a property
+ * \return < 0 if 'this' less than 'that', 0 if equal, or > 0 if 'this' is greater than 'that'
+ */
+
+static int mlt_compare( const void *this, const void *that )
+{
+ return strcmp( mlt_property_get_string( *( const mlt_property * )this ), mlt_property_get_string( *( const mlt_property * )that ) );
+}
+
+/** Get the contents of a directory.
+ *
+ * Obtains an optionally sorted list of the files found in a directory with a specific wild card.
+ * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
+ * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param dirname the name of the directory
+ * \param pattern a wildcard pattern to filter the directory listing
+ * \param sort Do you want to sort the directory listing?
+ * \return the number of items in the directory listing
+ */
+
+int mlt_properties_dir_list( mlt_properties this, const char *dirname, const char *pattern, int sort )
+{
+ DIR *dir = opendir( dirname );
+
+ if ( dir )
+ {
+ char key[ 20 ];
+ struct dirent *de = readdir( dir );
+ char fullname[ 1024 ];
+ while( de != NULL )
+ {
+ sprintf( key, "%d", mlt_properties_count( this ) );
+ snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
+ if ( pattern == NULL )
+ mlt_properties_set( this, key, fullname );
+ else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
+ mlt_properties_set( this, key, fullname );
+ de = readdir( dir );
+ }
+
+ closedir( dir );
+ }
+
+ if ( sort && mlt_properties_count( this ) )
+ {
+ property_list *list = this->local;
+ qsort( list->value, mlt_properties_count( this ), sizeof( mlt_property ), mlt_compare );
+ }
+
+ return mlt_properties_count( this );
+}
+
+/** Close a properties object.
+ *
+ * Deallocates the properties object and everything it contains.
+ * \public \memberof mlt_properties_s
+ * \param this a properties object
+ */
+
+void mlt_properties_close( mlt_properties this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
+ {
+ if ( this->close != NULL )
+ {
+ this->close( this->close_object );
+ }
+ else
+ {
+ property_list *list = this->local;
+ int index = 0;
+
+#if _MLT_PROPERTY_CHECKS_ == 1
+ // Show debug info
+ mlt_properties_debug( this, "Closing", stderr );
+#endif
+
+#ifdef _MLT_PROPERTY_CHECKS_
+ // Increment destroyed count
+ properties_destroyed ++;
+
+ // Show current stats - these should match when the app is closed
+ mlt_log( NULL, MLT_LOG_DEBUG, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
+#endif
+
+ // Clean up names and values
+ for ( index = list->count - 1; index >= 0; index -- )
+ {
+ free( list->name[ index ] );
+ mlt_property_close( list->value[ index ] );
+ }
+
+ // Clear up the list
+ pthread_mutex_destroy( &list->mutex );
+ free( list->name );
+ free( list->value );
+ free( list );
+
+ // Free this now if this has no child
+ if ( this->child == NULL )
+ free( this );
+ }
+ }
+}
+
+/** Determine if the properties list is really just a sequence or ordered list.
+ *
+ * \public \memberof mlt_properties_s
+ * \param properties a properties list
+ * \return true if all of the property names are numeric (a sequence)
+ */
+
+int mlt_properties_is_sequence( mlt_properties properties )
+{
+ int i;
+ int n = mlt_properties_count( properties );
+ for ( i = 0; i < n; i++ )
+ if ( ! isdigit( mlt_properties_get_name( properties, i )[0] ) )
+ return 0;
+ return 1;
+}
+
+/** \brief YAML Tiny Parser context structure
+ *
+ * YAML is a nifty text format popular in the Ruby world as a cleaner,
+ * less verbose alternative to XML. See this Wikipedia topic for an overview:
+ * http://en.wikipedia.org/wiki/YAML
+ * The YAML specification is at:
+ * http://yaml.org/
+ * YAML::Tiny is a Perl module that specifies a subset of YAML that we are
+ * using here (for the same reasons):
+ * http://search.cpan.org/~adamk/YAML-Tiny-1.25/lib/YAML/Tiny.pm
+ * \private
+ */
+
+struct yaml_parser_context
+{
+ mlt_deque stack;
+ unsigned int level;
+ unsigned int index;
+ char block;
+ char *block_name;
+ unsigned int block_indent;
+
+};
+typedef struct yaml_parser_context *yaml_parser;
+
+/** Remove spaces from the left side of a string.
+ *
+ * \param s the string to trim
+ * \return the number of characters removed
+ */
+
+static unsigned int ltrim( char **s )
+{
+ unsigned int i = 0;
+ char *c = *s;
+ int n = strlen( c );
+ for ( i = 0; i < n && *c == ' '; i++, c++ );
+ *s = c;
+ return i;
+}
+
+/** Remove spaces from the right side of a string.
+ *
+ * \param s the string to trim
+ * \return the number of characters removed
+ */
+
+static unsigned int rtrim( char *s )
+{
+ int n = strlen( s );
+ int i;
+ for ( i = n; i > 0 && s[i - 1] == ' '; --i )
+ s[i - 1] = 0;
+ return n - i;
+}
+
+/** Parse a line of YAML Tiny.
+ *
+ * Adds a property if needed.
+ * \private \memberof yaml_parser_context
+ * \param context a YAML Tiny Parser context
+ * \param namevalue a line of YAML Tiny
+ * \return true if there was an error
+ */
+
+static int parse_yaml( yaml_parser context, const char *namevalue )
+{
+ char *name_ = strdup( namevalue );
+ char *name = name_;
+ char *value = NULL;
+ int error = 0;
+ char *ptr = strchr( name, ':' );
+ unsigned int indent = ltrim( &name );
+ mlt_properties properties = mlt_deque_peek_front( context->stack );
+
+ // Ascending one more levels in the tree
+ if ( indent < context->level )
+ {
+ unsigned int i;
+ unsigned int n = ( context->level - indent ) / 2;
+ for ( i = 0; i < n; i++ )
+ mlt_deque_pop_front( context->stack );
+ properties = mlt_deque_peek_front( context->stack );
+ context->level = indent;
+ }
+
+ // Descending a level in the tree
+ else if ( indent > context->level && context->block == 0 )
+ {
+ context->level = indent;
+ }
+
+ // If there is a colon that is not part of a block
+ if ( ptr && ( indent == context->level ) )
+ {
+ // Reset block processing
+ if ( context->block_name )
+ {
+ free( context->block_name );
+ context->block_name = NULL;
+ context->block = 0;
+ }
+
+ // Terminate the name and setup the value pointer
+ *( ptr ++ ) = 0;
+
+ // Trim comment
+ char *comment = strchr( ptr, '#' );
+ if ( comment )
+ {
+ *comment = 0;
+ }
+
+ // Trim leading and trailing spaces from bare value
+ ltrim( &ptr );
+ rtrim( ptr );
+
+ // No value means a child
+ if ( strcmp( ptr, "" ) == 0 )
+ {
+ mlt_properties child = mlt_properties_new();
+ mlt_properties_set_data( properties, name, child, 0,
+ ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_deque_push_front( context->stack, child );
+ context->index = 0;
+ free( name_ );
+ return error;
+ }
+
+ // A dash indicates a sequence item
+ if ( name[0] == '-' )
+ {
+ mlt_properties child = mlt_properties_new();
+ char key[20];
+
+ snprintf( key, sizeof(key), "%d", context->index++ );
+ mlt_properties_set_data( properties, key, child, 0,
+ ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_deque_push_front( context->stack, child );
+
+ name ++;
+ context->level += ltrim( &name ) + 1;
+ properties = child;
+ }
+
+ // Value is quoted
+ if ( *ptr == '\"' )
+ {
+ ptr ++;
+ value = strdup( ptr );
+ if ( value && value[ strlen( value ) - 1 ] == '\"' )
+ value[ strlen( value ) - 1 ] = 0;
+ }
+
+ // Value is folded or unfolded block
+ else if ( *ptr == '|' || *ptr == '>' )
+ {
+ context->block = *ptr;
+ context->block_name = strdup( name );
+ context->block_indent = 0;
+ value = strdup( "" );
+ }
+
+ // Bare value
+ else
+ {
+ value = strdup( ptr );
+ }
+ }
+
+ // A list of scalars
+ else if ( name[0] == '-' )
+ {
+ // Reset block processing
+ if ( context->block_name )
+ {
+ free( context->block_name );
+ context->block_name = NULL;
+ context->block = 0;
+ }
+
+ char key[20];
+
+ snprintf( key, sizeof(key), "%d", context->index++ );
+ ptr = name + 1;
+
+ // Trim comment
+ char *comment = strchr( ptr, '#' );
+ if ( comment )
+ *comment = 0;
+
+ // Trim leading and trailing spaces from bare value
+ ltrim( &ptr );
+ rtrim( ptr );
+
+ // Value is quoted
+ if ( *ptr == '\"' )
+ {
+ ptr ++;
+ value = strdup( ptr );
+ if ( value && value[ strlen( value ) - 1 ] == '\"' )
+ value[ strlen( value ) - 1 ] = 0;
+ }
+
+ // Value is folded or unfolded block
+ else if ( *ptr == '|' || *ptr == '>' )
+ {
+ context->block = *ptr;
+ context->block_name = strdup( key );
+ context->block_indent = 0;
+ value = strdup( "" );
+ }
+
+ // Bare value
+ else
+ {
+ value = strdup( ptr );
+ }
+
+ free( name_ );
+ name = name_ = strdup( key );
+ }
+
+ // Non-folded block
+ else if ( context->block == '|' )
+ {
+ if ( context->block_indent == 0 )
+ context->block_indent = indent;
+ if ( indent > context->block_indent )
+ name = &name_[ context->block_indent ];
+ rtrim( name );
+ char *old_value = mlt_properties_get( properties, context->block_name );
+ value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
+ strcpy( value, old_value );
+ if ( strcmp( old_value, "" ) )
+ strcat( value, "\n" );
+ strcat( value, name );
+ name = context->block_name;
+ }
+
+ // Folded block
+ else if ( context->block == '>' )
+ {
+ ltrim( &name );
+ rtrim( name );
+ char *old_value = mlt_properties_get( properties, context->block_name );
+
+ // Blank line (prepended with spaces) is new line
+ if ( strcmp( name, "" ) == 0 )
+ {
+ value = calloc( 1, strlen( old_value ) + 2 );
+ strcat( value, old_value );
+ strcat( value, "\n" );
+ }
+ // Concatenate with space
+ else
+ {
+ value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
+ strcat( value, old_value );
+ if ( strcmp( old_value, "" ) && old_value[ strlen( old_value ) - 1 ] != '\n' )
+ strcat( value, " " );
+ strcat( value, name );
+ }
+ name = context->block_name;
+ }
+
+ else
+ {
+ value = strdup( "" );
+ }
+
+ error = mlt_properties_set( properties, name, value );
+
+ free( name_ );
+ free( value );
+
+ return error;
+}
+
+/** Parse a YAML Tiny file by name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param filename the name of a text file containing YAML Tiny
+ * \return a new properties list
+ */
+
+mlt_properties mlt_properties_parse_yaml( const char *filename )
+{
+ // Construct a standalone properties object
+ mlt_properties this = mlt_properties_new( );
+
+ if ( this )
+ {
+ // Open the file
+ FILE *file = fopen( filename, "r" );
+
+ // Load contents of file
+ if ( file )
+ {
+ // Temp string
+ char temp[ 1024 ];
+ char *ptemp = &temp[ 0 ];
+
+ // Parser context
+ yaml_parser context = calloc( 1, sizeof( struct yaml_parser_context ) );
+ context->stack = mlt_deque_init();
+ mlt_deque_push_front( context->stack, this );
+
+ // Read each string from the file
+ while( fgets( temp, 1024, file ) )
+ {
+ // Check for end-of-stream
+ if ( strncmp( ptemp, "...", 3 ) == 0 )
+ break;
+
+ // Chomp the string
+ temp[ strlen( temp ) - 1 ] = '\0';
+
+ // Skip blank lines, comment lines, and document separator
+ if ( strcmp( ptemp, "" ) && ptemp[ 0 ] != '#' && strncmp( ptemp, "---", 3 )
+ && strncmp( ptemp, "%YAML", 5 ) && strncmp( ptemp, "% YAML", 6 ) )
+ parse_yaml( context, temp );
+ }
+
+ // Close the file
+ fclose( file );
+ mlt_deque_close( context->stack );
+ if ( context->block_name )
+ free( context->block_name );
+ free( context );
+ }
+ }
+
+ // Return the pointer
+ return this;
+}
+
+/*
+ * YAML Tiny Serializer
+ */
+
+/** How many bytes to grow at a time */
+#define STRBUF_GROWTH (1024)
+
+/** \brief Private to mlt_properties_s, a self-growing buffer for building strings
+ * \private
+ */
+
+struct strbuf_s
+{
+ size_t size;
+ char *string;
+};
+
+typedef struct strbuf_s *strbuf;
+
+/** Create a new string buffer
+ *
+ * \private \memberof strbuf_s
+ * \return a new string buffer
+ */
+
+static strbuf strbuf_new( )
+{
+ strbuf buffer = calloc( 1, sizeof( struct strbuf_s ) );
+ buffer->size = STRBUF_GROWTH;
+ buffer->string = calloc( 1, buffer->size );
+ return buffer;
+}
+
+/** Destroy a string buffer
+ *
+ * \private \memberof strbuf_s
+ * \param buffer the string buffer to close
+ */
+
+static void strbuf_close( strbuf buffer )
+{
+ // We do not free buffer->string; strbuf user must save that pointer
+ // and free it.
+ if ( buffer )
+ free( buffer );
+}
+
+/** Format a string into a string buffer
+ *
+ * A variable number of arguments follows the format string - one for each
+ * format specifier.
+ * \private \memberof strbuf_s
+ * \param buffer the string buffer to write into
+ * \param format a string that contains text and formatting instructions
+ * \return the formatted string
+ */
+
+static char *strbuf_printf( strbuf buffer, const char *format, ... )
+{
+ while ( buffer->string )
+ {
+ va_list ap;
+ va_start( ap, format );
+ size_t len = strlen( buffer->string );
+ size_t remain = buffer->size - len - 1;
+ int need = vsnprintf( buffer->string + len, remain, format, ap );
+ va_end( ap );
+ if ( need > -1 && need < remain )
+ break;
+ buffer->string[ len ] = 0;
+ buffer->size += need + STRBUF_GROWTH;
+ buffer->string = realloc( buffer->string, buffer->size );
+ }
+ return buffer->string;
+}
+
+/** Indent a line of YAML Tiny.
+ *
+ * \private \memberof strbuf_s
+ * \param output a string buffer
+ * \param indent the number of spaces to indent
+ */
+
+static inline void indent_yaml( strbuf output, int indent )
+{
+ int j;
+ for ( j = 0; j < indent; j++ )
+ strbuf_printf( output, " " );
+}
+
+/** Convert a line string into a YAML block literal.
+ *
+ * \private \memberof strbuf_s
+ * \param output a string buffer
+ * \param value the string to format as a block literal
+ * \param indent the number of spaces to indent
+ */
+
+static void output_yaml_block_literal( strbuf output, const char *value, int indent )
+{
+ char *v = strdup( value );
+ char *sol = v;
+ char *eol = strchr( sol, '\n' );
+
+ while ( eol )
+ {
+ indent_yaml( output, indent );
+ *eol = '\0';
+ strbuf_printf( output, "%s\n", sol );
+ sol = eol + 1;
+ eol = strchr( sol, '\n' );
+ }
+ indent_yaml( output, indent );
+ strbuf_printf( output, "%s\n", sol );
+}
+
+/** Recursively serialize a properties list into a string buffer as YAML Tiny.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param output a string buffer to hold the serialized YAML Tiny
+ * \param indent the number of spaces to indent (for recursion, initialize to 0)
+ * \param is_parent_sequence Is 'this' properties list really just a sequence (for recursion, initialize to 0)?
+ */
+
+static void serialise_yaml( mlt_properties this, strbuf output, int indent, int is_parent_sequence )
+{
+ property_list *list = this->local;
+ int i = 0;
+
+ for ( i = 0; i < list->count; i ++ )
+ {
+ // This implementation assumes that all data elements are property lists.
+ // Unfortunately, we do not have run time type identification.
+ mlt_properties child = mlt_property_get_data( list->value[ i ], NULL );
+
+ if ( mlt_properties_is_sequence( this ) )
+ {
+ // Ignore hidden/non-serialisable items
+ if ( list->name[ i ][ 0 ] != '_' )
+ {
+ // Indicate a sequence item
+ indent_yaml( output, indent );
+ strbuf_printf( output, "- " );
+
+ // If the value can be represented as a string
+ const char *value = mlt_properties_get( this, list->name[ i ] );
+ if ( value && strcmp( value, "" ) )
+ {
+ // Determine if this is an unfolded block literal
+ if ( strchr( value, '\n' ) )
+ {
+ strbuf_printf( output, "|\n" );
+ output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( "|" ) );
+ }
+ else
+ {
+ strbuf_printf( output, "%s\n", value );
+ }
+ }
+ }
+ // Recurse on child
+ if ( child )
+ serialise_yaml( child, output, indent + 2, 1 );
+ }
+ else
+ {
+ // Assume this is a normal map-oriented properties list
+ const char *value = mlt_properties_get( this, list->name[ i ] );
+
+ // Ignore hidden/non-serialisable items
+ // If the value can be represented as a string
+ if ( list->name[ i ][ 0 ] != '_' && value && strcmp( value, "" ) )
+ {
+ if ( is_parent_sequence == 0 )
+ indent_yaml( output, indent );
+ else
+ is_parent_sequence = 0;
+
+ // Determine if this is an unfolded block literal
+ if ( strchr( value, '\n' ) )
+ {
+ strbuf_printf( output, "%s: |\n", list->name[ i ] );
+ output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( ": " ) );
+ }
+ else
+ {
+ strbuf_printf( output, "%s: %s\n", list->name[ i ], value );
+ }
+ }
+
+ // Output a child as a map item
+ if ( child )
+ {
+ indent_yaml( output, indent );
+ strbuf_printf( output, "%s:\n", list->name[ i ] );
+
+ // Recurse on child
+ serialise_yaml( child, output, indent + 2, 0 );
+ }
+ }
+ }
+}
+
+/** Serialize a properties list as a string of YAML Tiny.
+ *
+ * The caller MUST free the returned string!
+ * This operates on properties containing properties as a hierarchical data
+ * structure.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return a string containing YAML Tiny that represents the properties list
+ */
+
+char *mlt_properties_serialise_yaml( mlt_properties this )
+{
+ strbuf b = strbuf_new();
+ strbuf_printf( b, "---\n" );
+ serialise_yaml( this, b, 0, 0 );
+ strbuf_printf( b, "...\n" );
+ char *ret = b->string;
+ strbuf_close( b );
+ return ret;
+}
--- /dev/null
+/**
+ * \file mlt_properties.h
+ * \brief Properties class declaration
+ * \see mlt_properties_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_PROPERTIES_H_
+#define _MLT_PROPERTIES_H_
+
+#include "mlt_types.h"
+#include "mlt_events.h"
+#include <stdio.h>
+
+/** \brief Properties class
+ *
+ * Properties is a combination list/dictionary of name/::mlt_property pairs.
+ * It is also a base class for many of the other MLT classes.
+ */
+
+struct mlt_properties_s
+{
+ void *child; /**< \private the object of a subclass */
+ void *local; /**< \private instance object */
+
+ /** the destructor virtual function */
+ mlt_destructor close;
+ void *close_object; /**< the object supplied to the close virtual function */
+};
+
+extern int mlt_properties_init( mlt_properties, void *child );
+extern mlt_properties mlt_properties_new( );
+extern mlt_properties mlt_properties_load( const char *file );
+extern int mlt_properties_inc_ref( mlt_properties self );
+extern int mlt_properties_dec_ref( mlt_properties self );
+extern int mlt_properties_ref_count( mlt_properties self );
+extern void mlt_properties_mirror( mlt_properties self, mlt_properties that );
+extern int mlt_properties_inherit( mlt_properties self, mlt_properties that );
+extern int mlt_properties_pass( mlt_properties self, mlt_properties that, const char *prefix );
+extern void mlt_properties_pass_property( mlt_properties self, mlt_properties that, const char *name );
+extern int mlt_properties_pass_list( mlt_properties self, mlt_properties that, const char *list );
+extern int mlt_properties_set( mlt_properties self, const char *name, const char *value );
+extern int mlt_properties_set_or_default( mlt_properties self, const char *name, const char *value, const char *def );
+extern int mlt_properties_parse( mlt_properties self, const char *namevalue );
+extern char *mlt_properties_get( mlt_properties self, const char *name );
+extern char *mlt_properties_get_name( mlt_properties self, int index );
+extern char *mlt_properties_get_value( mlt_properties self, int index );
+extern void *mlt_properties_get_data_at( mlt_properties self, int index, int *size );
+extern int mlt_properties_get_int( mlt_properties self, const char *name );
+extern int mlt_properties_set_int( mlt_properties self, const char *name, int value );
+extern int64_t mlt_properties_get_int64( mlt_properties self, const char *name );
+extern int mlt_properties_set_int64( mlt_properties self, const char *name, int64_t value );
+extern double mlt_properties_get_double( mlt_properties self, const char *name );
+extern int mlt_properties_set_double( mlt_properties self, const char *name, double value );
+extern mlt_position mlt_properties_get_position( mlt_properties self, const char *name );
+extern int mlt_properties_set_position( mlt_properties self, const char *name, mlt_position value );
+extern int mlt_properties_set_data( mlt_properties self, const char *name, void *value, int length, mlt_destructor, mlt_serialiser );
+extern void *mlt_properties_get_data( mlt_properties self, const char *name, int *length );
+extern int mlt_properties_rename( mlt_properties self, const char *source, const char *dest );
+extern int mlt_properties_count( mlt_properties self );
+extern void mlt_properties_dump( mlt_properties self, FILE *output );
+extern void mlt_properties_debug( mlt_properties self, const char *title, FILE *output );
+extern int mlt_properties_save( mlt_properties, const char * );
+extern int mlt_properties_dir_list( mlt_properties, const char *, const char *, int );
+extern void mlt_properties_close( mlt_properties self );
+extern int mlt_properties_is_sequence( mlt_properties self );
+extern mlt_properties mlt_properties_parse_yaml( const char *file );
+extern char *mlt_properties_serialise_yaml( mlt_properties self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_property.c
+ * \brief Property class definition
+ * \see mlt_property_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_property.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/** Bit pattern used internally to indicated representations available.
+*/
+
+typedef enum
+{
+ mlt_prop_none = 0, //!< not set
+ mlt_prop_int = 1, //!< set as an integer
+ mlt_prop_string = 2, //!< set as string or already converted to string
+ mlt_prop_position = 4,//!< set as a position
+ mlt_prop_double = 8, //!< set as a floating point
+ mlt_prop_data = 16, //!< set as opaque binary
+ mlt_prop_int64 = 32 //!< set as a 64-bit integer
+}
+mlt_property_type;
+
+/** \brief Property class
+ *
+ * A property is like a variant or dynamic type. They are used for many things
+ * in MLT, but in particular they are the parameter mechanism for the plugins.
+ */
+
+struct mlt_property_s
+{
+ /// Stores a bit pattern of types available for this property
+ mlt_property_type types;
+
+ /// Atomic type handling
+ int prop_int;
+ mlt_position prop_position;
+ double prop_double;
+ int64_t prop_int64;
+
+ /// String handling
+ char *prop_string;
+
+ /// Generic type handling
+ void *data;
+ int length;
+ mlt_destructor destructor;
+ mlt_serialiser serialiser;
+};
+
+/** Construct a property and initialize it
+ * \public \memberof mlt_property_s
+ */
+
+mlt_property mlt_property_init( )
+{
+ mlt_property this = malloc( sizeof( struct mlt_property_s ) );
+ if ( this != NULL )
+ {
+ this->types = 0;
+ this->prop_int = 0;
+ this->prop_position = 0;
+ this->prop_double = 0;
+ this->prop_int64 = 0;
+ this->prop_string = NULL;
+ this->data = NULL;
+ this->length = 0;
+ this->destructor = NULL;
+ this->serialiser = NULL;
+ }
+ return this;
+}
+
+/** Clear (0/null) a property.
+ *
+ * Frees up any associated resources in the process.
+ * \private \memberof mlt_property_s
+ * \param this a property
+ */
+
+static inline void mlt_property_clear( mlt_property this )
+{
+ // Special case data handling
+ if ( this->types & mlt_prop_data && this->destructor != NULL )
+ this->destructor( this->data );
+
+ // Special case string handling
+ if ( this->types & mlt_prop_string )
+ free( this->prop_string );
+
+ // Wipe stuff
+ this->types = 0;
+ this->prop_int = 0;
+ this->prop_position = 0;
+ this->prop_double = 0;
+ this->prop_int64 = 0;
+ this->prop_string = NULL;
+ this->data = NULL;
+ this->length = 0;
+ this->destructor = NULL;
+ this->serialiser = NULL;
+}
+
+/** Set the property to an integer value.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value an integer
+ * \return false
+ */
+
+int mlt_property_set_int( mlt_property this, int value )
+{
+ mlt_property_clear( this );
+ this->types = mlt_prop_int;
+ this->prop_int = value;
+ return 0;
+}
+
+/** Set the property to a floating point value.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value a double precision floating point value
+ * \return false
+ */
+
+int mlt_property_set_double( mlt_property this, double value )
+{
+ mlt_property_clear( this );
+ this->types = mlt_prop_double;
+ this->prop_double = value;
+ return 0;
+}
+
+/** Set the property to a position value.
+ *
+ * Position is a relative time value in frame units.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value a position value
+ * \return false
+ */
+
+int mlt_property_set_position( mlt_property this, mlt_position value )
+{
+ mlt_property_clear( this );
+ this->types = mlt_prop_position;
+ this->prop_position = value;
+ return 0;
+}
+
+/** Set the property to a string value.
+ *
+ * This makes a copy of the string you supply so you do not need to track
+ * a new reference to it.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value the string to copy to the property
+ * \return true if it failed
+ */
+
+int mlt_property_set_string( mlt_property this, const char *value )
+{
+ if ( value != this->prop_string )
+ {
+ mlt_property_clear( this );
+ this->types = mlt_prop_string;
+ if ( value != NULL )
+ this->prop_string = strdup( value );
+ }
+ else
+ {
+ this->types = mlt_prop_string;
+ }
+ return this->prop_string == NULL;
+}
+
+/** Set the property to a 64-bit integer value.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value a 64-bit integer
+ * \return false
+ */
+
+int mlt_property_set_int64( mlt_property this, int64_t value )
+{
+ mlt_property_clear( this );
+ this->types = mlt_prop_int64;
+ this->prop_int64 = value;
+ return 0;
+}
+
+/** Set a property to an opaque binary value.
+ *
+ * This does not make a copy of the data. You can use a Properties object
+ * with its reference tracking and the destructor function to control
+ * the lifetime of the data. Otherwise, pass NULL for the destructor
+ * function and control the lifetime yourself.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value an opaque pointer
+ * \param length the number of bytes pointed to by value (optional)
+ * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
+ * \param serialiser a function to use to convert this binary data to a string (optional)
+ * \return false
+ */
+
+int mlt_property_set_data( mlt_property this, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
+{
+ if ( this->data == value )
+ this->destructor = NULL;
+ mlt_property_clear( this );
+ this->types = mlt_prop_data;
+ this->data = value;
+ this->length = length;
+ this->destructor = destructor;
+ this->serialiser = serialiser;
+ return 0;
+}
+
+/** Convert a base 10 or base 16 string to an integer.
+ *
+ * The string must begin with '0x' to be interpreted as hexadecimal.
+ * Otherwise, it is interpreted as base 10.
+ * If the string begins with '#' it is interpreted as a hexadecimal color value
+ * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
+ * always in the form RRGGBBAA where the alpha components are not optional.
+ * Applications and services should expect the binary color value in bytes to
+ * be in the following order: RGBA. This means they will have to cast the int
+ * to an unsigned int. This is especially important when they need to shift
+ * right to obtain RGB without alpha in order to make it do a logical instead
+ * of arithmetic shift.
+ *
+ * \private \memberof mlt_property_s
+ * \param value a string to convert
+ * \return the resultant integer
+ */
+static inline int mlt_property_atoi( const char *value )
+{
+ if ( value == NULL )
+ return 0;
+ // Parse a hex color value as #RRGGBB or #AARRGGBB.
+ if ( value[0] == '#' )
+ {
+ unsigned int rgb = strtoul( value + 1, NULL, 16 );
+ unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
+ return ( rgb << 8 ) | alpha;
+ }
+ // Do hex and decimal explicitly to avoid decimal value with leading zeros
+ // interpreted as octal.
+ else if ( value[0] == '0' && value[1] == 'x' )
+ {
+ return strtoul( value + 2, NULL, 16 );
+ }
+ else
+ {
+ return strtol( value, NULL, 10 );
+ }
+}
+
+/** Get the property as an integer.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return an integer value
+ */
+
+int mlt_property_get_int( mlt_property this )
+{
+ if ( this->types & mlt_prop_int )
+ return this->prop_int;
+ else if ( this->types & mlt_prop_double )
+ return ( int )this->prop_double;
+ else if ( this->types & mlt_prop_position )
+ return ( int )this->prop_position;
+ else if ( this->types & mlt_prop_int64 )
+ return ( int )this->prop_int64;
+ else if ( this->types & mlt_prop_string )
+ return mlt_property_atoi( this->prop_string );
+ return 0;
+}
+
+/** Get the property as a floating point.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return a floating point value
+ */
+
+double mlt_property_get_double( mlt_property this )
+{
+ if ( this->types & mlt_prop_double )
+ return this->prop_double;
+ else if ( this->types & mlt_prop_int )
+ return ( double )this->prop_int;
+ else if ( this->types & mlt_prop_position )
+ return ( double )this->prop_position;
+ else if ( this->types & mlt_prop_int64 )
+ return ( double )this->prop_int64;
+ else if ( this->types & mlt_prop_string )
+ return atof( this->prop_string );
+ return 0;
+}
+
+/** Get the property as a position.
+ *
+ * A position is an offset time in terms of frame units.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return the position in frames
+ */
+
+mlt_position mlt_property_get_position( mlt_property this )
+{
+ if ( this->types & mlt_prop_position )
+ return this->prop_position;
+ else if ( this->types & mlt_prop_int )
+ return ( mlt_position )this->prop_int;
+ else if ( this->types & mlt_prop_double )
+ return ( mlt_position )this->prop_double;
+ else if ( this->types & mlt_prop_int64 )
+ return ( mlt_position )this->prop_int64;
+ else if ( this->types & mlt_prop_string )
+ return ( mlt_position )atol( this->prop_string );
+ return 0;
+}
+
+/** Convert a string to a 64-bit integer.
+ *
+ * If the string begins with '0x' it is interpreted as a hexadecimal value.
+ * \private \memberof mlt_property_s
+ * \param value a string
+ * \return a 64-bit integer
+ */
+
+static inline int64_t mlt_property_atoll( const char *value )
+{
+ if ( value == NULL )
+ return 0;
+ else if ( value[0] == '0' && value[1] == 'x' )
+ return strtoll( value + 2, NULL, 16 );
+ else
+ return strtoll( value, NULL, 10 );
+}
+
+/** Get the property as a signed integer.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return a 64-bit integer
+ */
+
+int64_t mlt_property_get_int64( mlt_property this )
+{
+ if ( this->types & mlt_prop_int64 )
+ return this->prop_int64;
+ else if ( this->types & mlt_prop_int )
+ return ( int64_t )this->prop_int;
+ else if ( this->types & mlt_prop_double )
+ return ( int64_t )this->prop_double;
+ else if ( this->types & mlt_prop_position )
+ return ( int64_t )this->prop_position;
+ else if ( this->types & mlt_prop_string )
+ return mlt_property_atoll( this->prop_string );
+ return 0;
+}
+
+/** Get the property as a string.
+ *
+ * The caller is not responsible for deallocating the returned string!
+ * The string is deallocated when the Property is closed.
+ * This tries its hardest to convert the property to string including using
+ * a serialization function for binary data, if supplied.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return a string representation of the property or NULL if failed
+ */
+
+char *mlt_property_get_string( mlt_property this )
+{
+ // Construct a string if need be
+ if ( ! ( this->types & mlt_prop_string ) )
+ {
+ if ( this->types & mlt_prop_int )
+ {
+ this->types |= mlt_prop_string;
+ this->prop_string = malloc( 32 );
+ sprintf( this->prop_string, "%d", this->prop_int );
+ }
+ else if ( this->types & mlt_prop_double )
+ {
+ this->types |= mlt_prop_string;
+ this->prop_string = malloc( 32 );
+ sprintf( this->prop_string, "%f", this->prop_double );
+ }
+ else if ( this->types & mlt_prop_position )
+ {
+ this->types |= mlt_prop_string;
+ this->prop_string = malloc( 32 );
+ sprintf( this->prop_string, "%d", (int)this->prop_position ); /* I don't know if this is wanted. -Zach */
+ }
+ else if ( this->types & mlt_prop_int64 )
+ {
+ this->types |= mlt_prop_string;
+ this->prop_string = malloc( 32 );
+ sprintf( this->prop_string, "%lld", (long long int)this->prop_int64 );
+ }
+ else if ( this->types & mlt_prop_data && this->serialiser != NULL )
+ {
+ this->types |= mlt_prop_string;
+ this->prop_string = this->serialiser( this->data, this->length );
+ }
+ }
+
+ // Return the string (may be NULL)
+ return this->prop_string;
+}
+
+/** Get the binary data from a property.
+ *
+ * This only works if you previously put binary data into the property.
+ * This does not return a copy of the data; it returns a pointer to it.
+ * If you supplied a destructor function when setting the binary data,
+ * the destructor is used when the Property is closed to free the memory.
+ * Therefore, only free the returned pointer if you did not supply a
+ * destructor function.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param[out] length the size of the binary object in bytes (optional)
+ * \return an opaque data pointer or NULL if not available
+ */
+
+void *mlt_property_get_data( mlt_property this, int *length )
+{
+ // Assign length if not NULL
+ if ( length != NULL )
+ *length = this->length;
+
+ // Return the data (note: there is no conversion here)
+ return this->data;
+}
+
+/** Destroy a property and free all related resources.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ */
+
+void mlt_property_close( mlt_property this )
+{
+ mlt_property_clear( this );
+ free( this );
+}
+
+/** Copy a property.
+ *
+ * A Property holding binary data only copies the data if a serialiser
+ * function was supplied when you set the Property.
+ * \public \memberof mlt_property_s
+ * \author Zach <zachary.drew@gmail.com>
+ * \param this a property
+ * \param that another property
+ */
+void mlt_property_pass( mlt_property this, mlt_property that )
+{
+ mlt_property_clear( this );
+
+ this->types = that->types;
+
+ if ( this->types & mlt_prop_int64 )
+ this->prop_int64 = that->prop_int64;
+ else if ( this->types & mlt_prop_int )
+ this->prop_int = that->prop_int;
+ else if ( this->types & mlt_prop_double )
+ this->prop_double = that->prop_double;
+ else if ( this->types & mlt_prop_position )
+ this->prop_position = that->prop_position;
+ else if ( this->types & mlt_prop_string )
+ {
+ if ( that->prop_string != NULL )
+ this->prop_string = strdup( that->prop_string );
+ }
+ else if ( this->types & mlt_prop_data && this->serialiser != NULL )
+ {
+ this->types = mlt_prop_string;
+ this->prop_string = this->serialiser( this->data, this->length );
+ }
+}
--- /dev/null
+/**
+ * \file mlt_property.h
+ * \brief Property class declaration
+ * \see mlt_property_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_PROPERTY_H_
+#define _MLT_PROPERTY_H_
+
+#include "mlt_types.h"
+
+extern mlt_property mlt_property_init( );
+extern int mlt_property_set_int( mlt_property self, int value );
+extern int mlt_property_set_double( mlt_property self, double value );
+extern int mlt_property_set_position( mlt_property self, mlt_position value );
+extern int mlt_property_set_int64( mlt_property self, int64_t value );
+extern int mlt_property_set_string( mlt_property self, const char *value );
+extern int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser );
+extern int mlt_property_get_int( mlt_property self );
+extern double mlt_property_get_double( mlt_property self );
+extern mlt_position mlt_property_get_position( mlt_property self );
+extern int64_t mlt_property_get_int64( mlt_property self );
+extern char *mlt_property_get_string( mlt_property self );
+extern void *mlt_property_get_data( mlt_property self, int *length );
+extern void mlt_property_close( mlt_property self );
+
+extern void mlt_property_pass( mlt_property this, mlt_property that );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_repository.c
+ * \brief provides a map between service and shared objects
+ * \see mlt_repository_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_repository.h"
+#include "mlt_properties.h"
+#include "mlt_tokeniser.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+
+/** \brief Repository class
+ *
+ * The Repository is a collection of plugin modules and their services and service metadata.
+ *
+ * \extends mlt_properties_s
+ * \properties \p language a cached list of user locales
+ */
+
+struct mlt_repository_s
+{
+ struct mlt_properties_s parent; /// a list of object files
+ mlt_properties consumers; /// a list of entry points for consumers
+ mlt_properties filters; /// a list of entry points for filters
+ mlt_properties producers; /// a list of entry points for producers
+ mlt_properties transitions; /// a list of entry points for transitions
+};
+
+/** Construct a new repository.
+ *
+ * \public \memberof mlt_repository_s
+ * \param directory the full path of a directory from which to read modules
+ * \return a new repository or NULL if failed
+ */
+
+mlt_repository mlt_repository_init( const char *directory )
+{
+ // Safety check
+ if ( directory == NULL || strcmp( directory, "" ) == 0 )
+ return NULL;
+
+ // Construct the repository
+ mlt_repository this = calloc( sizeof( struct mlt_repository_s ), 1 );
+ mlt_properties_init( &this->parent, this );
+ this->consumers = mlt_properties_new();
+ this->filters = mlt_properties_new();
+ this->producers = mlt_properties_new();
+ this->transitions = mlt_properties_new();
+
+ // Get the directory list
+ mlt_properties dir = mlt_properties_new();
+ int count = mlt_properties_dir_list( dir, directory, NULL, 0 );
+ int i;
+
+ // Iterate over files
+ for ( i = 0; i < count; i++ )
+ {
+ int flags = RTLD_NOW;
+ const char *object_name = mlt_properties_get_value( dir, i);
+
+ // Very temporary hack to allow the quicktime plugins to work
+ // TODO: extend repository to allow this to be used on a case by case basis
+ if ( strstr( object_name, "libmltkino" ) )
+ flags |= RTLD_GLOBAL;
+
+ // Open the shared object
+ void *object = dlopen( object_name, flags );
+ if ( object != NULL )
+ {
+ // Get the registration function
+ mlt_repository_callback symbol_ptr = dlsym( object, "mlt_register" );
+
+ // Call the registration function
+ if ( symbol_ptr != NULL )
+ {
+ symbol_ptr( this );
+
+ // Register the object file for closure
+ mlt_properties_set_data( &this->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL );
+ }
+ else
+ {
+ dlclose( object );
+ }
+ }
+ else if ( strstr( object_name, "libmlt" ) )
+ {
+ mlt_log( NULL, MLT_LOG_WARNING, "%s: failed to dlopen %s\n (%s)\n", __FUNCTION__, object_name, dlerror() );
+ }
+ }
+
+ mlt_properties_close( dir );
+
+ return this;
+}
+
+/** Create a properties list for a service holding a function pointer to its constructor function.
+ *
+ * \private \memberof mlt_repository_s
+ * \param symbol a pointer to a function that can create the service.
+ * \return a properties list
+ */
+
+static mlt_properties new_service( void *symbol )
+{
+ mlt_properties properties = mlt_properties_new();
+ mlt_properties_set_data( properties, "symbol", symbol, 0, NULL, NULL );
+ return properties;
+}
+
+/** Register a service with the repository.
+ *
+ * Typically, this is invoked by a module within its mlt_register().
+ *
+ * \public \memberof mlt_repository_s
+ * \param this a repository
+ * \param service_type a service class
+ * \param service the name of a service
+ * \param symbol a pointer to a function to create the service
+ */
+
+void mlt_repository_register( mlt_repository this, mlt_service_type service_type, const char *service, mlt_register_callback symbol )
+{
+ // Add the entry point to the corresponding service list
+ switch ( service_type )
+ {
+ case consumer_type:
+ mlt_properties_set_data( this->consumers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ case filter_type:
+ mlt_properties_set_data( this->filters, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ case producer_type:
+ mlt_properties_set_data( this->producers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ case transition_type:
+ mlt_properties_set_data( this->transitions, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ default:
+ break;
+ }
+}
+
+/** Get the repository properties for particular service class.
+ *
+ * \private \memberof mlt_repository_s
+ * \param this a repository
+ * \param type a service class
+ * \param service the name of a service
+ * \return a properties list or NULL if error
+ */
+
+static mlt_properties get_service_properties( mlt_repository this, mlt_service_type type, const char *service )
+{
+ mlt_properties service_properties = NULL;
+
+ // Get the entry point from the corresponding service list
+ switch ( type )
+ {
+ case consumer_type:
+ service_properties = mlt_properties_get_data( this->consumers, service, NULL );
+ break;
+ case filter_type:
+ service_properties = mlt_properties_get_data( this->filters, service, NULL );
+ break;
+ case producer_type:
+ service_properties = mlt_properties_get_data( this->producers, service, NULL );
+ break;
+ case transition_type:
+ service_properties = mlt_properties_get_data( this->transitions, service, NULL );
+ break;
+ default:
+ break;
+ }
+ return service_properties;
+}
+
+/** Construct a new instance of a service.
+ *
+ * \public \memberof mlt_repository_s
+ * \param this a repository
+ * \param profile a \p mlt_profile to give the service
+ * \param type a service class
+ * \param service the name of the service
+ * \param input an optional argument to the service constructor
+ */
+
+void *mlt_repository_create( mlt_repository this, mlt_profile profile, mlt_service_type type, const char *service, const void *input )
+{
+ mlt_properties properties = get_service_properties( this, type, service );
+ if ( properties != NULL )
+ {
+ mlt_register_callback symbol_ptr = mlt_properties_get_data( properties, "symbol", NULL );
+
+ // Construct the service
+ return ( symbol_ptr != NULL ) ? symbol_ptr( profile, type, service, input ) : NULL;
+ }
+ return NULL;
+}
+
+/** Destroy a repository and free its resources.
+ *
+ * \public \memberof mlt_repository_s
+ * \param this a repository
+ */
+
+void mlt_repository_close( mlt_repository this )
+{
+ mlt_properties_close( this->consumers );
+ mlt_properties_close( this->filters );
+ mlt_properties_close( this->producers );
+ mlt_properties_close( this->transitions );
+ mlt_properties_close( &this->parent );
+ free( this );
+}
+
+/** Get the list of registered consumers.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list containing all of the consumers
+ */
+
+mlt_properties mlt_repository_consumers( mlt_repository self )
+{
+ return self->consumers;
+}
+
+/** Get the list of registered filters.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list of all of the filters
+ */
+
+mlt_properties mlt_repository_filters( mlt_repository self )
+{
+ return self->filters;
+}
+
+/** Get the list of registered producers.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list of all of the producers
+ */
+
+mlt_properties mlt_repository_producers( mlt_repository self )
+{
+ return self->producers;
+}
+
+/** Get the list of registered transitions.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list of all of the transitions
+ */
+
+mlt_properties mlt_repository_transitions( mlt_repository self )
+{
+ return self->transitions;
+}
+
+/** Register the metadata for a service.
+ *
+ * IMPORTANT: mlt_repository will take responsibility for deallocating the metadata properties
+ * that you supply!
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \param type a service class
+ * \param service the name of a service
+ * \param callback the pointer to a function that can supply metadata
+ * \param callback_data an opaque user data pointer to be supplied on the callback
+ */
+
+void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data )
+{
+ mlt_properties service_properties = get_service_properties( self, type, service );
+ mlt_properties_set_data( service_properties, "metadata_cb", callback, 0, NULL, NULL );
+ mlt_properties_set_data( service_properties, "metadata_cb_data", callback_data, 0, NULL, NULL );
+}
+
+/** Get the metadata about a service.
+ *
+ * Returns NULL if service or its metadata are unavailable.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \param type a service class
+ * \param service the name of a service
+ * \return the service metadata as a structured properties list
+ */
+
+mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service )
+{
+ mlt_properties metadata = NULL;
+ mlt_properties properties = get_service_properties( self, type, service );
+
+ // If this is a valid service
+ if ( properties )
+ {
+ // Lookup cached metadata
+ metadata = mlt_properties_get_data( properties, "metadata", NULL );
+ if ( ! metadata )
+ {
+ // Not cached, so get the registered metadata callback function
+ mlt_metadata_callback callback = mlt_properties_get_data( properties, "metadata_cb", NULL );
+
+ // If a metadata callback function is registered
+ if ( callback )
+ {
+ // Fetch the callback data arg
+ void *data = mlt_properties_get_data( properties, "metadata_cb_data", NULL );
+
+ // Fetch the metadata through the callback
+ metadata = callback( type, service, data );
+
+ // Cache the metadata
+ if ( metadata )
+ // Include dellocation and serialisation
+ mlt_properties_set_data( properties, "metadata", metadata, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml );
+ }
+ }
+ }
+ return metadata;
+}
+
+/** Try to determine the locale from some commonly used environment variables.
+ *
+ * \private \memberof mlt_repository_s
+ * \return a string containing the locale id or NULL if unknown
+ */
+
+static char *getenv_locale()
+{
+ char *s = getenv( "LANGUAGE" );
+ if ( s && s[0] )
+ return s;
+ s = getenv( "LC_ALL" );
+ if ( s && s[0] )
+ return s;
+ s = getenv( "LC_MESSAGES" );
+ if ( s && s[0] )
+ return s;
+ s = getenv( "LANG" );
+ if ( s && s[0] )
+ return s;
+ return NULL;
+}
+
+/** Return a list of user-preferred language codes taken from environment variables.
+ *
+ * A module should use this to locate a localized YAML Tiny file from which to build
+ * its metadata strucutured properties.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list that is a list (not a map) of locales, defaults to "en" if not
+ * overridden by environment variables, in order: LANGUAGE, LC_ALL, LC_MESSAGES, LANG
+ */
+
+mlt_properties mlt_repository_languages( mlt_repository self )
+{
+ mlt_properties languages = mlt_properties_get_data( &self->parent, "languages", NULL );
+ if ( languages )
+ return languages;
+
+ languages = mlt_properties_new();
+ char *locale = getenv_locale();
+ if ( locale )
+ {
+ locale = strdup( locale );
+ mlt_tokeniser tokeniser = mlt_tokeniser_init();
+ int count = mlt_tokeniser_parse_new( tokeniser, locale, ":" );
+ if ( count )
+ {
+ int i;
+ for ( i = 0; i < count; i++ )
+ {
+ char *locale = mlt_tokeniser_get_string( tokeniser, i );
+ if ( strcmp( locale, "C" ) == 0 || strcmp( locale, "POSIX" ) == 0 )
+ locale = "en";
+ else if ( strlen( locale ) > 2 )
+ locale[2] = 0;
+ char string[21];
+ snprintf( string, sizeof(string), "%d", i );
+ mlt_properties_set( languages, string, locale );
+ }
+ }
+ else
+ {
+ mlt_properties_set( languages, "0", "en" );
+ }
+ free( locale );
+ mlt_tokeniser_close( tokeniser );
+ }
+ else
+ {
+ mlt_properties_set( languages, "0", "en" );
+ }
+ mlt_properties_set_data( &self->parent, "languages", languages, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ return languages;
+}
--- /dev/null
+/**
+ * \file mlt_repository.h
+ * \brief provides a map between service and shared objects
+ * \see mlt_repository_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_REPOSITORY_H_
+#define _MLT_REPOSITORY_H_
+
+#include "mlt_types.h"
+#include "mlt_profile.h"
+
+/** This callback is the main entry point into a module, which must be exported
+ * with the symbol "mlt_register".
+ *
+ * Inside the callback, the module registers the additional callbacks below.
+ */
+
+typedef void ( *mlt_repository_callback )( mlt_repository );
+
+/** The callback function that modules implement to construct a service.
+ */
+
+typedef void *( *mlt_register_callback )( mlt_profile, mlt_service_type, const char * /* service name */, const void * /* arg */ );
+
+/** The callback function that modules implement to supply metadata as a properties list.
+ */
+
+typedef mlt_properties ( *mlt_metadata_callback )( mlt_service_type, const char * /* service name */, void * /* callback_data */ );
+
+/** A convenience macro to create an entry point for service registration. */
+#define MLT_REPOSITORY void mlt_register( mlt_repository repository )
+
+/** A convenience macro to a register service in a more declarative manner. */
+#define MLT_REGISTER( type, service, symbol ) ( mlt_repository_register( repository, (type), (service), ( mlt_register_callback )(symbol) ) )
+
+/** A convenience macro to a register metadata in a more declarative manner. */
+#define MLT_REGISTER_METADATA( type, service, callback, data ) ( mlt_repository_register_metadata( repository, (type), (service), ( mlt_metadata_callback )(callback), (data) ) )
+
+extern mlt_repository mlt_repository_init( const char *directory );
+extern void mlt_repository_register( mlt_repository self, mlt_service_type service_type, const char *service, mlt_register_callback );
+extern void *mlt_repository_create( mlt_repository self, mlt_profile profile, mlt_service_type type, const char *service, const void *arg );
+extern void mlt_repository_close( mlt_repository self );
+extern mlt_properties mlt_repository_consumers( mlt_repository self );
+extern mlt_properties mlt_repository_filters( mlt_repository self );
+extern mlt_properties mlt_repository_producers( mlt_repository self );
+extern mlt_properties mlt_repository_transitions( mlt_repository self );
+extern void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback, void *callback_data );
+extern mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service );
+extern mlt_properties mlt_repository_languages( mlt_repository self );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_service.c
+ * \brief interface definition for all service classes
+ * \see mlt_service_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_service.h"
+#include "mlt_filter.h"
+#include "mlt_frame.h"
+#include "mlt_cache.h"
+#include "mlt_factory.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+
+/* IMPORTANT NOTES
+
+ The base service implements a null frame producing service - as such,
+ it is functional without extension and will produce test cards frames
+ and PAL sized audio frames.
+
+ PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
+ CONTROL THIS IN EXTENDING CLASSES.
+*/
+
+/** \brief private service definition */
+
+typedef struct
+{
+ int size;
+ int count;
+ mlt_service *in;
+ mlt_service out;
+ int filter_count;
+ int filter_size;
+ mlt_filter *filters;
+ pthread_mutex_t mutex;
+}
+mlt_service_base;
+
+/* Private methods
+ */
+
+static void mlt_service_disconnect( mlt_service this );
+static void mlt_service_connect( mlt_service this, mlt_service that );
+static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service this, void **args );
+static void purge_cache( mlt_service self );
+
+/** Initialize a service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service structure to initialize
+ * \param child pointer to the child object for the subclass
+ * \return true if there was an error
+ */
+
+int mlt_service_init( mlt_service this, void *child )
+{
+ int error = 0;
+
+ // Initialise everything to NULL
+ memset( this, 0, sizeof( struct mlt_service_s ) );
+
+ // Assign the child
+ this->child = child;
+
+ // Generate local space
+ this->local = calloc( sizeof( mlt_service_base ), 1 );
+
+ // Associate the methods
+ this->get_frame = service_get_frame;
+
+ // Initialise the properties
+ error = mlt_properties_init( &this->parent, this );
+ if ( error == 0 )
+ {
+ this->parent.close = ( mlt_destructor )mlt_service_close;
+ this->parent.close_object = this;
+
+ mlt_events_init( &this->parent );
+ mlt_events_register( &this->parent, "service-changed", NULL );
+ mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
+ pthread_mutex_init( &( ( mlt_service_base * )this->local )->mutex, NULL );
+ }
+
+ return error;
+}
+
+/** The transmitter for property changes.
+ *
+ * Invokes the listener.
+ *
+ * \private \memberof mlt_service_s
+ * \param listener a function pointer that will be invoked
+ * \param owner a properties list that will be passed to \p listener
+ * \param this a service that will be passed to \p listener
+ * \param args an array of pointers - the first entry is passed as a string to \p listener
+ */
+
+static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( char * )args[ 0 ] );
+}
+
+/** Acquire a mutual exclusion lock on this service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service to lock
+ */
+
+void mlt_service_lock( mlt_service this )
+{
+ if ( this != NULL )
+ pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
+}
+
+/** Release a mutual exclusion lock on this service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service to unlock
+ */
+
+void mlt_service_unlock( mlt_service this )
+{
+ if ( this != NULL )
+ pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
+}
+
+/** Identify the subclass of the service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the subclass
+ */
+
+mlt_service_type mlt_service_identify( mlt_service this )
+{
+ mlt_service_type type = invalid_type;
+ if ( this != NULL )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+ char *mlt_type = mlt_properties_get( properties, "mlt_type" );
+ char *resource = mlt_properties_get( properties, "resource" );
+ if ( mlt_type == NULL )
+ type = unknown_type;
+ else if (resource != NULL && !strcmp( resource, "<playlist>" ) )
+ type = playlist_type;
+ else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
+ type = tractor_type;
+ else if (resource != NULL && !strcmp( resource, "<multitrack>" ) )
+ type = multitrack_type;
+ else if ( !strcmp( mlt_type, "producer" ) )
+ type = producer_type;
+ else if ( !strcmp( mlt_type, "filter" ) )
+ type = filter_type;
+ else if ( !strcmp( mlt_type, "transition" ) )
+ type = transition_type;
+ else if ( !strcmp( mlt_type, "consumer" ) )
+ type = consumer_type;
+ else
+ type = unknown_type;
+ }
+ return type;
+}
+
+/** Connect a producer to the service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param producer a producer
+ * \param index which of potentially multiple producers to this service (0 based)
+ * \return > 0 warning, == 0 success, < 0 serious error,
+ * 1 = this service does not accept input,
+ * 2 = the producer is invalid,
+ * 3 = the producer is already registered with this consumer
+ */
+
+int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
+{
+ int i = 0;
+
+ // Get the service base
+ mlt_service_base *base = this->local;
+
+ // Special case 'track' index - only works for last filter(s) in a particular chain
+ // but allows a filter to apply to the output frame regardless of which track it comes from
+ if ( index == -1 )
+ index = 0;
+
+ // Check if the producer is already registered with this service
+ for ( i = 0; i < base->count; i ++ )
+ if ( base->in[ i ] == producer )
+ return 3;
+
+ // Allocate space
+ if ( index >= base->size )
+ {
+ int new_size = base->size + index + 10;
+ base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
+ if ( base->in != NULL )
+ {
+ for ( i = base->size; i < new_size; i ++ )
+ base->in[ i ] = NULL;
+ base->size = new_size;
+ }
+ }
+
+ // If we have space, assign the input
+ if ( base->in != NULL && index >= 0 && index < base->size )
+ {
+ // Get the current service
+ mlt_service current = base->in[ index ];
+
+ // Increment the reference count on this producer
+ if ( producer != NULL )
+ mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
+
+ // Now we disconnect the producer service from its consumer
+ mlt_service_disconnect( producer );
+
+ // Add the service to index specified
+ base->in[ index ] = producer;
+
+ // Determine the number of active tracks
+ if ( index >= base->count )
+ base->count = index + 1;
+
+ // Now we connect the producer to its connected consumer
+ mlt_service_connect( producer, this );
+
+ // Close the current service
+ mlt_service_close( current );
+
+ // Inform caller that all went well
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+/** Disconnect this service from its consumer.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ */
+
+static void mlt_service_disconnect( mlt_service this )
+{
+ if ( this != NULL )
+ {
+ // Get the service base
+ mlt_service_base *base = this->local;
+
+ // Disconnect
+ base->out = NULL;
+ }
+}
+
+/** Obtain the consumer this service is connected to.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the consumer
+ */
+
+mlt_service mlt_service_consumer( mlt_service this )
+{
+ // Get the service base
+ mlt_service_base *base = this->local;
+
+ // Return the connected consumer
+ return base->out;
+}
+
+/** Obtain the producer this service is connected to.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the last-most producer
+ */
+
+mlt_service mlt_service_producer( mlt_service this )
+{
+ // Get the service base
+ mlt_service_base *base = this->local;
+
+ // Return the connected producer
+ return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
+}
+
+/** Associate this service to a consumer.
+ *
+ * Overwrites connection to any existing consumer.
+ * \private \memberof mlt_service_s
+ * \param this a service
+ * \param that a consumer
+ */
+
+static void mlt_service_connect( mlt_service this, mlt_service that )
+{
+ if ( this != NULL )
+ {
+ // Get the service base
+ mlt_service_base *base = this->local;
+
+ // There's a bit more required here...
+ base->out = that;
+ }
+}
+
+/** Get the first connected producer.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the first producer
+ */
+
+mlt_service mlt_service_get_producer( mlt_service this )
+{
+ mlt_service producer = NULL;
+
+ // Get the service base
+ mlt_service_base *base = this->local;
+
+ if ( base->in != NULL )
+ producer = base->in[ 0 ];
+
+ return producer;
+}
+
+/** Default implementation of the get_frame virtual function.
+ *
+ * \private \memberof mlt_service_s
+ * \param this a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the producer
+ * \return false
+ */
+
+static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
+{
+ mlt_service_base *base = this->local;
+ if ( index < base->count )
+ {
+ mlt_service producer = base->in[ index ];
+ if ( producer != NULL )
+ return mlt_service_get_frame( producer, frame, index );
+ }
+ *frame = mlt_frame_init( this );
+ return 0;
+}
+
+/** Return the properties object.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the properties
+ */
+
+mlt_properties mlt_service_properties( mlt_service this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Recursively apply attached filters.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param frame a frame
+ * \param index used to track depth of recursion, top caller should supply 0
+ */
+
+void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
+{
+ int i;
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+ mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
+ mlt_service_base *base = this->local;
+ mlt_position position = mlt_frame_get_position( frame );
+ mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
+ mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
+
+ if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
+ {
+ // Process the frame with the attached filters
+ for ( i = 0; i < base->filter_count; i ++ )
+ {
+ if ( base->filters[ i ] != NULL )
+ {
+ mlt_position in = mlt_filter_get_in( base->filters[ i ] );
+ mlt_position out = mlt_filter_get_out( base->filters[ i ] );
+ int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
+ if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
+ {
+ mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
+ mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
+ mlt_filter_process( base->filters[ i ], frame );
+ mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
+ }
+ }
+ }
+ }
+}
+
+/** Obtain a frame.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the producer
+ * \return true if there was an error
+ */
+
+int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
+{
+ int result = 0;
+
+ // Lock the service
+ mlt_service_lock( this );
+
+ // Ensure that the frame is NULL
+ *frame = NULL;
+
+ // Only process if we have a valid service
+ if ( this != NULL && this->get_frame != NULL )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+ mlt_position in = mlt_properties_get_position( properties, "in" );
+ mlt_position out = mlt_properties_get_position( properties, "out" );
+
+ result = this->get_frame( this, frame, index );
+
+ if ( result == 0 )
+ {
+ mlt_properties_inc_ref( properties );
+ properties = MLT_FRAME_PROPERTIES( *frame );
+ if ( in >=0 && out > 0 )
+ {
+ mlt_properties_set_position( properties, "in", in );
+ mlt_properties_set_position( properties, "out", out );
+ }
+ mlt_service_apply_filters( this, *frame, 1 );
+ mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
+ }
+ }
+
+ // Make sure we return a frame
+ if ( *frame == NULL )
+ *frame = mlt_frame_init( this );
+
+ // Unlock the service
+ mlt_service_unlock( this );
+
+ return result;
+}
+
+/** The service-changed event handler.
+ *
+ * \private \memberof mlt_service_s
+ * \param owner ignored
+ * \param this the service on which the "service-changed" event is fired
+ */
+
+static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
+{
+ mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
+}
+
+/** Attach a filter.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param filter the filter to attach
+ * \return true if there was an error
+ */
+
+int mlt_service_attach( mlt_service this, mlt_filter filter )
+{
+ int error = this == NULL || filter == NULL;
+ if ( error == 0 )
+ {
+ int i = 0;
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+ mlt_service_base *base = this->local;
+
+ for ( i = 0; error == 0 && i < base->filter_count; i ++ )
+ if ( base->filters[ i ] == filter )
+ error = 1;
+
+ if ( error == 0 )
+ {
+ if ( base->filter_count == base->filter_size )
+ {
+ base->filter_size += 10;
+ base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
+ }
+
+ if ( base->filters != NULL )
+ {
+ mlt_properties props = MLT_FILTER_PROPERTIES( filter );
+ mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
+ base->filters[ base->filter_count ++ ] = filter;
+ mlt_events_fire( properties, "service-changed", NULL );
+ mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
+ mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
+ }
+ else
+ {
+ error = 2;
+ }
+ }
+ }
+ return error;
+}
+
+/** Detach a filter.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param filter the filter to detach
+ * \return true if there was an error
+ */
+
+int mlt_service_detach( mlt_service this, mlt_filter filter )
+{
+ int error = this == NULL || filter == NULL;
+ if ( error == 0 )
+ {
+ int i = 0;
+ mlt_service_base *base = this->local;
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+
+ for ( i = 0; i < base->filter_count; i ++ )
+ if ( base->filters[ i ] == filter )
+ break;
+
+ if ( i < base->filter_count )
+ {
+ base->filters[ i ] = NULL;
+ for ( i ++ ; i < base->filter_count; i ++ )
+ base->filters[ i - 1 ] = base->filters[ i ];
+ base->filter_count --;
+ mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
+ mlt_filter_close( filter );
+ mlt_events_fire( properties, "service-changed", NULL );
+ }
+ }
+ return error;
+}
+
+/** Retrieve a filter.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param index which one of potentially multiple filters
+ * \return the filter or null if there was an error
+ */
+
+mlt_filter mlt_service_filter( mlt_service this, int index )
+{
+ mlt_filter filter = NULL;
+ if ( this != NULL )
+ {
+ mlt_service_base *base = this->local;
+ if ( index >= 0 && index < base->filter_count )
+ filter = base->filters[ index ];
+ }
+ return filter;
+}
+
+/** Retrieve the profile.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the profile
+ */
+
+mlt_profile mlt_service_profile( mlt_service this )
+{
+ return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL );
+}
+
+/** Destroy a service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service to destroy
+ */
+
+void mlt_service_close( mlt_service this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
+ {
+ if ( this->close != NULL )
+ {
+ this->close( this->close_object );
+ }
+ else
+ {
+ mlt_service_base *base = this->local;
+ int i = 0;
+ int count = base->filter_count;
+ mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
+ while( count -- )
+ mlt_service_detach( this, base->filters[ 0 ] );
+ free( base->filters );
+ for ( i = 0; i < base->count; i ++ )
+ if ( base->in[ i ] != NULL )
+ mlt_service_close( base->in[ i ] );
+ this->parent.close = NULL;
+ free( base->in );
+ pthread_mutex_destroy( &base->mutex );
+ free( base );
+ purge_cache( this );
+ mlt_properties_close( &this->parent );
+ }
+ }
+ else
+ {
+ mlt_service_unlock( this );
+ }
+}
+
+/** Release a service's cache items.
+ *
+ * \private \memberof mlt_service_s
+ * \param self a service
+ */
+
+static void purge_cache( mlt_service self )
+{
+ mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
+
+ if ( caches )
+ {
+ int i = mlt_properties_count( caches );
+ while ( i-- )
+ {
+ mlt_cache_purge( mlt_properties_get_data_at( caches, i, NULL ), self );
+ mlt_properties_set_data( mlt_global_properties(), mlt_properties_get_name( caches, i ), NULL, 0, NULL, NULL );
+ }
+ }
+}
+
+/** Lookup the cache object for a service.
+ *
+ * \private \memberof mlt_service_s
+ * \param self a service
+ * \param name a name for the object
+ * \return a cache
+ */
+
+static mlt_cache get_cache( mlt_service self, const char *name )
+{
+ mlt_cache result = NULL;
+ mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
+
+ if ( !caches )
+ {
+ caches = mlt_properties_new();
+ mlt_properties_set_data( mlt_global_properties(), "caches", caches, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ }
+ if ( caches )
+ {
+ result = mlt_properties_get_data( caches, name, NULL );
+ if ( !result )
+ {
+ result = mlt_cache_init();
+ mlt_properties_set_data( caches, name, result, 0, ( mlt_destructor )mlt_cache_close, NULL );
+ }
+ }
+
+ return result;
+}
+
+/** Put an object into a service's cache.
+ *
+ * \public \memberof mlt_service_s
+ * \param self a service
+ * \param name a name for the object that is unique to the service class, but not to the instance
+ * \param data an opaque pointer to the object to put into the cache
+ * \param size the number of bytes pointed to by data
+ * \param destructor a function that releases the data
+ */
+
+void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor )
+{
+ mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data );
+ mlt_cache cache = get_cache( self, name );
+
+ if ( cache )
+ mlt_cache_put( cache, self, data, size, destructor );
+}
+
+/** Get an object from a service's cache.
+ *
+ * \public \memberof mlt_service_s
+ * \param self a service
+ * \param name a name for the object that is unique to the service class, but not to the instance
+ * \return a cache item or NULL if an object is not found
+ * \see mlt_cache_item_data
+ */
+
+mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name )
+{
+ mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self );
+ mlt_cache_item result = NULL;
+ mlt_cache cache = get_cache( self, name );
+
+ if ( cache )
+ result = mlt_cache_get( cache, self );
+
+ return result;
+}
--- /dev/null
+/**
+ * \file mlt_service.h
+ * \brief interface declaration for all service classes
+ * \see mlt_service_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_SERVICE_H_
+#define _MLT_SERVICE_H_
+
+#include "mlt_properties.h"
+#include "mlt_types.h"
+
+/** \brief Service abstract base class
+ *
+ * \extends mlt_properties
+ * The service is the base class for all of the interesting classes and
+ * plugins for MLT. A service can have multiple inputs connections to
+ * other services called its "producers" but only a single output to another
+ * service called its "consumer." A service that has both producer and
+ * consumer connections is called a filter. Any service can have zero or more
+ * filters "attached" to it. We call any collection of services and their
+ * connections a "service network," which is similar to what DirectShow calls
+ * a filter graph or what gstreamer calls an element pipeline.
+ *
+ * \event \em service-changed
+ * \event \em property-changed
+ * \properties \em mlt_type identifies the subclass
+ * \properties \em _mlt_service_hidden a flag that indicates whether to hide the mlt_service
+ * \properties \em mlt_service is the name of the implementation of the service
+ * \properties \em resource is either the stream identifier or grandchild-class
+ * \properties \em in when to start, what is started is service-specific
+ * \properties \em out when to stop
+ * \properties \em _filter_private Set this on a service to ensure that attached filters are handled privately.
+ * See modules/core/filter_region.c and modules/core/filter_watermark.c for examples.
+ * \properties \em disable Set this on a filter to disable it while keeping it in the object model.
+ * \properties \em _profile stores the mlt_profile for a service
+ * \properties \em _unique_id is a unique identifier
+ */
+
+struct mlt_service_s
+{
+ struct mlt_properties_s parent; /**< \private */
+
+ /** Get a frame of data (virtual function).
+ *
+ * \param mlt_producer a producer
+ * \param mlt_frame_ptr a frame pointer by reference
+ * \param int an index
+ * \return true if there was an error
+ */
+ int ( *get_frame )( mlt_service self, mlt_frame_ptr frame, int index );
+
+ /** the destructor virtual function */
+ mlt_destructor close;
+ void *close_object; /**< the object supplied to the close virtual function */
+
+ void *local; /**< \private instance object */
+ void *child; /**< \private the object of a subclass */
+};
+
+#define MLT_SERVICE_PROPERTIES( service ) ( &( service )->parent )
+
+extern int mlt_service_init( mlt_service self, void *child );
+extern void mlt_service_lock( mlt_service self );
+extern void mlt_service_unlock( mlt_service self );
+extern mlt_service_type mlt_service_identify( mlt_service self );
+extern int mlt_service_connect_producer( mlt_service self, mlt_service producer, int index );
+extern mlt_service mlt_service_get_producer( mlt_service self );
+extern int mlt_service_get_frame( mlt_service self, mlt_frame_ptr frame, int index );
+extern mlt_properties mlt_service_properties( mlt_service self );
+extern mlt_service mlt_service_consumer( mlt_service self );
+extern mlt_service mlt_service_producer( mlt_service self );
+extern int mlt_service_attach( mlt_service self, mlt_filter filter );
+extern int mlt_service_detach( mlt_service self, mlt_filter filter );
+extern void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index );
+extern mlt_filter mlt_service_filter( mlt_service self, int index );
+extern mlt_profile mlt_service_profile( mlt_service self );
+extern void mlt_service_close( mlt_service self );
+extern void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor );
+extern mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name );
+
+#endif
+
--- /dev/null
+/**
+ * \file mlt_tokeniser.c
+ * \brief string tokeniser
+ * \see mlt_tokeniser_s
+ *
+ * Copyright (C) 2002-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "mlt_tokeniser.h"
+
+/** Initialise a tokeniser.
+*/
+
+mlt_tokeniser mlt_tokeniser_init( )
+{
+ return calloc( 1, sizeof( mlt_tokeniser_t ) );
+}
+
+/** Clear the tokeniser.
+*/
+
+static void mlt_tokeniser_clear( mlt_tokeniser tokeniser )
+{
+ int index = 0;
+ for ( index = 0; index < tokeniser->count; index ++ )
+ free( tokeniser->tokens[ index ] );
+ tokeniser->count = 0;
+ free( tokeniser->input );
+ tokeniser->input = NULL;
+}
+
+/** Append a string to the tokeniser.
+*/
+
+static int mlt_tokeniser_append( mlt_tokeniser tokeniser, char *token )
+{
+ int error = 0;
+
+ if ( tokeniser->count == tokeniser->size )
+ {
+ tokeniser->size += 20;
+ tokeniser->tokens = realloc( tokeniser->tokens, tokeniser->size * sizeof( char * ) );
+ }
+
+ if ( tokeniser->tokens != NULL )
+ {
+ tokeniser->tokens[ tokeniser->count ++ ] = strdup( token );
+ }
+ else
+ {
+ tokeniser->count = 0;
+ error = -1;
+ }
+ return error;
+}
+
+/** Parse a string by splitting on the delimiter provided.
+*/
+
+int mlt_tokeniser_parse_new( mlt_tokeniser tokeniser, char *string, const char *delimiter )
+{
+ int count = 0;
+ int length = strlen( string );
+ int delimiter_size = strlen( delimiter );
+ int index = 0;
+ char *token = strdup( string );
+
+ mlt_tokeniser_clear( tokeniser );
+ tokeniser->input = strdup( string );
+ strcpy( token, "" );
+
+ for ( index = 0; index < length; )
+ {
+ char *start = string + index;
+ char *end = strstr( start, delimiter );
+
+ if ( end == NULL )
+ {
+ strcat( token, start );
+ mlt_tokeniser_append( tokeniser, token );
+ index = length;
+ count ++;
+ }
+ else if ( start != end )
+ {
+ strncat( token, start, end - start );
+ index += end - start;
+ if ( strchr( token, '\"' ) == NULL || token[ strlen( token ) - 1 ] == '\"' )
+ {
+ mlt_tokeniser_append( tokeniser, token );
+ strcpy( token, "" );
+ count ++;
+ }
+ else while ( strncmp( string + index, delimiter, delimiter_size ) == 0 )
+ {
+ strncat( token, delimiter, delimiter_size );
+ index += delimiter_size;
+ }
+ }
+ else
+ {
+ index += strlen( delimiter );
+ }
+ }
+
+ /* Special case - malformed string condition */
+ if ( !strcmp( token, "" ) )
+ {
+ count = 0 - ( count - 1 );
+ mlt_tokeniser_append( tokeniser, token );
+ }
+
+ free( token );
+ return count;
+}
+
+/** Get the original input.
+*/
+
+char *mlt_tokeniser_get_input( mlt_tokeniser tokeniser )
+{
+ return tokeniser->input;
+}
+
+/** Get the number of tokens.
+*/
+
+int mlt_tokeniser_count( mlt_tokeniser tokeniser )
+{
+ return tokeniser->count;
+}
+
+/** Get a token as a string.
+*/
+
+char *mlt_tokeniser_get_string( mlt_tokeniser tokeniser, int index )
+{
+ if ( index < tokeniser->count )
+ return tokeniser->tokens[ index ];
+ else
+ return NULL;
+}
+
+/** Close the tokeniser.
+*/
+
+void mlt_tokeniser_close( mlt_tokeniser tokeniser )
+{
+ mlt_tokeniser_clear( tokeniser );
+ free( tokeniser->tokens );
+ free( tokeniser );
+}
--- /dev/null
+/**
+ * \file mlt_tokeniser.h
+ * \brief string tokeniser
+ * \see mlt_tokeniser_s
+ *
+ * Copyright (C) 2002-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_TOKENISER_H_
+#define _MLT_TOKENISER_H_
+
+/** \brief Tokeniser class
+ *
+ */
+
+typedef struct
+{
+ char *input;
+ char **tokens;
+ int count;
+ int size;
+}
+*mlt_tokeniser, mlt_tokeniser_t;
+
+/* Remote parser API.
+*/
+
+extern mlt_tokeniser mlt_tokeniser_init( );
+extern int mlt_tokeniser_parse_new( mlt_tokeniser self, char *text, const char *delimiter );
+extern char *mlt_tokeniser_get_input( mlt_tokeniser self );
+extern int mlt_tokeniser_count( mlt_tokeniser self );
+extern char *mlt_tokeniser_get_string( mlt_tokeniser self, int index );
+extern void mlt_tokeniser_close( mlt_tokeniser self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_tractor.c
+ * \brief tractor service class
+ * \see mlt_tractor_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_tractor.h"
+#include "mlt_frame.h"
+#include "mlt_multitrack.h"
+#include "mlt_field.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Forward references to static methods.
+*/
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track );
+static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor this );
+
+/** Construct a tractor without a field or multitrack.
+ *
+ * Sets the resource property to "<tractor>", the mlt_type to "mlt_producer",
+ * and mlt_service to "tractor".
+ *
+ * \public \memberof mlt_tractor_s
+ * \return the new tractor
+ */
+
+mlt_tractor mlt_tractor_init( )
+{
+ mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 );
+ if ( this != NULL )
+ {
+ mlt_producer producer = &this->parent;
+ if ( mlt_producer_init( producer, this ) == 0 )
+ {
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ mlt_properties_set( properties, "resource", "<tractor>" );
+ mlt_properties_set( properties, "mlt_type", "mlt_producer" );
+ mlt_properties_set( properties, "mlt_service", "tractor" );
+ mlt_properties_set_int( properties, "in", 0 );
+ mlt_properties_set_int( properties, "out", -1 );
+ mlt_properties_set_int( properties, "length", 0 );
+
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )mlt_tractor_close;
+ producer->close_object = this;
+ }
+ else
+ {
+ free( this );
+ this = NULL;
+ }
+ }
+ return this;
+}
+
+/** Construct a tractor as well as a field and multitrack.
+ *
+ * Sets the resource property to "<tractor>", the mlt_type to "mlt_producer",
+ * and mlt_service to "tractor".
+ *
+ * \public \memberof mlt_tractor_s
+ * \return the new tractor
+ */
+
+mlt_tractor mlt_tractor_new( )
+{
+ mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 );
+ if ( this != NULL )
+ {
+ mlt_producer producer = &this->parent;
+ if ( mlt_producer_init( producer, this ) == 0 )
+ {
+ mlt_multitrack multitrack = mlt_multitrack_init( );
+ mlt_field field = mlt_field_new( multitrack, this );
+ mlt_properties props = MLT_PRODUCER_PROPERTIES( producer );
+
+ mlt_properties_set( props, "resource", "<tractor>" );
+ mlt_properties_set( props, "mlt_type", "mlt_producer" );
+ mlt_properties_set( props, "mlt_service", "tractor" );
+ mlt_properties_set_position( props, "in", 0 );
+ mlt_properties_set_position( props, "out", 0 );
+ mlt_properties_set_position( props, "length", 0 );
+ mlt_properties_set_data( props, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
+ mlt_properties_set_data( props, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );
+
+ mlt_events_listen( MLT_MULTITRACK_PROPERTIES( multitrack ), this, "producer-changed", ( mlt_listener )mlt_tractor_listener );
+
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )mlt_tractor_close;
+ producer->close_object = this;
+ }
+ else
+ {
+ free( this );
+ this = NULL;
+ }
+ }
+ return this;
+}
+
+/** Get the service object associated to the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return the parent service object
+ * \see MLT_TRACTOR_SERVICE
+ */
+
+mlt_service mlt_tractor_service( mlt_tractor this )
+{
+ return MLT_PRODUCER_SERVICE( &this->parent );
+}
+
+/** Get the producer object associated to the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return the parent producer object
+ * \see MLT_TRACTOR_PRODUCER
+ */
+
+mlt_producer mlt_tractor_producer( mlt_tractor this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the properties object associated to the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return the tractor's property list
+ * \see MLT_TRACTOR_PROPERTIES
+ */
+
+mlt_properties mlt_tractor_properties( mlt_tractor this )
+{
+ return MLT_PRODUCER_PROPERTIES( &this->parent );
+}
+
+/** Get the field this tractor is harvesting.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return a field or NULL if there is no field for this tractor
+ */
+
+mlt_field mlt_tractor_field( mlt_tractor this )
+{
+ return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( this ), "field", NULL );
+}
+
+/** Get the multitrack this tractor is pulling.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return a multitrack or NULL if there is none
+ */
+
+mlt_multitrack mlt_tractor_multitrack( mlt_tractor this )
+{
+ return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( this ), "multitrack", NULL );
+}
+
+/** Ensure the tractors in/out points match the multitrack.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ */
+
+void mlt_tractor_refresh( mlt_tractor this )
+{
+ mlt_multitrack multitrack = mlt_tractor_multitrack( this );
+ mlt_properties properties = MLT_MULTITRACK_PROPERTIES( multitrack );
+ mlt_properties self = MLT_TRACTOR_PROPERTIES( this );
+ mlt_events_block( properties, self );
+ mlt_events_block( self, self );
+ mlt_multitrack_refresh( multitrack );
+ mlt_properties_set_position( self, "in", 0 );
+ mlt_properties_set_position( self, "out", mlt_properties_get_position( properties, "out" ) );
+ mlt_events_unblock( self, self );
+ mlt_events_unblock( properties, self );
+ mlt_properties_set_position( self, "length", mlt_properties_get_position( properties, "length" ) );
+}
+
+static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor this )
+{
+ mlt_tractor_refresh( this );
+}
+
+/** Connect the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \param producer a producer
+ * \return true on error
+ */
+
+int mlt_tractor_connect( mlt_tractor this, mlt_service producer )
+{
+ int ret = mlt_service_connect_producer( MLT_TRACTOR_SERVICE( this ), producer, 0 );
+
+ // This is the producer we're going to connect to
+ if ( ret == 0 )
+ this->producer = producer;
+
+ return ret;
+}
+
+/** Set the producer for a specific track.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \param producer a producer
+ * \param index the 0-based track index
+ * \return true on error
+ */
+
+int mlt_tractor_set_track( mlt_tractor this, mlt_producer producer, int index )
+{
+ return mlt_multitrack_connect( mlt_tractor_multitrack( this ), producer, index );
+}
+
+/** Get the producer for a specific track.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \param index the 0-based track index
+ * \return the producer for track \p index
+ */
+
+mlt_producer mlt_tractor_get_track( mlt_tractor this, int index )
+{
+ return mlt_multitrack_track( mlt_tractor_multitrack( this ), index );
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ uint8_t *data = NULL;
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+ mlt_frame frame = mlt_frame_pop_service( this );
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+ mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale.interp" ) );
+ mlt_properties_set_int( frame_properties, "resize_alpha", mlt_properties_get_int( properties, "resize_alpha" ) );
+ mlt_properties_set_int( frame_properties, "distort", mlt_properties_get_int( properties, "distort" ) );
+ mlt_properties_set_double( frame_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
+ mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "consumer_deinterlace" ) );
+ mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
+ mlt_properties_set_int( frame_properties, "normalised_width", mlt_properties_get_int( properties, "normalised_width" ) );
+ mlt_properties_set_int( frame_properties, "normalised_height", mlt_properties_get_int( properties, "normalised_height" ) );
+ mlt_frame_get_image( frame, buffer, format, width, height, writable );
+ mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, NULL, NULL );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+ mlt_properties_set_int( properties, "format", *format );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_frame_get_aspect_ratio( frame ) );
+ mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( frame_properties, "progressive" ) );
+ mlt_properties_set_int( properties, "distort", mlt_properties_get_int( frame_properties, "distort" ) );
+ data = mlt_frame_get_alpha_mask( frame );
+ mlt_properties_set_data( properties, "alpha", data, 0, NULL, NULL );
+ return 0;
+}
+
+static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+ mlt_frame frame = mlt_frame_pop_audio( this );
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+ mlt_properties_set_data( properties, "audio", *buffer, 0, NULL, NULL );
+ mlt_properties_set_int( properties, "frequency", *frequency );
+ mlt_properties_set_int( properties, "channels", *channels );
+ return 0;
+}
+
+static void destroy_data_queue( void *arg )
+{
+ if ( arg != NULL )
+ {
+ // Assign the correct type
+ mlt_deque queue = arg;
+
+ // Iterate through each item and destroy them
+ while ( mlt_deque_peek_front( queue ) != NULL )
+ mlt_properties_close( mlt_deque_pop_back( queue ) );
+
+ // Close the deque
+ mlt_deque_close( queue );
+ }
+}
+
+/** Get the next frame.
+ *
+ * \private \memberof mlt_tractor_s
+ * \param parent the producer interface to the tractor
+ * \param[out] frame a frame by reference
+ * \param track the 0-based track index
+ * \return true on error
+ */
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track )
+{
+ mlt_tractor this = parent->child;
+
+ // We only respond to the first track requests
+ if ( track == 0 && this->producer != NULL )
+ {
+ int i = 0;
+ int done = 0;
+ mlt_frame temp = NULL;
+ int count = 0;
+ int image_count = 0;
+
+ // Get the properties of the parent producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent );
+
+ // Try to obtain the multitrack associated to the tractor
+ mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
+
+ // Or a specific producer
+ mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+ // The output frame will hold the 'global' data feeds (ie: those which are targetted for the final frame)
+ mlt_deque data_queue = mlt_deque_init( );
+
+ // Determine whether this tractor feeds to the consumer or stops here
+ int global_feed = mlt_properties_get_int( properties, "global_feed" );
+
+ // If we don't have one, we're in trouble...
+ if ( multitrack != NULL )
+ {
+ // Used to garbage collect all frames
+ char label[ 30 ];
+
+ // Get the id of the tractor
+ char *id = mlt_properties_get( properties, "_unique_id" );
+
+ // Will be used to store the frame properties object
+ mlt_properties frame_properties = NULL;
+
+ // We'll store audio and video frames to use here
+ mlt_frame audio = NULL;
+ mlt_frame video = NULL;
+ mlt_frame first_video = NULL;
+
+ // Temporary properties
+ mlt_properties temp_properties = NULL;
+
+ // Get the multitrack's producer
+ mlt_producer target = MLT_MULTITRACK_PRODUCER( multitrack );
+ mlt_producer_seek( target, mlt_producer_frame( parent ) );
+ mlt_producer_set_speed( target, mlt_producer_get_speed( parent ) );
+
+ // We will create one frame and attach everything to it
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+
+ // Get the properties of the frame
+ frame_properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Loop through each of the tracks we're harvesting
+ for ( i = 0; !done; i ++ )
+ {
+ // Get a frame from the producer
+ mlt_service_get_frame( this->producer, &temp, i );
+
+ // Get the temporary properties
+ temp_properties = MLT_FRAME_PROPERTIES( temp );
+
+ // Check for last track
+ done = mlt_properties_get_int( temp_properties, "last_track" );
+
+ // Handle fx only tracks
+ if ( mlt_properties_get_int( temp_properties, "fx_cut" ) )
+ {
+ int hide = ( video == NULL ? 1 : 0 ) | ( audio == NULL ? 2 : 0 );
+ mlt_properties_set_int( temp_properties, "hide", hide );
+ }
+
+ // We store all frames with a destructor on the output frame
+ sprintf( label, "_%s_%d", id, count ++ );
+ mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL );
+
+ // We want to append all 'final' feeds to the global queue
+ if ( !done && mlt_properties_get_data( temp_properties, "data_queue", NULL ) != NULL )
+ {
+ // Move the contents of this queue on to the output frames data queue
+ mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "data_queue", NULL );
+ mlt_deque temp = mlt_deque_init( );
+ while ( global_feed && mlt_deque_count( sub_queue ) )
+ {
+ mlt_properties p = mlt_deque_pop_back( sub_queue );
+ if ( mlt_properties_get_int( p, "final" ) )
+ mlt_deque_push_back( data_queue, p );
+ else
+ mlt_deque_push_back( temp, p );
+ }
+ while( mlt_deque_count( temp ) )
+ mlt_deque_push_front( sub_queue, mlt_deque_pop_back( temp ) );
+ mlt_deque_close( temp );
+ }
+
+ // Now do the same with the global queue but without the conditional behaviour
+ if ( mlt_properties_get_data( temp_properties, "global_queue", NULL ) != NULL )
+ {
+ mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "global_queue", NULL );
+ while ( mlt_deque_count( sub_queue ) )
+ {
+ mlt_properties p = mlt_deque_pop_back( sub_queue );
+ mlt_deque_push_back( data_queue, p );
+ }
+ }
+
+ // Pick up first video and audio frames
+ if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 2 ) )
+ {
+ // Order of frame creation is starting to get problematic
+ if ( audio != NULL )
+ {
+ mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), producer_get_audio );
+ mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), audio );
+ }
+ audio = temp;
+ }
+ if ( !done && !mlt_frame_is_test_card( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 1 ) )
+ {
+ if ( video != NULL )
+ {
+ mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), producer_get_image );
+ mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), video );
+ }
+ video = temp;
+ if ( first_video == NULL )
+ first_video = temp;
+
+ // Ensure that all frames know the aspect ratio of the background
+ mlt_properties_set_double( temp_properties, "output_ratio",
+ mlt_properties_get_double( MLT_FRAME_PROPERTIES( first_video ), "aspect_ratio" ) );
+
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( temp ), "image_count", ++ image_count );
+ image_count = 1;
+ }
+ }
+
+ // Now stack callbacks
+ if ( audio != NULL )
+ {
+ mlt_frame_push_audio( *frame, audio );
+ mlt_frame_push_audio( *frame, producer_get_audio );
+ }
+
+ if ( video != NULL )
+ {
+ mlt_properties video_properties = MLT_FRAME_PROPERTIES( first_video );
+ mlt_frame_push_service( *frame, video );
+ mlt_frame_push_service( *frame, producer_get_image );
+ if ( global_feed )
+ mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, NULL, NULL );
+ mlt_properties_set_data( video_properties, "global_queue", data_queue, 0, destroy_data_queue, NULL );
+ mlt_properties_set_int( frame_properties, "width", mlt_properties_get_int( video_properties, "width" ) );
+ mlt_properties_set_int( frame_properties, "height", mlt_properties_get_int( video_properties, "height" ) );
+ mlt_properties_set_int( frame_properties, "real_width", mlt_properties_get_int( video_properties, "real_width" ) );
+ mlt_properties_set_int( frame_properties, "real_height", mlt_properties_get_int( video_properties, "real_height" ) );
+ mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) );
+ mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) );
+ mlt_properties_set_int( frame_properties, "image_count", image_count );
+ }
+ else
+ {
+ destroy_data_queue( data_queue );
+ }
+
+ mlt_frame_set_position( *frame, mlt_producer_frame( parent ) );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", audio == NULL );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", video == NULL );
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "consumer_lock_service", this, 0, NULL, NULL );
+ }
+ else if ( producer != NULL )
+ {
+ mlt_producer_seek( producer, mlt_producer_frame( parent ) );
+ mlt_producer_set_speed( producer, mlt_producer_get_speed( parent ) );
+ mlt_service_get_frame( this->producer, frame, track );
+ }
+ else
+ {
+ mlt_log( MLT_PRODUCER_SERVICE( parent ), MLT_LOG_ERROR, "tractor without a multitrack!!\n" );
+ mlt_service_get_frame( this->producer, frame, track );
+ }
+
+ // Prepare the next frame
+ mlt_producer_prepare_next( parent );
+
+ // Indicate our found status
+ return 0;
+ }
+ else
+ {
+ // Generate a test card
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+ return 0;
+ }
+}
+
+/** Close the tractor and free its resources.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ */
+
+void mlt_tractor_close( mlt_tractor this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_TRACTOR_PROPERTIES( this ) ) <= 0 )
+ {
+ this->parent.close = NULL;
+ mlt_producer_close( &this->parent );
+ free( this );
+ }
+}
+
--- /dev/null
+/**
+ * \file mlt_tractor.h
+ * \brief tractor service class
+ * \see mlt_tractor_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_TRACTOR_H_
+#define _MLT_TRACTOR_H_
+
+#include "mlt_producer.h"
+
+/** \brief Tractor class
+ *
+ * The tractor is a convenience class that works with the field class
+ * to manage a multitrack, track filters, and transitions.
+ *
+ * \extends mlt_producer_s
+ * \properties \em multitrack holds a reference to the mulitrack object that a tractor manages
+ * \properties \em field holds a reference to the field object that a tractor manages
+ * \properties \em producer holds a reference to an encapsulated producer
+ * \properties \em global_feed a flag to indicate whether this tractor feeds to the consumer or stops here
+ * \properties \em global_queue is something for the data_feed functionality in the core module
+ * \properties \em data_queue is something for the data_feed functionality in the core module
+ */
+
+struct mlt_tractor_s
+{
+ struct mlt_producer_s parent;
+ mlt_service producer;
+};
+
+#define MLT_TRACTOR_PRODUCER( tractor ) ( &( tractor )->parent )
+#define MLT_TRACTOR_SERVICE( tractor ) MLT_PRODUCER_SERVICE( MLT_TRACTOR_PRODUCER( tractor ) )
+#define MLT_TRACTOR_PROPERTIES( tractor ) MLT_SERVICE_PROPERTIES( MLT_TRACTOR_SERVICE( tractor ) )
+
+extern mlt_tractor mlt_tractor_init( );
+extern mlt_tractor mlt_tractor_new( );
+extern mlt_service mlt_tractor_service( mlt_tractor self );
+extern mlt_producer mlt_tractor_producer( mlt_tractor self );
+extern mlt_properties mlt_tractor_properties( mlt_tractor self );
+extern mlt_field mlt_tractor_field( mlt_tractor self );
+extern mlt_multitrack mlt_tractor_multitrack( mlt_tractor self );
+extern int mlt_tractor_connect( mlt_tractor self, mlt_service service );
+extern void mlt_tractor_refresh( mlt_tractor self );
+extern int mlt_tractor_set_track( mlt_tractor self, mlt_producer producer, int index );
+extern mlt_producer mlt_tractor_get_track( mlt_tractor self, int index );
+extern void mlt_tractor_close( mlt_tractor self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_transition.c
+ * \brief abstraction for all transition services
+ * \see mlt_transition_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mlt_transition.h"
+#include "mlt_frame.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Forward references */
+
+static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** Initialize a new transition.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param child the object of a subclass
+ * \return true on error
+ */
+
+int mlt_transition_init( mlt_transition this, void *child )
+{
+ mlt_service service = &this->parent;
+ memset( this, 0, sizeof( struct mlt_transition_s ) );
+ this->child = child;
+ if ( mlt_service_init( service, this ) == 0 )
+ {
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ service->get_frame = transition_get_frame;
+ service->close = ( mlt_destructor )mlt_transition_close;
+ service->close_object = this;
+
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", 0 );
+ mlt_properties_set_int( properties, "a_track", 0 );
+ mlt_properties_set_int( properties, "b_track", 1 );
+
+ return 0;
+ }
+ return 1;
+}
+
+/** Create and initialize a new transition.
+ *
+ * \public \memberof mlt_transition_s
+ * \return a new transition
+ */
+
+mlt_transition mlt_transition_new( )
+{
+ mlt_transition this = calloc( 1, sizeof( struct mlt_transition_s ) );
+ if ( this != NULL )
+ mlt_transition_init( this, NULL );
+ return this;
+}
+
+/** Get the service class interface.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the service class
+ * \see MLT_TRANSITION_SERVICE
+ */
+
+mlt_service mlt_transition_service( mlt_transition this )
+{
+ return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the properties interface.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the transition's properties
+ * \see MLT_TRANSITION_PROPERTIES
+ */
+
+mlt_properties mlt_transition_properties( mlt_transition this )
+{
+ return MLT_TRANSITION_PROPERTIES( this );
+}
+
+/** Connect this transition with a producers a and b tracks.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param producer a producer
+ * \param a_track the track index of the first input
+ * \param b_track the track index of the second index
+ * \return true on error
+ */
+
+int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track )
+{
+ int ret = mlt_service_connect_producer( &this->parent, producer, a_track );
+ if ( ret == 0 )
+ {
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ this->producer = producer;
+ mlt_properties_set_int( properties, "a_track", a_track );
+ mlt_properties_set_int( properties, "b_track", b_track );
+ }
+ return ret;
+}
+
+/** Set the starting and ending time for when the transition is active.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param in the starting time
+ * \param out the ending time
+ */
+
+void mlt_transition_set_in_and_out( mlt_transition this, mlt_position in, mlt_position out )
+{
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ mlt_properties_set_position( properties, "in", in );
+ mlt_properties_set_position( properties, "out", out );
+}
+
+/** Get the index of the a track.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the 0-based index of the track of the first producer
+ */
+
+int mlt_transition_get_a_track( mlt_transition this )
+{
+ return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "a_track" );
+}
+
+/** Get the index of the b track.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the 0-based index of the track of the second producer
+ */
+
+int mlt_transition_get_b_track( mlt_transition this )
+{
+ return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "b_track" );
+}
+
+/** Get the in point.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the starting time
+ */
+
+mlt_position mlt_transition_get_in( mlt_transition this )
+{
+ return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( this ), "in" );
+}
+
+/** Get the out point.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the ending time
+ */
+
+mlt_position mlt_transition_get_out( mlt_transition this )
+{
+ return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( this ), "out" );
+}
+
+/** Process the frame.
+ *
+ * If we have no process method (unlikely), we simply return the a_frame unmolested.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param a_frame a frame from the first producer
+ * \param b_frame a frame from the second producer
+ * \return a frame
+ */
+
+mlt_frame mlt_transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+ if ( this->process == NULL )
+ return a_frame;
+ else
+ return this->process( this, a_frame, b_frame );
+}
+
+/** Get a frame from this transition.
+
+ The logic is complex here. A transition is typically applied to frames on the a and
+ b tracks specified in the connect method above and only if both contain valid info
+ for the transition type (this is either audio or image).
+
+ However, the fixed a_track may not always contain data of the correct type, eg:
+<pre>
+ +---------+ +-------+
+ |c1 | |c5 | <-- A(0,1) <-- B(0,2) <-- get frame
+ +---------+ +---------+-+-----+ | |
+ |c4 | <------+ |
+ +----------+-----------+-+---------+ |
+ |c2 |c3 | <-----------------+
+ +----------+-------------+
+</pre>
+ During the overlap of c1 and c2, there is nothing for the A transition to do, so this
+ results in a no operation, but B is triggered. During the overlap of c2 and c3, again,
+ the A transition is inactive and because the B transition is pointing at track 0,
+ it too would be inactive. This isn't an ideal situation - it's better if the B
+ transition simply treats the frames from c3 as though they're the a track.
+
+ For this to work, we cache all frames coming from all tracks between the a and b
+ tracks. Before we process, we determine that the b frame contains someting of the
+ right type and then we determine which frame to use as the a frame (selecting a
+ matching frame from a_track to b_track - 1). If both frames contain data of the
+ correct type, we process the transition.
+
+ This method is invoked for each track and we return the cached frames as needed.
+ We clear the cache only when the requested frame is flagged as a 'last_track' frame.
+
+ * \private \memberof mlt_transition_s
+ * \param service a service
+ * \param[out] frame a frame by reference
+ * \param index 0-based track index
+ * \return true on error
+ */
+
+static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
+{
+ int error = 0;
+ mlt_transition this = service->child;
+
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" );
+ int a_track = mlt_properties_get_int( properties, "a_track" );
+ int b_track = mlt_properties_get_int( properties, "b_track" );
+ mlt_position in = mlt_properties_get_position( properties, "in" );
+ mlt_position out = mlt_properties_get_position( properties, "out" );
+ int always_active = mlt_properties_get_int( properties, "always_active" );
+ int type = mlt_properties_get_int( properties, "_transition_type" );
+ int reverse_order = 0;
+
+ // Ensure that we have the correct order
+ if ( a_track > b_track )
+ {
+ reverse_order = 1;
+ a_track = b_track;
+ b_track = mlt_properties_get_int( properties, "a_track" );
+ }
+
+ // Only act on this operation once per multitrack iteration from the tractor
+ if ( !this->held )
+ {
+ int active = 0;
+ int i = 0;
+ int a_frame = a_track;
+ int b_frame = b_track;
+ mlt_position position;
+ int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio;
+
+ // Initialise temporary store
+ if ( this->frames == NULL )
+ this->frames = calloc( sizeof( mlt_frame ), b_track + 1 );
+
+ // Get all frames between a and b
+ for( i = a_track; i <= b_track; i ++ )
+ mlt_service_get_frame( this->producer, &this->frames[ i ], i );
+
+ // We're holding these frames until the last_track frame property is received
+ this->held = 1;
+
+ // When we need to locate the a_frame
+ switch( type )
+ {
+ case 1:
+ case 2:
+ // Some transitions (esp. audio) may accept blank frames
+ active = accepts_blanks;
+
+ // If we're not active then...
+ if ( !active )
+ {
+ // Hunt for the a_frame
+ while( a_frame <= b_frame && invalid( this->frames[ a_frame ] ) )
+ a_frame ++;
+
+ // Determine if we're active now
+ active = a_frame != b_frame && !invalid( this->frames[ b_frame ] );
+ }
+ break;
+
+ default:
+ mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" );
+ break;
+ }
+
+ // Now handle the non-always active case
+ if ( active && !always_active )
+ {
+ // For non-always-active transitions, we need the current position of the a frame
+ position = mlt_frame_get_position( this->frames[ a_frame ] );
+
+ // If a is in range, we're active
+ active = position >= in && position <= out;
+ }
+
+ // Finally, process the a and b frames
+ if ( active )
+ {
+ mlt_frame a_frame_ptr = this->frames[ !reverse_order ? a_frame : b_frame ];
+ mlt_frame b_frame_ptr = this->frames[ !reverse_order ? b_frame : a_frame ];
+ int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" );
+ int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" );
+ if ( !( a_hide & type ) && !( b_hide & type ) )
+ {
+ // Process the transition
+ *frame = mlt_transition_process( this, a_frame_ptr, b_frame_ptr );
+
+ // We need to ensure that the tractor doesn't consider this frame for output
+ if ( *frame == a_frame_ptr )
+ b_hide |= type;
+ else
+ a_hide |= type;
+
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide );
+ }
+ }
+ }
+
+ // Obtain the frame from the cache or the producer we're attached to
+ if ( index >= a_track && index <= b_track )
+ *frame = this->frames[ index ];
+ else
+ error = mlt_service_get_frame( this->producer, frame, index );
+
+ // Determine if that was the last track
+ this->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" );
+
+ return error;
+}
+
+/** Close and destroy the transition.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ */
+
+void mlt_transition_close( mlt_transition this )
+{
+ if ( this != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( this ) ) <= 0 )
+ {
+ this->parent.close = NULL;
+ if ( this->close != NULL )
+ {
+ this->close( this );
+ }
+ else
+ {
+ mlt_service_close( &this->parent );
+ free( this->frames );
+ free( this );
+ }
+ }
+}
--- /dev/null
+/**
+ * \file mlt_transition.h
+ * \brief abstraction for all transition services
+ * \see mlt_transition_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_TRANSITION_H_
+#define _MLT_TRANSITION_H_
+
+#include "mlt_service.h"
+
+/** \brief Transition abstract service class
+ *
+ * A transition may modify the output of a producer based on the output of a second producer.
+ *
+ * \extends mlt_service_s
+ * \properties \em a_track the track index (0-based) of a multitrack of the first producer
+ * \properties \em b_track the track index (0-based) of a multitrack of the second producer
+ * \properties \em accepts_blanks a flag to indicate if the transition should accept blank frames
+ * \properties \em always_active a flag to indicate that the in and out points do not apply
+ * \properties \em _transition_type 1 for video, 2 for audio
+ */
+
+struct mlt_transition_s
+{
+ /** We're implementing service here */
+ struct mlt_service_s parent;
+
+ /** public virtual */
+ void ( *close )( mlt_transition );
+
+ /** protected transition method */
+ mlt_frame ( *process )( mlt_transition, mlt_frame, mlt_frame );
+
+ /** Protected */
+ void *child;
+
+ /** track and in/out points */
+ mlt_service producer;
+
+ /** Private */
+ mlt_frame *frames;
+ int held;
+};
+
+#define MLT_TRANSITION_SERVICE( transition ) ( &( transition )->parent )
+#define MLT_TRANSITION_PROPERTIES( transition ) MLT_SERVICE_PROPERTIES( MLT_TRANSITION_SERVICE( transition ) )
+
+extern int mlt_transition_init( mlt_transition self, void *child );
+extern mlt_transition mlt_transition_new( );
+extern mlt_service mlt_transition_service( mlt_transition self );
+extern mlt_properties mlt_transition_properties( mlt_transition self );
+extern int mlt_transition_connect( mlt_transition self, mlt_service producer, int a_track, int b_track );
+extern void mlt_transition_set_in_and_out( mlt_transition self, mlt_position in, mlt_position out );
+extern int mlt_transition_get_a_track( mlt_transition self );
+extern int mlt_transition_get_b_track( mlt_transition self );
+extern mlt_position mlt_transition_get_in( mlt_transition self );
+extern mlt_position mlt_transition_get_out( mlt_transition self );
+extern mlt_frame mlt_transition_process( mlt_transition self, mlt_frame a_frame, mlt_frame b_frame );
+extern void mlt_transition_close( mlt_transition self );
+
+#endif
--- /dev/null
+/**
+ * \file mlt_types.h
+ * \brief Provides forward definitions of all public types
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MLT_TYPES_H_
+#define _MLT_TYPES_H_
+
+#ifndef GCC_VERSION
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif
+
+#include <stdint.h>
+
+#include "mlt_pool.h"
+
+/** The set of supported image formats */
+
+typedef enum
+{
+ mlt_image_none = 0,/**< image not available */
+ mlt_image_rgb24, /**< 8-bit RGB */
+ mlt_image_rgb24a, /**< 8-bit RGB with alpha channel */
+ mlt_image_yuv422, /**< 8-bit YUV 4:2:2 packed */
+ mlt_image_yuv420p, /**< 8-bit YUV 4:2:0 planar */
+ mlt_image_opengl /**< suitable for OpenGL texture */
+}
+mlt_image_format;
+
+/** The set of supported audio formats */
+
+typedef enum
+{
+ mlt_audio_none = 0,/**< audio not available */
+ mlt_audio_pcm /**< signed 16-bit interleaved PCM */
+}
+mlt_audio_format;
+
+/** The relative time qualifiers */
+
+typedef enum
+{
+ mlt_whence_relative_start, /**< relative to the beginning */
+ mlt_whence_relative_current,/**< relative to the current position */
+ mlt_whence_relative_end /**< relative to the end */
+}
+mlt_whence;
+
+/** The recognized subclasses of mlt_service */
+
+typedef enum
+{
+ invalid_type, /**< invalid service */
+ unknown_type, /**< unknown class */
+ producer_type, /**< Producer class */
+ tractor_type, /**< Tractor class */
+ playlist_type, /**< Playlist class */
+ multitrack_type, /**< Multitrack class */
+ filter_type, /**< Filter class */
+ transition_type, /**< Transition class */
+ consumer_type, /**< Consumer class */
+ field_type /**< Field class */
+}
+mlt_service_type;
+
+/* I don't want to break anyone's applications without warning. -Zach */
+#undef DOUBLE_MLT_POSITION
+#ifdef DOUBLE_MLT_POSITION
+typedef double mlt_position;
+#else
+typedef int32_t mlt_position;
+#endif
+
+typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr; /**< pointer to Frame object */
+typedef struct mlt_property_s *mlt_property; /**< pointer to Property object */
+typedef struct mlt_properties_s *mlt_properties; /**< pointer to Properties object */
+typedef struct mlt_event_struct *mlt_event; /**< pointer to Event object */
+typedef struct mlt_service_s *mlt_service; /**< pointer to Service object */
+typedef struct mlt_producer_s *mlt_producer; /**< pointer to Producer object */
+typedef struct mlt_playlist_s *mlt_playlist; /**< pointer to Playlist object */
+typedef struct mlt_multitrack_s *mlt_multitrack; /**< pointer to Multitrack object */
+typedef struct mlt_filter_s *mlt_filter; /**< pointer to Filter object */
+typedef struct mlt_transition_s *mlt_transition; /**< pointer to Transition object */
+typedef struct mlt_tractor_s *mlt_tractor; /**< pointer to Tractor object */
+typedef struct mlt_field_s *mlt_field; /**< pointer to Field object */
+typedef struct mlt_consumer_s *mlt_consumer; /**< pointer to Consumer object */
+typedef struct mlt_parser_s *mlt_parser; /**< pointer to Properties object */
+typedef struct mlt_deque_s *mlt_deque; /**< pointer to Deque object */
+typedef struct mlt_geometry_s *mlt_geometry; /**< pointer to Geometry object */
+typedef struct mlt_geometry_item_s *mlt_geometry_item; /**< pointer to Geometry Item object */
+typedef struct mlt_profile_s *mlt_profile; /**< pointer to Profile object */
+typedef struct mlt_repository_s *mlt_repository; /**< pointer to Repository object */
+typedef struct mlt_cache_s *mlt_cache; /**< pointer to Cache object */
+typedef struct mlt_cache_item_s *mlt_cache_item; /**< pointer to CacheItem object */
+
+typedef void ( *mlt_destructor )( void * ); /**< pointer to destructor function */
+typedef char *( *mlt_serialiser )( void *, int length );/**< pointer to serialization function */
+
+#define MLT_SERVICE(x) ( ( mlt_service )( x ) ) /**< Cast to a Service pointer */
+#define MLT_PRODUCER(x) ( ( mlt_producer )( x ) ) /**< Cast to a Producer pointer */
+#define MLT_MULTITRACK(x) ( ( mlt_multitrack )( x ) ) /**< Cast to a Multitrack pointer */
+#define MLT_PLAYLIST(x) ( ( mlt_playlist )( x ) ) /**< Cast to a Playlist pointer */
+#define MLT_TRACTOR(x) ( ( mlt_tractor )( x ) ) /**< Cast to a Tractor pointer */
+#define MLT_FILTER(x) ( ( mlt_filter )( x ) ) /**< Cast to a Filter pointer */
+#define MLT_TRANSITION(x) ( ( mlt_transition )( x ) ) /**< Cast to a Transition pointer */
+
+#endif
--- /dev/null
+include ../../config.mak
+
+TARGET = humperdink
+
+OBJS = client.o \
+ io.o \
+ remote.o
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../valerie -lvalerie
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -d "$(DESTDIR)$(bindir)"
+ install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * client.c -- Valerie client demo
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "client.h"
+#include "io.h"
+
+/** Clip navigation enumeration.
+*/
+
+typedef enum
+{
+ absolute,
+ relative
+}
+dv_demo_whence;
+
+/** Function prototype for menu handling.
+*/
+
+typedef valerie_error_code (*demo_function)( dv_demo );
+
+/** The menu structure.
+*/
+
+typedef struct
+{
+ const char *description;
+ struct menu_item
+ {
+ const char *option;
+ demo_function function;
+ }
+ array[ 50 ];
+}
+*dv_demo_menu, dv_demo_menu_t;
+
+/** Forward reference to menu runner.
+*/
+
+extern valerie_error_code dv_demo_run_menu( dv_demo, dv_demo_menu );
+
+/** Foward references.
+*/
+
+extern valerie_error_code dv_demo_list_nodes( dv_demo );
+extern valerie_error_code dv_demo_add_unit( dv_demo );
+extern valerie_error_code dv_demo_select_unit( dv_demo );
+extern valerie_error_code dv_demo_execute( dv_demo );
+extern valerie_error_code dv_demo_load( dv_demo );
+extern valerie_error_code dv_demo_transport( dv_demo );
+static void *dv_demo_status_thread( void * );
+
+/** Connected menu definition.
+*/
+
+dv_demo_menu_t connected_menu =
+{
+ "Connected Menu",
+ {
+ { "Add Unit", dv_demo_add_unit },
+ { "Select Unit", dv_demo_select_unit },
+ { "Command Shell", dv_demo_execute },
+ { NULL, NULL }
+ }
+};
+
+/** Initialise the demo structure.
+*/
+
+dv_demo dv_demo_init( valerie_parser parser )
+{
+ dv_demo this = malloc( sizeof( dv_demo_t ) );
+ if ( this != NULL )
+ {
+ int index = 0;
+ memset( this, 0, sizeof( dv_demo_t ) );
+ strcpy( this->last_directory, "/" );
+ for ( index = 0; index < 4; index ++ )
+ {
+ this->queues[ index ].unit = index;
+ this->queues[ index ].position = -1;
+ }
+ this->parser = parser;
+ }
+ return this;
+}
+
+/** Display a status record.
+*/
+
+void dv_demo_show_status( dv_demo demo, valerie_status status )
+{
+ if ( status->unit == demo->selected_unit && demo->showing )
+ {
+ char temp[ 1024 ] = "";
+
+ sprintf( temp, "U%d ", demo->selected_unit );
+
+ switch( status->status )
+ {
+ case unit_offline:
+ strcat( temp, "offline " );
+ break;
+ case unit_undefined:
+ strcat( temp, "undefined " );
+ break;
+ case unit_not_loaded:
+ strcat( temp, "unloaded " );
+ break;
+ case unit_stopped:
+ strcat( temp, "stopped " );
+ break;
+ case unit_playing:
+ strcat( temp, "playing " );
+ break;
+ case unit_paused:
+ strcat( temp, "paused " );
+ break;
+ case unit_disconnected:
+ strcat( temp, "disconnect" );
+ break;
+ default:
+ strcat( temp, "unknown " );
+ break;
+ }
+
+ sprintf( temp + strlen( temp ), " %9d %9d %9d ", status->in, status->position, status->out );
+ strcat( temp, status->clip );
+
+ printf( "%-80.80s\r", temp );
+ fflush( stdout );
+ }
+}
+
+/** Determine action to carry out as dictated by the client unit queue.
+*/
+
+void dv_demo_queue_action( dv_demo demo, valerie_status status )
+{
+ dv_demo_queue queue = &demo->queues[ status->unit ];
+
+ /* SPECIAL CASE STATUS NOTIFICATIONS TO IGNORE */
+
+ /* When we've issued a LOAD on the previous notification, then ignore this one. */
+ if ( queue->ignore )
+ {
+ queue->ignore --;
+ return;
+ }
+
+ if ( queue->mode && status->status != unit_offline && queue->head != queue->tail )
+ {
+ if ( ( status->position >= status->out && status->speed > 0 ) || status->status == unit_not_loaded )
+ {
+ queue->position = ( queue->position + 1 ) % 50;
+ if ( queue->position == queue->tail )
+ queue->position = queue->head;
+ valerie_unit_load( demo->dv_status, status->unit, queue->list[ queue->position ] );
+ if ( status->status == unit_not_loaded )
+ valerie_unit_play( demo->dv, queue->unit );
+ queue->ignore = 1;
+ }
+ else if ( ( status->position <= status->in && status->speed < 0 ) || status->status == unit_not_loaded )
+ {
+ if ( queue->position == -1 )
+ queue->position = queue->head;
+ valerie_unit_load( demo->dv_status, status->unit, queue->list[ queue->position ] );
+ if ( status->status == unit_not_loaded )
+ valerie_unit_play( demo->dv, queue->unit );
+ queue->position = ( queue->position - 1 ) % 50;
+ queue->ignore = 1;
+ }
+ }
+}
+
+/** Status thread.
+*/
+
+static void *dv_demo_status_thread( void *arg )
+{
+ dv_demo demo = arg;
+ valerie_status_t status;
+ valerie_notifier notifier = valerie_get_notifier( demo->dv_status );
+
+ while ( !demo->terminated )
+ {
+ if ( valerie_notifier_wait( notifier, &status ) != -1 )
+ {
+ dv_demo_queue_action( demo, &status );
+ dv_demo_show_status( demo, &status );
+ if ( status.status == unit_disconnected )
+ demo->disconnected = 1;
+ }
+ }
+
+ return NULL;
+}
+
+/** Turn on/off status display.
+*/
+
+void dv_demo_change_status( dv_demo demo, int flag )
+{
+ if ( demo->disconnected && flag )
+ {
+ valerie_error_code error = valerie_connect( demo->dv );
+ if ( error == valerie_ok )
+ demo->disconnected = 0;
+ else
+ beep();
+ }
+
+ if ( flag )
+ {
+ valerie_status_t status;
+ valerie_notifier notifier = valerie_get_notifier( demo->dv );
+ valerie_notifier_get( notifier, &status, demo->selected_unit );
+ demo->showing = 1;
+ dv_demo_show_status( demo, &status );
+ }
+ else
+ {
+ demo->showing = 0;
+ printf( "%-80.80s\r", " " );
+ fflush( stdout );
+ }
+}
+
+/** Add a unit.
+*/
+
+valerie_error_code dv_demo_add_unit( dv_demo demo )
+{
+ valerie_error_code error = valerie_ok;
+ valerie_nodes nodes = valerie_nodes_init( demo->dv );
+ valerie_units units = valerie_units_init( demo->dv );
+
+ if ( valerie_nodes_count( nodes ) != -1 && valerie_units_count( units ) != -1 )
+ {
+ char pressed;
+ valerie_node_entry_t node;
+ valerie_unit_entry_t unit;
+ int node_index = 0;
+ int unit_index = 0;
+
+ printf( "Select a Node\n\n" );
+
+ for ( node_index = 0; node_index < valerie_nodes_count( nodes ); node_index ++ )
+ {
+ valerie_nodes_get( nodes, node_index, &node );
+ printf( "%d: %s - %s ", node_index + 1, node.guid, node.name );
+ for ( unit_index = 0; unit_index < valerie_units_count( units ); unit_index ++ )
+ {
+ valerie_units_get( units, unit_index, &unit );
+ if ( !strcmp( unit.guid, node.guid ) )
+ printf( "[U%d] ", unit.unit );
+ }
+ printf( "\n" );
+ }
+
+ printf( "0. Exit\n\n" );
+
+ printf( "Node: " );
+
+ while ( ( pressed = get_keypress( ) ) != '0' )
+ {
+ node_index = pressed - '1';
+ if ( node_index >= 0 && node_index < valerie_nodes_count( nodes ) )
+ {
+ int unit;
+ printf( "%c\n\n", pressed );
+ valerie_nodes_get( nodes, node_index, &node );
+ if ( valerie_unit_add( demo->dv, node.guid, &unit ) == valerie_ok )
+ {
+ printf( "Unit added as U%d\n", unit );
+ demo->selected_unit = unit;
+ }
+ else
+ {
+ int index = 0;
+ valerie_response response = valerie_get_last_response( demo->dv );
+ printf( "Failed to add unit:\n\n" );
+ for( index = 1; index < valerie_response_count( response ) - 1; index ++ )
+ printf( "%s\n", valerie_response_get_line( response, index ) );
+ }
+ printf( "\n" );
+ wait_for_any_key( NULL );
+ break;
+ }
+ else
+ {
+ beep( );
+ }
+ }
+ }
+ else
+ {
+ printf( "Invalid response from the server.\n\n" );
+ wait_for_any_key( NULL );
+ }
+
+ valerie_nodes_close( nodes );
+ valerie_units_close( units );
+
+ return error;
+}
+
+/** Select a unit.
+*/
+
+valerie_error_code dv_demo_select_unit( dv_demo demo )
+{
+ int terminated = 0;
+ int refresh = 1;
+
+ while ( !terminated )
+ {
+ valerie_units units = valerie_units_init( demo->dv );
+
+ if ( valerie_units_count( units ) > 0 )
+ {
+ valerie_unit_entry_t unit;
+ int index = 0;
+ char key = '\0';
+
+ if ( refresh )
+ {
+ printf( "Select a Unit\n\n" );
+
+ for ( index = 0; index < valerie_units_count( units ); index ++ )
+ {
+ valerie_units_get( units, index, &unit );
+ printf( "%d: U%d - %s [%s]\n", index + 1,
+ unit.unit,
+ unit.guid,
+ unit.online ? "online" : "offline" );
+ }
+ printf( "0: Exit\n\n" );
+
+ printf( "Unit [%d]: ", demo->selected_unit + 1 );
+ refresh = 0;
+ }
+
+ key = get_keypress( );
+
+ if ( key == '\r' )
+ key = demo->selected_unit + '1';
+
+ if ( key != '0' )
+ {
+ if ( key >= '1' && key < '1' + valerie_units_count( units ) )
+ {
+ demo->selected_unit = key - '1';
+ printf( "%c\n\n", key );
+ dv_demo_load( demo );
+ refresh = 1;
+ }
+ else
+ {
+ beep( );
+ }
+ }
+ else
+ {
+ printf( "0\n\n" );
+ terminated = 1;
+ }
+ }
+ else if ( valerie_units_count( units ) == 0 )
+ {
+ printf( "No units added - add a unit first\n\n" );
+ dv_demo_add_unit( demo );
+ }
+ else
+ {
+ printf( "Unable to obtain Unit List.\n" );
+ terminated = 1;
+ }
+
+ valerie_units_close( units );
+ }
+
+ return valerie_ok;
+}
+
+/** Execute an arbitrary command.
+*/
+
+valerie_error_code dv_demo_execute( dv_demo demo )
+{
+ valerie_error_code error = valerie_ok;
+ char command[ 10240 ];
+ int terminated = 0;
+
+ printf( "Miracle Shell\n" );
+ printf( "Enter an empty command to exit.\n\n" );
+
+ while ( !terminated )
+ {
+ terminated = 1;
+ printf( "Command> " );
+
+ if ( chomp( io_get_string( command, 10240, "" ) ) != NULL )
+ {
+ if ( strcmp( command, "" ) )
+ {
+ int index = 0;
+ valerie_response response = NULL;
+ error = valerie_execute( demo->dv, 10240, command );
+ printf( "\n" );
+ response = valerie_get_last_response( demo->dv );
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ {
+ char *line = valerie_response_get_line( response, index );
+ printf( "%4d: %s\n", index, line );
+ }
+ printf( "\n" );
+ terminated = 0;
+ }
+ }
+ }
+
+ printf( "\n" );
+
+ return error;
+}
+
+/** Add a file to the queue.
+*/
+
+valerie_error_code dv_demo_queue_add( dv_demo demo, dv_demo_queue queue, char *file )
+{
+ valerie_status_t status;
+ valerie_notifier notifier = valerie_get_notifier( demo->dv );
+
+ if ( ( queue->tail + 1 ) % 50 == queue->head )
+ queue->head = ( queue->head + 1 ) % 50;
+ strcpy( queue->list[ queue->tail ], file );
+ queue->tail = ( queue->tail + 1 ) % 50;
+
+ valerie_notifier_get( notifier, &status, queue->unit );
+ valerie_notifier_put( notifier, &status );
+
+ return valerie_ok;
+}
+
+/** Basic queue maintenance and status reports.
+*/
+
+valerie_error_code dv_demo_queue_maintenance( dv_demo demo, dv_demo_queue queue )
+{
+ printf( "Queue Maintenance for Unit %d\n\n", queue->unit );
+
+ if ( !queue->mode )
+ {
+ char ch;
+ printf( "Activate queueing? [Y] " );
+ ch = get_keypress( );
+ if ( ch == 'y' || ch == 'Y' || ch == '\r' )
+ queue->mode = 1;
+ printf( "\n\n" );
+ }
+
+ if ( queue->mode )
+ {
+ int terminated = 0;
+ int last_position = -2;
+
+ term_init( );
+
+ while ( !terminated )
+ {
+ int first = ( queue->position + 1 ) % 50;
+ int index = first;
+
+ if ( first == queue->tail )
+ index = first = queue->head;
+
+ if ( queue->head == queue->tail )
+ {
+ if ( last_position == -2 )
+ {
+ printf( "Queue is empty\n" );
+ printf( "\n" );
+ printf( "0 = exit, t = turn off queueing\n\n" );
+ last_position = -1;
+ }
+ }
+ else if ( last_position != queue->position )
+ {
+ printf( "Order of play\n\n" );
+
+ do
+ {
+ printf( "%c%02d: %s\n", index == first ? '*' : ' ', index, queue->list[ index ] + 1 );
+ index = ( index + 1 ) % 50;
+ if ( index == queue->tail )
+ index = queue->head;
+ }
+ while( index != first );
+
+ printf( "\n" );
+ printf( "0 = exit, t = turn off queueing, c = clear queue\n\n" );
+ last_position = queue->position;
+ }
+
+ dv_demo_change_status( demo, 1 );
+
+ switch( term_read( ) )
+ {
+ case -1:
+ break;
+ case '0':
+ terminated = 1;
+ break;
+ case 't':
+ terminated = 1;
+ queue->mode = 0;
+ break;
+ case 'c':
+ queue->head = queue->tail = 0;
+ queue->position = -1;
+ last_position = -2;
+ break;
+ }
+
+ dv_demo_change_status( demo, 0 );
+ }
+
+ term_exit( );
+ }
+
+ return valerie_ok;
+}
+
+/** Load a file to the selected unit. Horrible function - sorry :-/. Not a good
+ demo....
+*/
+
+valerie_error_code dv_demo_load( dv_demo demo )
+{
+ valerie_error_code error = valerie_ok;
+ int terminated = 0;
+ int refresh = 1;
+ int start = 0;
+
+ strcpy( demo->current_directory, demo->last_directory );
+
+ term_init( );
+
+ while ( !terminated )
+ {
+ valerie_dir dir = valerie_dir_init( demo->dv, demo->current_directory );
+
+ if ( valerie_dir_count( dir ) == -1 )
+ {
+ printf( "Invalid directory - retrying %s\n", demo->last_directory );
+ valerie_dir_close( dir );
+ dir = valerie_dir_init( demo->dv, demo->last_directory );
+ if ( valerie_dir_count( dir ) == -1 )
+ {
+ printf( "Invalid directory - going back to /\n" );
+ valerie_dir_close( dir );
+ dir = valerie_dir_init( demo->dv, "/" );
+ strcpy( demo->current_directory, "/" );
+ }
+ else
+ {
+ strcpy( demo->current_directory, demo->last_directory );
+ }
+ }
+
+ terminated = valerie_dir_count( dir ) == -1;
+
+ if ( !terminated )
+ {
+ int index = 0;
+ int selected = 0;
+ int max = 9;
+ int end = 0;
+
+ end = valerie_dir_count( dir );
+
+ strcpy( demo->last_directory, demo->current_directory );
+
+ while ( !selected && !terminated )
+ {
+ valerie_dir_entry_t entry;
+ int pressed;
+
+ if ( refresh )
+ {
+ const char *action = "Load & Play";
+ if ( demo->queues[ demo->selected_unit ].mode )
+ action = "Queue";
+ printf( "%s from %s\n\n", action, demo->current_directory );
+ if ( strcmp( demo->current_directory, "/" ) )
+ printf( "-: Parent directory\n" );
+ for ( index = start; index < end && ( index - start ) < max; index ++ )
+ {
+ valerie_dir_get( dir, index, &entry );
+ printf( "%d: %s\n", index - start + 1, entry.name );
+ }
+ while ( ( index ++ % 9 ) != 0 )
+ printf( "\n" );
+ printf( "\n" );
+ if ( start + max < end )
+ printf( "space = more files" );
+ else if ( end > max )
+ printf( "space = return to start of list" );
+ if ( start > 0 )
+ printf( ", b = previous files" );
+ printf( "\n" );
+ printf( "0 = abort, t = transport, x = execute command, q = queue maintenance\n\n" );
+ refresh = 0;
+ }
+
+ dv_demo_change_status( demo, 1 );
+
+ pressed = term_read( );
+ switch( pressed )
+ {
+ case -1:
+ break;
+ case '0':
+ terminated = 1;
+ break;
+ case 'b':
+ refresh = start - max >= 0;
+ if ( refresh )
+ start = start - max;
+ break;
+ case ' ':
+ refresh = start + max < end;
+ if ( refresh )
+ {
+ start = start + max;
+ }
+ else if ( end > max )
+ {
+ start = 0;
+ refresh = 1;
+ }
+ break;
+ case '-':
+ if ( strcmp( demo->current_directory, "/" ) )
+ {
+ selected = 1;
+ ( *strrchr( demo->current_directory, '/' ) ) = '\0';
+ ( *( strrchr( demo->current_directory, '/' ) + 1 ) ) = '\0';
+ }
+ break;
+ case 't':
+ dv_demo_change_status( demo, 0 );
+ term_exit( );
+ dv_demo_transport( demo );
+ term_init( );
+ selected = 1;
+ break;
+ case 'x':
+ dv_demo_change_status( demo, 0 );
+ term_exit( );
+ dv_demo_execute( demo );
+ term_init( );
+ selected = 1;
+ break;
+ case 'q':
+ dv_demo_change_status( demo, 0 );
+ term_exit( );
+ dv_demo_queue_maintenance( demo, &demo->queues[ demo->selected_unit ] );
+ term_init( );
+ selected = 1;
+ break;
+ default:
+ if ( pressed >= '1' && pressed <= '9' )
+ {
+ if ( ( start + pressed - '1' ) < end )
+ {
+ valerie_dir_get( dir, start + pressed - '1', &entry );
+ selected = 1;
+ strcat( demo->current_directory, entry.name );
+ }
+ }
+ break;
+ }
+
+ dv_demo_change_status( demo, 0 );
+ }
+
+ valerie_dir_close( dir );
+ }
+
+ if ( !terminated && demo->current_directory[ strlen( demo->current_directory ) - 1 ] != '/' )
+ {
+ if ( demo->queues[ demo->selected_unit ].mode == 0 )
+ {
+ error = valerie_unit_load( demo->dv, demo->selected_unit, demo->current_directory );
+ valerie_unit_play( demo->dv, demo->selected_unit );
+ }
+ else
+ {
+ dv_demo_queue_add( demo, &demo->queues[ demo->selected_unit ], demo->current_directory );
+ printf( "File %s added to queue.\n", demo->current_directory );
+ }
+ strcpy( demo->current_directory, demo->last_directory );
+ refresh = 0;
+ }
+ else
+ {
+ refresh = 1;
+ start = 0;
+ }
+ }
+
+ term_exit( );
+
+ return error;
+}
+
+/** Set the in point of the clip on the select unit.
+*/
+
+valerie_error_code dv_demo_set_in( dv_demo demo )
+{
+ int position = 0;
+ valerie_status_t status;
+ valerie_notifier notifier = valerie_parser_get_notifier( demo->parser );
+ valerie_notifier_get( notifier, &status, demo->selected_unit );
+ position = status.position;
+ return valerie_unit_set_in( demo->dv, demo->selected_unit, position );
+}
+
+/** Set the out point of the clip on the selected unit.
+*/
+
+valerie_error_code dv_demo_set_out( dv_demo demo )
+{
+ int position = 0;
+ valerie_status_t status;
+ valerie_notifier notifier = valerie_parser_get_notifier( demo->parser );
+ valerie_notifier_get( notifier, &status, demo->selected_unit );
+ position = status.position;
+ return valerie_unit_set_out( demo->dv, demo->selected_unit, position );
+}
+
+/** Clear the in and out points on the selected unit.
+*/
+
+valerie_error_code dv_demo_clear_in_out( dv_demo demo )
+{
+ return valerie_unit_clear_in_out( demo->dv, demo->selected_unit );
+}
+
+/** Goto a user specified frame on the selected unit.
+*/
+
+valerie_error_code dv_demo_goto( dv_demo demo )
+{
+ int frame = 0;
+ printf( "Frame: " );
+ if ( get_int( &frame, 0 ) )
+ return valerie_unit_goto( demo->dv, demo->selected_unit, frame );
+ return valerie_ok;
+}
+
+/** Manipulate playback on the selected unit.
+*/
+
+valerie_error_code dv_demo_transport( dv_demo demo )
+{
+ valerie_error_code error = valerie_ok;
+ int refresh = 1;
+ int terminated = 0;
+ valerie_status_t status;
+ valerie_notifier notifier = valerie_get_notifier( demo->dv );
+
+ while ( !terminated )
+ {
+ if ( refresh )
+ {
+ printf( " +----+ +------+ +----+ +------+ +---+ +-----+ +------+ +-----+ +---+ \n" );
+ printf( " |1=-5| |2=-2.5| |3=-1| |4=-0.5| |5=1| |6=0.5| |7=1.25| |8=2.5| |9=5| \n" );
+ printf( " +----+ +------+ +----+ +------+ +---+ +-----+ +------+ +-----+ +---+ \n" );
+ printf( "\n" );
+ printf( "+----------------------------------------------------------------------+\n" );
+ printf( "| 0 = quit, x = eXecute, 'space' = pause |\n" );
+ printf( "| g = goto a frame, q = queue maintenance |\n" );
+ printf( "| h = step -1, j = end of clip, k = start of clip, l = step 1 |\n" );
+ printf( "| eof handling: p = pause, r = repeat, t = terminate |\n" );
+ printf( "| i = set in point, o = set out point, c = clear in/out |\n" );
+ printf( "| u = use point settings, d = don't use point settings |\n" );
+ printf( "+----------------------------------------------------------------------+\n" );
+ printf( "\n" );
+ term_init( );
+ refresh = 0;
+ }
+
+ dv_demo_change_status( demo, 1 );
+
+ switch( term_read( ) )
+ {
+ case '0':
+ terminated = 1;
+ break;
+ case -1:
+ break;
+ case ' ':
+ error = valerie_unit_pause( demo->dv, demo->selected_unit );
+ break;
+ case '1':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -5000 );
+ break;
+ case '2':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -2500 );
+ break;
+ case '3':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -1000 );
+ break;
+ case '4':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -500 );
+ break;
+ case '5':
+ error = valerie_unit_play( demo->dv, demo->selected_unit );
+ break;
+ case '6':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 500 );
+ break;
+ case '7':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 1250 );
+ break;
+ case '8':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 2500 );
+ break;
+ case '9':
+ error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 5000 );
+ break;
+ case 's':
+ error = valerie_unit_goto( demo->dv, demo->selected_unit, 0 );
+ break;
+ case 'h':
+ error = valerie_unit_step( demo->dv, demo->selected_unit, -1 );
+ break;
+ case 'j':
+ valerie_notifier_get( notifier, &status, demo->selected_unit );
+ error = valerie_unit_goto( demo->dv, demo->selected_unit, status.tail_out );
+ break;
+ case 'k':
+ valerie_notifier_get( notifier, &status, demo->selected_unit );
+ error = valerie_unit_goto( demo->dv, demo->selected_unit, status.in );
+ break;
+ case 'l':
+ error = valerie_unit_step( demo->dv, demo->selected_unit, 1 );
+ break;
+ case 'p':
+ error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "pause" );
+ break;
+ case 'r':
+ error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "loop" );
+ break;
+ case 't':
+ error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "stop" );
+ break;
+ case 'i':
+ error = dv_demo_set_in( demo );
+ break;
+ case 'o':
+ error = dv_demo_set_out( demo );
+ break;
+ case 'g':
+ dv_demo_change_status( demo, 0 );
+ term_exit( );
+ error = dv_demo_goto( demo );
+ refresh = 1;
+ break;
+ case 'c':
+ error = dv_demo_clear_in_out( demo );
+ break;
+ case 'u':
+ error = valerie_unit_set( demo->dv, demo->selected_unit, "points", "use" );
+ break;
+ case 'd':
+ error = valerie_unit_set( demo->dv, demo->selected_unit, "points", "ignore" );
+ break;
+ case 'x':
+ dv_demo_change_status( demo, 0 );
+ term_exit( );
+ dv_demo_execute( demo );
+ refresh = 1;
+ break;
+ case 'q':
+ dv_demo_change_status( demo, 0 );
+ term_exit( );
+ dv_demo_queue_maintenance( demo, &demo->queues[ demo->selected_unit ] );
+ refresh = 1;
+ break;
+ }
+
+ dv_demo_change_status( demo, 0 );
+ }
+
+ term_exit( );
+
+ return error;
+}
+
+/** Recursive menu execution.
+*/
+
+valerie_error_code dv_demo_run_menu( dv_demo demo, dv_demo_menu menu )
+{
+ const char *items = "123456789abcdefghijklmnopqrstuvwxyz";
+ int refresh_menu = 1;
+ int terminated = 0;
+ int item_count = 0;
+ int item_selected = 0;
+ int index = 0;
+ char key;
+
+ while( !terminated )
+ {
+
+ if ( refresh_menu )
+ {
+ printf( "%s\n\n", menu->description );
+ for ( index = 0; menu->array[ index ].option != NULL; index ++ )
+ printf( "%c: %s\n", items[ index ], menu->array[ index ].option );
+ printf( "0: Exit\n\n" );
+ printf( "Select Option: " );
+ refresh_menu = 0;
+ item_count = index;
+ }
+
+ key = get_keypress( );
+
+ if ( demo->disconnected && key != '0' )
+ {
+ valerie_error_code error = valerie_connect( demo->dv );
+ if ( error == valerie_ok )
+ demo->disconnected = 0;
+ else
+ beep();
+ }
+
+ if ( !demo->disconnected || key == '0' )
+ {
+ item_selected = strchr( items, key ) - items;
+
+ if ( key == '0' )
+ {
+ printf( "%c\n\n", key );
+ terminated = 1;
+ }
+ else if ( item_selected >= 0 && item_selected < item_count )
+ {
+ printf( "%c\n\n", key );
+ menu->array[ item_selected ].function( demo );
+ refresh_menu = 1;
+ }
+ else
+ {
+ beep( );
+ }
+ }
+ }
+
+ return valerie_ok;
+}
+
+/** Entry point for main menu.
+*/
+
+void dv_demo_run( dv_demo this )
+{
+ this->dv = valerie_init( this->parser );
+ this->dv_status = valerie_init( this->parser );
+ if ( valerie_connect( this->dv ) == valerie_ok )
+ {
+ pthread_create( &this->thread, NULL, dv_demo_status_thread, this );
+ dv_demo_run_menu( this, &connected_menu );
+ this->terminated = 1;
+ pthread_join( this->thread, NULL );
+ this->terminated = 0;
+ }
+ else
+ {
+ printf( "Unable to connect." );
+ wait_for_any_key( "" );
+ }
+
+ valerie_close( this->dv_status );
+ valerie_close( this->dv );
+
+ printf( "Demo Exit.\n" );
+}
+
+/** Close the demo structure.
+*/
+
+void dv_demo_close( dv_demo demo )
+{
+ free( demo );
+}
--- /dev/null
+/*
+ * client.h -- Valerie client demo
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_CLIENT_H_
+#define _DEMO_CLIENT_H_
+
+#include <stdio.h>
+#include <pthread.h>
+#include <valerie/valerie.h>
+
+/** Queue for unit playback
+*/
+
+typedef struct
+{
+ int mode;
+ int unit;
+ int position;
+ int head;
+ int tail;
+ char list[ 50 ][ PATH_MAX + NAME_MAX ];
+ int ignore;
+}
+*dv_demo_queue, dv_demo_queue_t;
+
+/** Structure for storing app state.
+*/
+
+typedef struct
+{
+ int disconnected;
+ valerie_parser parser;
+ valerie dv;
+ valerie dv_status;
+ int selected_unit;
+ char current_directory[ 512 ];
+ char last_directory[ 512 ];
+ int showing;
+ int terminated;
+ pthread_t thread;
+ dv_demo_queue_t queues[ MAX_UNITS ];
+}
+*dv_demo, dv_demo_t;
+
+extern dv_demo dv_demo_init( valerie_parser );
+extern void dv_demo_run( dv_demo );
+extern void dv_demo_close( dv_demo );
+
+#endif
--- /dev/null
+/*
+ * io.c -- Valerie client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "io.h"
+
+char *chomp( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ if ( length && input[ length - 1 ] == '\n' )
+ input[ length - 1 ] = '\0';
+ if ( length > 1 && input[ length - 2 ] == '\r' )
+ input[ length - 2 ] = '\0';
+ }
+ return input;
+}
+
+char *trim( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ int first = 0;
+ while( first < length && isspace( input[ first ] ) )
+ first ++;
+ memmove( input, input + first, length - first + 1 );
+ length = length - first;
+ while ( length > 0 && isspace( input[ length - 1 ] ) )
+ input[ -- length ] = '\0';
+ }
+ return input;
+}
+
+char *strip_quotes( char *input )
+{
+ if ( input != NULL )
+ {
+ char *ptr = strrchr( input, '\"' );
+ if ( ptr != NULL )
+ *ptr = '\0';
+ if ( input[ 0 ] == '\"' )
+ strcpy( input, input + 1 );
+ }
+ return input;
+}
+
+char *io_get_string( char *output, int maxlength, const char *use )
+{
+ char *value = NULL;
+ strcpy( output, use );
+ if ( trim( chomp( fgets( output, maxlength, stdin ) ) ) != NULL )
+ {
+ if ( !strcmp( output, "" ) )
+ strcpy( output, use );
+ value = output;
+ }
+ return value;
+}
+
+int *get_int( int *output, int use )
+{
+ int *value = NULL;
+ char temp[ 132 ];
+ *output = use;
+ if ( trim( chomp( fgets( temp, 132, stdin ) ) ) != NULL )
+ {
+ if ( strcmp( temp, "" ) )
+ *output = atoi( temp );
+ value = output;
+ }
+ return value;
+}
+
+/** This stores the previous settings
+*/
+
+static struct termios oldtty;
+static int mode = 0;
+
+/** This is called automatically on application exit to restore the
+ previous tty settings.
+*/
+
+void term_exit(void)
+{
+ if ( mode == 1 )
+ {
+ tcsetattr( 0, TCSANOW, &oldtty );
+ mode = 0;
+ }
+}
+
+/** Init terminal so that we can grab keys without blocking.
+*/
+
+void term_init( )
+{
+ struct termios tty;
+
+ tcgetattr( 0, &tty );
+ oldtty = tty;
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[ VMIN ] = 1;
+ tty.c_cc[ VTIME ] = 0;
+
+ tcsetattr( 0, TCSANOW, &tty );
+
+ mode = 1;
+
+ atexit( term_exit );
+}
+
+/** Check for a keypress without blocking infinitely.
+ Returns: ASCII value of keypress or -1 if no keypress detected.
+*/
+
+int term_read( )
+{
+ int n = 1;
+ unsigned char ch;
+ struct timeval tv;
+ fd_set rfds;
+
+ FD_ZERO( &rfds );
+ FD_SET( 0, &rfds );
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ n = select( 1, &rfds, NULL, NULL, &tv );
+ if (n > 0)
+ {
+ n = read( 0, &ch, 1 );
+ tcflush( 0, TCIFLUSH );
+ if (n == 1)
+ return ch;
+ return n;
+ }
+ return -1;
+}
+
+char get_keypress( )
+{
+ char value = '\0';
+ int pressed = 0;
+
+ fflush( stdout );
+
+ term_init( );
+ while ( ( pressed = term_read( ) ) == -1 ) ;
+ term_exit( );
+
+ value = (char)pressed;
+
+ return value;
+}
+
+void wait_for_any_key( const char *message )
+{
+ if ( message == NULL )
+ printf( "Press any key to continue: " );
+ else
+ printf( "%s", message );
+
+ get_keypress( );
+
+ printf( "\n\n" );
+}
+
+void beep( )
+{
+ printf( "%c", 7 );
+ fflush( stdout );
+}
--- /dev/null
+/*
+ * io.h -- Valerie client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_IO_H_
+#define _DEMO_IO_H_
+
+extern char *chomp( char * );
+extern char *trim( char * );
+extern char *strip_quotes( char * );
+extern char *io_get_string( char *, int, const char * );
+extern int *get_int( int *, int );
+extern void term_init( );
+extern int term_read( );
+extern void term_exit( );
+extern char get_keypress( );
+extern void wait_for_any_key( const char * );
+extern void beep( );
+
+#endif
--- /dev/null
+/*
+ * remote.c -- Remote Valerie client demo
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdint.h>
+
+#include <valerie/valerie_remote.h>
+
+/* Application header files */
+#include "client.h"
+#include "io.h"
+
+/** Connect to a remote server.
+*/
+
+static valerie_parser create_parser( )
+{
+ char server[ 132 ];
+ int port;
+ valerie_parser parser = NULL;
+
+ printf( "Connecting to a Server\n\n" );
+
+ printf( "Server [localhost]: " );
+
+ if ( io_get_string( server, sizeof( server ), "localhost" ) != NULL )
+ {
+ printf( "Port [5250]: " );
+
+ if ( get_int( &port, 5250 ) != NULL )
+ parser = valerie_parser_init_remote( server, port );
+ }
+
+ printf( "\n" );
+
+ return parser;
+}
+
+/** Main function.
+*/
+
+int main( int argc, char **argv )
+{
+ valerie_parser parser = create_parser( );
+
+ if ( parser != NULL )
+ {
+ dv_demo demo = dv_demo_init( parser );
+ dv_demo_run( demo );
+ dv_demo_close( demo );
+ valerie_parser_close( parser );
+ }
+
+ return 0;
+}
--- /dev/null
+include ../../config.mak
+
+TARGET = inigo
+
+OBJS = inigo.o \
+ io.o
+
+CFLAGS += -I.. $(RDYNAMIC) -DVERSION=\"$(version)\"
+
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -d "$(DESTDIR)$(bindir)"
+ install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * inigo.c -- MLT command line utility
+ * Copyright (C) 2002-2008 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+
+#include <framework/mlt.h>
+
+#ifdef __DARWIN__
+#include <SDL.h>
+#endif
+
+#include "io.h"
+
+static void transport_action( mlt_producer producer, char *value )
+{
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
+ mlt_consumer consumer = mlt_properties_get_data( properties, "transport_consumer", NULL );
+
+ mlt_properties_set_int( properties, "stats_off", 1 );
+
+ if ( strlen( value ) == 1 )
+ {
+ switch( value[ 0 ] )
+ {
+ case 'q':
+ mlt_properties_set_int( properties, "done", 1 );
+ break;
+ case '0':
+ mlt_producer_set_speed( producer, 1 );
+ mlt_producer_seek( producer, 0 );
+ break;
+ case '1':
+ mlt_producer_set_speed( producer, -10 );
+ break;
+ case '2':
+ mlt_producer_set_speed( producer, -5 );
+ break;
+ case '3':
+ mlt_producer_set_speed( producer, -2 );
+ break;
+ case '4':
+ mlt_producer_set_speed( producer, -1 );
+ break;
+ case '5':
+ mlt_producer_set_speed( producer, 0 );
+ break;
+ case '6':
+ case ' ':
+ mlt_producer_set_speed( producer, 1 );
+ break;
+ case '7':
+ mlt_producer_set_speed( producer, 2 );
+ break;
+ case '8':
+ mlt_producer_set_speed( producer, 5 );
+ break;
+ case '9':
+ mlt_producer_set_speed( producer, 10 );
+ break;
+ case 'd':
+ if ( multitrack != NULL )
+ {
+ int i = 0;
+ mlt_position last = -1;
+ fprintf( stderr, "\n" );
+ for ( i = 0; 1; i ++ )
+ {
+ mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i );
+ if ( time == last )
+ break;
+ last = time;
+ fprintf( stderr, "%d: %d\n", i, (int)time );
+ }
+ }
+ break;
+
+ case 'g':
+ if ( multitrack != NULL )
+ {
+ mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
+ mlt_producer_seek( producer, time );
+ }
+ break;
+ case 'H':
+ if ( producer != NULL )
+ {
+ mlt_position position = mlt_producer_position( producer );
+ mlt_producer_seek( producer, position - ( mlt_producer_get_fps( producer ) * 60 ) );
+ }
+ break;
+ case 'h':
+ if ( producer != NULL )
+ {
+ mlt_position position = mlt_producer_position( producer );
+ mlt_producer_set_speed( producer, 0 );
+ mlt_producer_seek( producer, position - 1 );
+ }
+ break;
+ case 'j':
+ if ( multitrack != NULL )
+ {
+ mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
+ mlt_producer_seek( producer, time );
+ }
+ break;
+ case 'k':
+ if ( multitrack != NULL )
+ {
+ mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
+ mlt_producer_seek( producer, time );
+ }
+ break;
+ case 'l':
+ if ( producer != NULL )
+ {
+ mlt_position position = mlt_producer_position( producer );
+ if ( mlt_producer_get_speed( producer ) != 0 )
+ mlt_producer_set_speed( producer, 0 );
+ else
+ mlt_producer_seek( producer, position + 1 );
+ }
+ break;
+ case 'L':
+ if ( producer != NULL )
+ {
+ mlt_position position = mlt_producer_position( producer );
+ mlt_producer_seek( producer, position + ( mlt_producer_get_fps( producer ) * 60 ) );
+ }
+ break;
+ }
+
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
+ }
+
+ mlt_properties_set_int( properties, "stats_off", 0 );
+}
+
+static mlt_consumer create_consumer( mlt_profile profile, char *id )
+{
+ char *arg = id != NULL ? strchr( id, ':' ) : NULL;
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ mlt_consumer consumer = mlt_factory_consumer( profile, id, arg );
+ if ( consumer != NULL )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+ mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
+ }
+ return consumer;
+}
+
+#ifdef __DARWIN__
+
+static void event_handling( mlt_producer producer, mlt_consumer consumer )
+{
+ SDL_Event event;
+
+ while ( SDL_PollEvent( &event ) )
+ {
+ switch( event.type )
+ {
+ case SDL_QUIT:
+ mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( consumer ), "done", 1 );
+ break;
+
+ case SDL_KEYDOWN:
+ if ( event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
+ {
+ char keyboard[ 2 ] = { event.key.keysym.unicode, 0 };
+ transport_action( producer, keyboard );
+ }
+ break;
+ }
+ }
+}
+
+#endif
+
+static void transport( mlt_producer producer, mlt_consumer consumer )
+{
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ int silent = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent" );
+ int progress = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress" );
+ struct timespec tm = { 0, 40000 };
+ int total_length = mlt_producer_get_length( producer );
+ int last_position = 0;
+
+ if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
+ {
+ if ( !silent && !progress )
+ {
+ term_init( );
+
+ fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+ fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n" );
+ fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+
+ fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+ fprintf( stderr, "| H = back 1 minute, L = forward 1 minute |\n" );
+ fprintf( stderr, "| h = previous frame, l = next frame |\n" );
+ fprintf( stderr, "| g = start of clip, j = next clip, k = previous clip |\n" );
+ fprintf( stderr, "| 0 = restart, q = quit, space = play |\n" );
+ fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+ }
+
+ while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
+ {
+ int value = ( silent || progress )? -1 : term_read( );
+
+ if ( value != -1 )
+ {
+ char string[ 2 ] = { value, 0 };
+ transport_action( producer, string );
+ }
+
+#ifdef __DARWIN__
+ event_handling( producer, consumer );
+#endif
+
+ if ( !silent && mlt_properties_get_int( properties, "stats_off" ) == 0 )
+ {
+ if ( progress )
+ {
+ int current_position = mlt_producer_position( producer );
+ if ( current_position > last_position )
+ {
+ fprintf( stderr, "Current Frame: %10d, percentage: %10d\r",
+ current_position, 100 * current_position / total_length );
+ last_position = current_position;
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Current Position: %10d\r", (int)mlt_producer_position( producer ) );
+ }
+ }
+
+ if ( silent )
+ nanosleep( &tm, NULL );
+ }
+
+ if ( !silent )
+ fprintf( stderr, "\n" );
+ }
+}
+
+static void query_metadata( mlt_repository repo, mlt_service_type type, const char *typestr, char *id )
+{
+ mlt_properties metadata = mlt_repository_metadata( repo, type, id );
+ if ( metadata )
+ {
+ char *s = mlt_properties_serialise_yaml( metadata );
+ fprintf( stderr, "%s", s );
+ free( s );
+ }
+ else
+ {
+ fprintf( stderr, "# No metadata for %s \"%s\"\n", typestr, id );
+ }
+}
+
+static void query_services( mlt_repository repo, mlt_service_type type )
+{
+ mlt_properties services = NULL;
+ const char *typestr = NULL;
+ switch ( type )
+ {
+ case consumer_type:
+ services = mlt_repository_consumers( repo );
+ typestr = "consumers";
+ break;
+ case filter_type:
+ services = mlt_repository_filters( repo );
+ typestr = "filters";
+ break;
+ case producer_type:
+ services = mlt_repository_producers( repo );
+ typestr = "producers";
+ break;
+ case transition_type:
+ services = mlt_repository_transitions( repo );
+ typestr = "transitions";
+ break;
+ default:
+ return;
+ }
+ fprintf( stderr, "---\n%s:\n", typestr );
+ if ( services )
+ {
+ int j;
+ for ( j = 0; j < mlt_properties_count( services ); j++ )
+ fprintf( stderr, " - %s\n", mlt_properties_get_name( services, j ) );
+ }
+ fprintf( stderr, "...\n" );
+}
+
+int main( int argc, char **argv )
+{
+ int i;
+ mlt_consumer consumer = NULL;
+ mlt_producer inigo = NULL;
+ FILE *store = NULL;
+ char *name = NULL;
+ mlt_profile profile = NULL;
+ int is_progress = 0;
+ int is_silent = 0;
+
+ // Construct the factory
+ mlt_repository repo = mlt_factory_init( NULL );
+
+ for ( i = 1; i < argc; i ++ )
+ {
+ // Check for serialisation switch
+ if ( !strcmp( argv[ i ], "-serialise" ) )
+ {
+ name = argv[ ++ i ];
+ if ( name != NULL && strstr( name, ".inigo" ) )
+ store = fopen( name, "w" );
+ else
+ {
+ if ( name == NULL || name[0] == '-' )
+ store = stdout;
+ name = NULL;
+ }
+ }
+ // Look for the profile option
+ else if ( !strcmp( argv[ i ], "-profile" ) )
+ {
+ const char *pname = argv[ ++ i ];
+ if ( pname && pname[0] != '-' )
+ profile = mlt_profile_init( pname );
+ }
+ else if ( !strcmp( argv[ i ], "-progress" ) )
+ {
+ is_progress = 1;
+ }
+ // Look for the query option
+ else if ( !strcmp( argv[ i ], "-query" ) )
+ {
+ const char *pname = argv[ ++ i ];
+ if ( pname && pname[0] != '-' )
+ {
+ if ( !strcmp( pname, "consumers" ) || !strcmp( pname, "consumer" ) )
+ query_services( repo, consumer_type );
+ else if ( !strcmp( pname, "filters" ) || !strcmp( pname, "filter" ) )
+ query_services( repo, filter_type );
+ else if ( !strcmp( pname, "producers" ) || !strcmp( pname, "producer" ) )
+ query_services( repo, producer_type );
+ else if ( !strcmp( pname, "transitions" ) || !strcmp( pname, "transition" ) )
+ query_services( repo, transition_type );
+
+ else if ( !strncmp( pname, "consumer=", 9 ) )
+ query_metadata( repo, consumer_type, "consumer", strchr( pname, '=' ) + 1 );
+ else if ( !strncmp( pname, "filter=", 7 ) )
+ query_metadata( repo, filter_type, "filter", strchr( pname, '=' ) + 1 );
+ else if ( !strncmp( pname, "producer=", 9 ) )
+ query_metadata( repo, producer_type, "producer", strchr( pname, '=' ) + 1 );
+ else if ( !strncmp( pname, "transition=", 11 ) )
+ query_metadata( repo, transition_type, "transition", strchr( pname, '=' ) + 1 );
+ else
+ goto query_all;
+ }
+ else
+ {
+query_all:
+ query_services( repo, consumer_type );
+ query_services( repo, filter_type );
+ query_services( repo, producer_type );
+ query_services( repo, transition_type );
+ fprintf( stderr, "# You can query the metadata for a specific service using:\n"
+ "# -query <type>=<identifer>\n"
+ "# where <type> is one of: consumer, filter, producer, or transition.\n" );
+ }
+ goto exit_factory;
+ }
+ else if ( !strcmp( argv[ i ], "-silent" ) )
+ {
+ is_silent = 1;
+ }
+ else if ( !strcmp( argv[ i ], "-verbose" ) )
+ {
+ mlt_log_set_level( MLT_LOG_VERBOSE );
+ }
+ else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) )
+ {
+ fprintf( stderr, "MLT inigo " VERSION "\n"
+ "Copyright (C) 2002-2008 Ushodaya Enterprises Limited\n"
+ "<http://www.mltframework.org/>\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
+ );
+ goto exit_factory;
+ }
+ else if ( !strcmp( argv[ i ], "-debug" ) )
+ {
+ mlt_log_set_level( MLT_LOG_DEBUG );
+ }
+ }
+
+ // Create profile if not set explicitly
+ if ( profile == NULL )
+ profile = mlt_profile_init( NULL );
+
+ // Look for the consumer option
+ for ( i = 1; i < argc; i ++ )
+ {
+ if ( !strcmp( argv[ i ], "-consumer" ) )
+ {
+ consumer = create_consumer( profile, argv[ ++ i ] );
+ if ( consumer )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+ while ( argv[ i + 1 ] != NULL && strstr( argv[ i + 1 ], "=" ) )
+ mlt_properties_parse( properties, argv[ ++ i ] );
+ }
+ }
+ }
+
+ // If we have no consumer, default to sdl
+ if ( store == NULL && consumer == NULL )
+ consumer = create_consumer( profile, NULL );
+
+ // Get inigo producer
+ if ( argc > 1 )
+ inigo = mlt_factory_producer( profile, "inigo", &argv[ 1 ] );
+
+ // Set transport properties on consumer and produder
+ if ( consumer != NULL && inigo != NULL )
+ {
+ mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", inigo, 0, NULL, NULL );
+ mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( inigo ), "transport_consumer", consumer, 0, NULL, NULL );
+ if ( is_progress )
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress", is_progress );
+ if ( is_silent )
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent", is_silent );
+ }
+
+ if ( argc > 1 && inigo != NULL && mlt_producer_get_length( inigo ) > 0 )
+ {
+ // Parse the arguments
+ for ( i = 1; i < argc; i ++ )
+ {
+ if ( !strcmp( argv[ i ], "-serialise" ) )
+ {
+ if ( store != stdout )
+ i ++;
+ }
+ else
+ {
+ if ( store != NULL )
+ fprintf( store, "%s\n", argv[ i ] );
+
+ i ++;
+
+ while ( argv[ i ] != NULL && argv[ i ][ 0 ] != '-' )
+ {
+ if ( store != NULL )
+ fprintf( store, "%s\n", argv[ i ] );
+ i += 1;
+ }
+
+ i --;
+ }
+ }
+
+ if ( consumer != NULL && store == NULL )
+ {
+ // Get inigo's properties
+ mlt_properties inigo_props = MLT_PRODUCER_PROPERTIES( inigo );
+
+ // Get the last group
+ mlt_properties group = mlt_properties_get_data( inigo_props, "group", 0 );
+
+ // Apply group settings
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+ mlt_properties_inherit( properties, group );
+
+ // Connect consumer to inigo
+ mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( inigo ) );
+
+ // Start the consumer
+ mlt_consumer_start( consumer );
+
+ // Transport functionality
+ transport( inigo, consumer );
+
+ // Stop the consumer
+ mlt_consumer_stop( consumer );
+ }
+ else if ( store != NULL && store != stdout && name != NULL )
+ {
+ fprintf( stderr, "Project saved as %s.\n", name );
+ fclose( store );
+ }
+ }
+ else
+ {
+ fprintf( stderr,
+"Usage: inigo [options] [producer [name=value]* ]+\n"
+"Options:\n"
+" -attach filter[:arg] [name=value]* Attach a filter to the output\n"
+" -attach-cut filter[:arg] [name=value]* Attach a filter to a cut\n"
+" -attach-track filter[:arg] [name=value]* Attach a filter to a track\n"
+" -attach-clip filter[:arg] [name=value]* Attach a filter to a producer\n"
+" -audio-track | -hide-video Add an audio-only track\n"
+" -blank frames Add blank silence to a track\n"
+" -consumer id[:arg] [name=value]* Set the consumer (sink)\n"
+" -debug Set the logging level to debug\n"
+" -filter filter[:arg] [name=value]* Add a filter to the current track\n"
+" -group [name=value]* Apply properties repeatedly\n"
+" -help Show this message\n"
+" -join clips Join multiple clips into one cut\n"
+" -mix length Add a mix between the last two cuts\n"
+" -mixer transition Add a transition to the mix\n"
+" -null-track | -hide-track Add a hidden track\n"
+" -profile name Set the processing settings\n"
+" -progress Display progress along with position\n"
+" -remove Remove the most recent cut\n"
+" -repeat times Repeat the last cut\n"
+" -query List all of the registered services\n"
+" -query \"consumers\" | \"consumer\"=id List consumers or show info about one\n"
+" -query \"filters\" | \"filter\"=id List filters or show info about one\n"
+" -query \"producers\" | \"producer\"=id List producers or show info about one\n"
+" -query \"transitions\" | \"transition\"=id List transitions, show info about one\n"
+" -serialise [filename] Write the commands to a text file\n"
+" -silent Do not display position/transport\n"
+" -split relative-frame Split the last cut into two cuts\n"
+" -swap Rearrange the last two cuts\n"
+" -track Add a track\n"
+" -transition id[:arg] [name=value]* Add a transition\n"
+" -verbose Set the logging level to verbose\n"
+" -version Show the version and copyright\n"
+" -video-track | -hide-audio Add a video-only track\n"
+"For more help: <http://www.mltframework.org/>\n" );
+ }
+
+ // Close the consumer
+ if ( consumer != NULL )
+ mlt_consumer_close( consumer );
+
+ // Close the producer
+ if ( inigo != NULL )
+ mlt_producer_close( inigo );
+
+ // Close the factory
+ mlt_profile_close( profile );
+
+exit_factory:
+
+ mlt_factory_close( );
+
+ return 0;
+}
--- /dev/null
+/*
+ * io.c -- inigo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "io.h"
+
+char *chomp( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ if ( length && input[ length - 1 ] == '\n' )
+ input[ length - 1 ] = '\0';
+ if ( length > 1 && input[ length - 2 ] == '\r' )
+ input[ length - 2 ] = '\0';
+ }
+ return input;
+}
+
+char *trim( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ int first = 0;
+ while( first < length && isspace( input[ first ] ) )
+ first ++;
+ memmove( input, input + first, length - first + 1 );
+ length = length - first;
+ while ( length > 0 && isspace( input[ length - 1 ] ) )
+ input[ -- length ] = '\0';
+ }
+ return input;
+}
+
+char *strip_quotes( char *input )
+{
+ if ( input != NULL )
+ {
+ char *ptr = strrchr( input, '\"' );
+ if ( ptr != NULL )
+ *ptr = '\0';
+ if ( input[ 0 ] == '\"' )
+ strcpy( input, input + 1 );
+ }
+ return input;
+}
+
+int *get_int( int *output, int use )
+{
+ int *value = NULL;
+ char temp[ 132 ];
+ *output = use;
+ if ( trim( chomp( fgets( temp, 132, stdin ) ) ) != NULL )
+ {
+ if ( strcmp( temp, "" ) )
+ *output = atoi( temp );
+ value = output;
+ }
+ return value;
+}
+
+/** This stores the previous settings
+*/
+
+static struct termios oldtty;
+static int mode = 0;
+
+/** This is called automatically on application exit to restore the
+ previous tty settings.
+*/
+
+void term_exit(void)
+{
+ if ( mode == 1 )
+ {
+ tcsetattr( 0, TCSANOW, &oldtty );
+ mode = 0;
+ }
+}
+
+/** Init terminal so that we can grab keys without blocking.
+*/
+
+void term_init( )
+{
+ struct termios tty;
+
+ tcgetattr( 0, &tty );
+ oldtty = tty;
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[ VMIN ] = 1;
+ tty.c_cc[ VTIME ] = 0;
+
+ tcsetattr( 0, TCSANOW, &tty );
+
+ mode = 1;
+
+ atexit( term_exit );
+}
+
+/** Check for a keypress without blocking infinitely.
+ Returns: ASCII value of keypress or -1 if no keypress detected.
+*/
+
+int term_read( )
+{
+ int n = 1;
+ unsigned char ch;
+ struct timeval tv;
+ fd_set rfds;
+
+ FD_ZERO( &rfds );
+ FD_SET( 0, &rfds );
+ tv.tv_sec = 0;
+ tv.tv_usec = 40000;
+ n = select( 1, &rfds, NULL, NULL, &tv );
+ if (n > 0)
+ {
+ n = read( 0, &ch, 1 );
+ tcflush( 0, TCIFLUSH );
+ if (n == 1)
+ return ch;
+ return n;
+ }
+ return -1;
+}
+
+char get_keypress( )
+{
+ char value = '\0';
+ int pressed = 0;
+
+ fflush( stdout );
+
+ term_init( );
+ while ( ( pressed = term_read( ) ) == -1 ) ;
+ term_exit( );
+
+ value = (char)pressed;
+
+ return value;
+}
+
+void wait_for_any_key( char *message )
+{
+ if ( message == NULL )
+ printf( "Press any key to continue: " );
+ else
+ printf( "%s", message );
+
+ get_keypress( );
+
+ printf( "\n\n" );
+}
+
+void beep( )
+{
+ printf( "%c", 7 );
+ fflush( stdout );
+}
--- /dev/null
+/*
+ * io.h -- inigo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_IO_H_
+#define _DEMO_IO_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *chomp( char * );
+extern char *trim( char * );
+extern char *strip_quotes( char * );
+extern char *get_string( char *, int, char * );
+extern int *get_int( int *, int );
+extern void term_init( );
+extern int term_read( );
+extern void term_exit( );
+extern char get_keypress( );
+extern void wait_for_any_key( char * );
+extern void beep( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+include ../../config.mak
+
+TARGET = miracle
+
+ifneq ($(targetos), Darwin)
+LIBNAME = libmiracle$(LIBSUF)
+LIBTARGET = $(LIBNAME).$(version)
+LIBSONAME = $(LIBNAME).$(soversion)
+SHFLAGS += -Wl,-soname,$(LIBSONAME)
+else
+LIBNAME = libmiracle$(LIBSUF)
+LIBTARGET = libmiracle.$(version)$(LIBSUF)
+LIBSONAME = libmiracle.$(soversion)$(LIBSUF)
+SHFLAGS += -install_name $(libdir)/$(LIBSONAME) -current_version $(version) -compatibility_version $(soversion)
+endif
+
+APP_OBJS = miracle.o
+
+LIB_OBJS = miracle_log.o \
+ miracle_server.o \
+ miracle_connection.o \
+ miracle_local.o \
+ miracle_unit.o \
+ miracle_commands.o \
+ miracle_unit_commands.o
+
+INCS = miracle_server.h \
+ miracle_local.h \
+ miracle_log.h
+
+OBJS = $(APP_OBJS) $(LIB_OBJS)
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../valerie -lvalerie
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(APP_OBJS) $(LIBTARGET)
+ $(CC) -o $@ $(APP_OBJS) -L. -lmiracle $(LDFLAGS)
+
+$(LIBTARGET): $(LIB_OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(LIB_OBJS) $(LDFLAGS)
+ ln -sf $(LIBTARGET) $(LIBNAME)
+ ln -sf $(LIBTARGET) $(LIBSONAME)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET) $(LIBNAME) $(LIBTARGET)
+
+install: all
+ install -d "$(DESTDIR)$(bindir)"
+ install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+ install -m 755 $(LIBTARGET) $(DESTDIR)$(libdir)
+ ln -sf $(LIBTARGET) $(DESTDIR)$(libdir)/$(LIBSONAME)
+ ln -sf $(LIBTARGET) $(DESTDIR)$(libdir)/$(LIBNAME)
+ mkdir -p "$(DESTDIR)$(prefix)/include/mlt/miracle"
+ install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/miracle"
+
+uninstall:
+ rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+ rm -f "$(DESTDIR)$(libdir)/$(LIBTARGET)"
+ rm -f "$(DESTDIR)$(libdir)/$(LIBSONAME)"
+ rm -f "$(DESTDIR)$(libdir)/$(LIBNAME)"
+ rm -rf "$(DESTDIR)$(prefix)/include/mlt/miracle"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+echo "miracle -I$prefix/include/mlt -D_REENTRANT -L$libdir -lmiracle" >> ../../packages.dat
--- /dev/null
+/*
+ * miracle.c -- MLT Video TCP Server
+ *
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Authors:
+ * Dan Dennedy <dan@dennedy.org>
+ * Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <sched.h>
+
+#include <framework/mlt.h>
+
+/* Application header files */
+#include "miracle_server.h"
+#include "miracle_log.h"
+
+/** Our dv server.
+*/
+
+static miracle_server server = NULL;
+
+/** atexit shutdown handler for the server.
+*/
+
+static void main_cleanup( )
+{
+ miracle_server_close( server );
+}
+
+/** Report usage and exit.
+*/
+
+void usage( char *app )
+{
+ fprintf( stderr, "Usage: %s [-test] [-port NNNN]\n", app );
+ exit( 0 );
+}
+
+/** The main function.
+*/
+
+int main( int argc, char **argv )
+{
+ int error = 0;
+ int index = 0;
+ int background = 1;
+ struct timespec tm = { 5, 0 };
+ struct sched_param scp;
+
+ // Use realtime scheduling if possible
+ memset( &scp, '\0', sizeof( scp ) );
+ scp.sched_priority = sched_get_priority_max( SCHED_FIFO ) - 1;
+#ifndef __DARWIN__
+ sched_setscheduler( 0, SCHED_FIFO, &scp );
+#endif
+
+ mlt_factory_init( NULL );
+
+ server = miracle_server_init( argv[ 0 ] );
+
+ for ( index = 1; index < argc; index ++ )
+ {
+ if ( !strcmp( argv[ index ], "-port" ) )
+ miracle_server_set_port( server, atoi( argv[ ++ index ] ) );
+ else if ( !strcmp( argv[ index ], "-proxy" ) )
+ miracle_server_set_proxy( server, argv[ ++ index ] );
+ else if ( !strcmp( argv[ index ], "-test" ) )
+ background = 0;
+ else
+ usage( argv[ 0 ] );
+ }
+
+ /* Optionally detatch ourselves from the controlling tty */
+
+ if ( background )
+ {
+ if ( fork() )
+ return 0;
+ setsid();
+ miracle_log_init( log_syslog, LOG_INFO );
+ }
+ else
+ {
+ miracle_log_init( log_stderr, LOG_DEBUG );
+ }
+
+ atexit( main_cleanup );
+
+ /* Set the config script */
+ miracle_server_set_config( server, "/etc/miracle.conf" );
+
+ /* Execute the server */
+ error = miracle_server_execute( server );
+
+ /* We need to wait until we're exited.. */
+ while ( !server->shutdown )
+ nanosleep( &tm, NULL );
+
+ return error;
+}
--- /dev/null
+/*
+ * global_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <pthread.h>
+
+#include "miracle_unit.h"
+#include "miracle_commands.h"
+#include "miracle_log.h"
+
+static miracle_unit g_units[MAX_UNITS];
+
+
+/** Return the miracle_unit given a numeric index.
+*/
+
+miracle_unit miracle_get_unit( int n )
+{
+ if (n < MAX_UNITS)
+ return g_units[n];
+ else
+ return NULL;
+}
+
+/** Destroy the miracle_unit given its numeric index.
+*/
+
+void miracle_delete_unit( int n )
+{
+ if (n < MAX_UNITS)
+ {
+ miracle_unit unit = miracle_get_unit(n);
+ if (unit != NULL)
+ {
+ miracle_unit_close( unit );
+ g_units[ n ] = NULL;
+ miracle_log( LOG_NOTICE, "Deleted unit U%d.", n );
+ }
+ }
+}
+
+/** Destroy all allocated units on the server.
+*/
+
+void miracle_delete_all_units( void )
+{
+ int i;
+ for (i = 0; i < MAX_UNITS; i++)
+ {
+ if ( miracle_get_unit(i) != NULL )
+ {
+ miracle_unit_close( miracle_get_unit(i) );
+ miracle_log( LOG_NOTICE, "Deleted unit U%d.", i );
+ }
+ }
+}
+
+/** Add a DV virtual vtr to the server.
+*/
+response_codes miracle_add_unit( command_argument cmd_arg )
+{
+ int i = 0;
+ for ( i = 0; i < MAX_UNITS; i ++ )
+ if ( g_units[ i ] == NULL )
+ break;
+
+ if ( i < MAX_UNITS )
+ {
+ char *arg = cmd_arg->argument;
+ g_units[ i ] = miracle_unit_init( i, arg );
+ if ( g_units[ i ] != NULL )
+ {
+ miracle_unit_set_notifier( g_units[ i ], valerie_parser_get_notifier( cmd_arg->parser ), cmd_arg->root_dir );
+ valerie_response_printf( cmd_arg->response, 10, "U%1d\n\n", i );
+ }
+ return g_units[ i ] != NULL ? RESPONSE_SUCCESS_N : RESPONSE_ERROR;
+ }
+ valerie_response_printf( cmd_arg->response, 1024, "no more units can be created\n\n" );
+
+ return RESPONSE_ERROR;
+}
+
+
+/** List all AV/C nodes on the bus.
+*/
+response_codes miracle_list_nodes( command_argument cmd_arg )
+{
+ response_codes error = RESPONSE_SUCCESS_N;
+ return error;
+}
+
+
+/** List units already added to server.
+*/
+response_codes miracle_list_units( command_argument cmd_arg )
+{
+ response_codes error = RESPONSE_SUCCESS_N;
+ int i = 0;
+
+ for ( i = 0; i < MAX_UNITS; i ++ )
+ {
+ miracle_unit unit = miracle_get_unit( i );
+ if ( unit != NULL )
+ {
+ mlt_properties properties = unit->properties;
+ char *constructor = mlt_properties_get( properties, "constructor" );
+ int node = mlt_properties_get_int( properties, "node" );
+ int online = !mlt_properties_get_int( properties, "offline" );
+ valerie_response_printf( cmd_arg->response, 1024, "U%d %02d %s %d\n", i, node, constructor, online );
+ }
+ }
+ valerie_response_printf( cmd_arg->response, 1024, "\n" );
+
+ return error;
+}
+
+static int filter_files( const struct dirent *de )
+{
+ return de->d_name[ 0 ] != '.';
+}
+
+/** List clips in a directory.
+*/
+response_codes miracle_list_clips( command_argument cmd_arg )
+{
+ response_codes error = RESPONSE_BAD_FILE;
+ const char *dir_name = (const char*) cmd_arg->argument;
+ DIR *dir;
+ char fullname[1024];
+ struct dirent **de = NULL;
+ int i, n;
+
+ snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, dir_name );
+ dir = opendir( fullname );
+ if (dir != NULL)
+ {
+ struct stat info;
+ error = RESPONSE_SUCCESS_N;
+ n = scandir( fullname, &de, filter_files, alphasort );
+ for (i = 0; i < n; i++ )
+ {
+ snprintf( fullname, 1023, "%s%s/%s", cmd_arg->root_dir, dir_name, de[i]->d_name );
+ if ( stat( fullname, &info ) == 0 && S_ISDIR( info.st_mode ) )
+ valerie_response_printf( cmd_arg->response, 1024, "\"%s/\"\n", de[i]->d_name );
+ }
+ for (i = 0; i < n; i++ )
+ {
+ snprintf( fullname, 1023, "%s%s/%s", cmd_arg->root_dir, dir_name, de[i]->d_name );
+ if ( lstat( fullname, &info ) == 0 &&
+ ( S_ISREG( info.st_mode ) || S_ISLNK( info.st_mode ) || ( strstr( fullname, ".clip" ) && info.st_mode | S_IXUSR ) ) )
+ valerie_response_printf( cmd_arg->response, 1024, "\"%s\" %llu\n", de[i]->d_name, (unsigned long long) info.st_size );
+ free( de[ i ] );
+ }
+ free( de );
+ closedir( dir );
+ valerie_response_write( cmd_arg->response, "\n", 1 );
+ }
+
+ return error;
+}
+
+/** Set a server configuration property.
+*/
+
+response_codes miracle_set_global_property( command_argument cmd_arg )
+{
+ char *key = (char*) cmd_arg->argument;
+ char *value = NULL;
+
+ value = strchr( key, '=' );
+ if (value == NULL)
+ return RESPONSE_OUT_OF_RANGE;
+ *value = 0;
+ value++;
+ miracle_log( LOG_DEBUG, "SET %s = %s", key, value );
+
+ if ( strncasecmp( key, "root", 1024) == 0 )
+ {
+ int len = strlen(value);
+ int i;
+
+ /* stop all units and unload clips */
+ for (i = 0; i < MAX_UNITS; i++)
+ {
+ if (g_units[i] != NULL)
+ miracle_unit_terminate( g_units[i] );
+ }
+
+ /* set the property */
+ strncpy( cmd_arg->root_dir, value, 1023 );
+
+ /* add a trailing slash if needed */
+ if ( len && cmd_arg->root_dir[ len - 1 ] != '/')
+ {
+ cmd_arg->root_dir[ len ] = '/';
+ cmd_arg->root_dir[ len + 1 ] = '\0';
+ }
+ }
+ else
+ return RESPONSE_OUT_OF_RANGE;
+
+ return RESPONSE_SUCCESS;
+}
+
+/** Get a server configuration property.
+*/
+
+response_codes miracle_get_global_property( command_argument cmd_arg )
+{
+ char *key = (char*) cmd_arg->argument;
+
+ if ( strncasecmp( key, "root", 1024) == 0 )
+ {
+ valerie_response_write( cmd_arg->response, cmd_arg->root_dir, strlen(cmd_arg->root_dir) );
+ return RESPONSE_SUCCESS_1;
+ }
+ else
+ return RESPONSE_OUT_OF_RANGE;
+
+ return RESPONSE_SUCCESS;
+}
+
+
--- /dev/null
+/*
+ * global_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _GLOBAL_COMMANDS_H_
+#define _GLOBAL_COMMANDS_H_
+
+#include <valerie/valerie_status.h>
+#include "miracle_unit.h"
+#include "miracle_connection.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern miracle_unit miracle_get_unit( int );
+extern void miracle_delete_unit( int );
+extern void miracle_delete_all_units( void );
+extern int miracle_unit_status( int n, valerie_status status, int root_offset );
+//extern void raw1394_start_service_threads( void );
+//extern void raw1394_stop_service_threads( void );
+
+extern response_codes miracle_add_unit( command_argument );
+extern response_codes miracle_list_nodes( command_argument );
+extern response_codes miracle_list_units( command_argument );
+extern response_codes miracle_list_clips( command_argument );
+extern response_codes miracle_set_global_property( command_argument );
+extern response_codes miracle_get_global_property( command_argument );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * miracle_connection.c -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <valerie/valerie_socket.h>
+
+/* Application header files */
+#include "miracle_commands.h"
+#include "miracle_connection.h"
+#include "miracle_server.h"
+#include "miracle_log.h"
+
+/** This is a generic replacement for fgets which operates on a file
+ descriptor. Unlike fgets, we can also specify a line terminator. Maximum
+ of (max - 1) chars can be read into buf from fd. If we reach the
+ end-of-file, *eof_chk is set to 1.
+*/
+
+int fdgetline( int fd, char *buf, int max, char line_terminator, int *eof_chk )
+{
+ int count = 0;
+ char tmp [1];
+ *eof_chk = 0;
+
+ if (fd)
+ while (count < max - 1) {
+ if (read (fd, tmp, 1) > 0) {
+ if (tmp [0] != line_terminator)
+ buf [count++] = tmp [0];
+ else
+ break;
+
+/* Is it an EOF character (ctrl-D, i.e. ascii 4)? If so we definitely want
+ to break. */
+
+ if (tmp [0] == 4) {
+ *eof_chk = 1;
+ break;
+ }
+ } else {
+ *eof_chk = 1;
+ break;
+ }
+ }
+
+ buf [count] = '\0';
+
+ return count;
+}
+
+static int connection_initiate( int );
+static int connection_send( int, valerie_response );
+static int connection_read( int, char *, int );
+static void connection_close( int );
+
+static int connection_initiate( int fd )
+{
+ int error = 0;
+ valerie_response response = valerie_response_init( );
+ valerie_response_set_error( response, 100, "VTR Ready" );
+ error = connection_send( fd, response );
+ valerie_response_close( response );
+ return error;
+}
+
+static int connection_send( int fd, valerie_response response )
+{
+ int error = 0;
+ int index = 0;
+ int code = valerie_response_get_error_code( response );
+
+ if ( code != -1 )
+ {
+ int items = valerie_response_count( response );
+
+ if ( items == 0 )
+ valerie_response_set_error( response, 500, "Unknown error" );
+
+ if ( code == 200 && items > 2 )
+ valerie_response_set_error( response, 201, "OK" );
+ else if ( code == 200 && items > 1 )
+ valerie_response_set_error( response, 202, "OK" );
+
+ code = valerie_response_get_error_code( response );
+ items = valerie_response_count( response );
+
+ for ( index = 0; !error && index < items; index ++ )
+ {
+ char *line = valerie_response_get_line( response, index );
+ int length = strlen( line );
+ if ( length == 0 && index != valerie_response_count( response ) - 1 && write( fd, " ", 1 ) != 1 )
+ error = -1;
+ else if ( length > 0 && write( fd, line, length ) != length )
+ error = -1;
+ if ( write( fd, "\r\n", 2 ) != 2 )
+ error = -1;
+ }
+
+ if ( ( code == 201 || code == 500 ) && strcmp( valerie_response_get_line( response, items - 1 ), "" ) )
+ if ( write( fd, "\r\n", 2 ) != 2 )
+ miracle_log( LOG_ERR, "write(\"\\r\\n\") failed!" );
+ }
+ else
+ {
+ const char *message = "500 Empty Response\r\n\r\n";
+ if ( write( fd, message, strlen( message ) ) != strlen( message ))
+ miracle_log( LOG_ERR, "write(%s) failed!", message );
+ }
+
+ return error;
+}
+
+static int connection_read( int fd, char *command, int length )
+{
+ int eof_chk;
+ int nchars = fdgetline( fd, command, length, '\n', &eof_chk );
+ char *cr = strchr( command, '\r');
+ if ( cr != NULL )
+ cr[0] = '\0';
+ if ( eof_chk || strncasecmp( command, "BYE", 3 ) == 0 )
+ nchars = 0;
+ return nchars;
+}
+
+int connection_status( int fd, valerie_notifier notifier )
+{
+ int error = 0;
+ int index = 0;
+ valerie_status_t status;
+ char text[ 10240 ];
+ valerie_socket socket = valerie_socket_init_fd( fd );
+
+ for ( index = 0; !error && index < MAX_UNITS; index ++ )
+ {
+ valerie_notifier_get( notifier, &status, index );
+ valerie_status_serialise( &status, text, sizeof( text ) );
+ error = valerie_socket_write_data( socket, text, strlen( text ) ) != strlen( text );
+ }
+
+ while ( !error )
+ {
+ if ( valerie_notifier_wait( notifier, &status ) == 0 )
+ {
+ valerie_status_serialise( &status, text, sizeof( text ) );
+ error = valerie_socket_write_data( socket, text, strlen( text ) ) != strlen( text );
+ }
+ else
+ {
+ struct timeval tv = { 0, 0 };
+ fd_set rfds;
+
+ FD_ZERO( &rfds );
+ FD_SET( fd, &rfds );
+
+ if ( select( socket->fd + 1, &rfds, NULL, NULL, &tv ) )
+ error = 1;
+ }
+ }
+
+ valerie_socket_close( socket );
+
+ return error;
+}
+
+static void connection_close( int fd )
+{
+ close( fd );
+}
+
+void *parser_thread( void *arg )
+{
+ struct hostent *he;
+ connection_t *connection = arg;
+ mlt_properties owner = connection->owner;
+ char address[ 512 ];
+ char command[ 1024 ];
+ int fd = connection->fd;
+ valerie_parser parser = connection->parser;
+ valerie_response response = NULL;
+
+ /* Get the connecting clients ip information */
+ he = gethostbyaddr( (char *) &( connection->sin.sin_addr.s_addr ), sizeof(u_int32_t), AF_INET);
+ if ( he != NULL )
+ strcpy( address, he->h_name );
+ else
+ inet_ntop( AF_INET, &( connection->sin.sin_addr.s_addr), address, 32 );
+
+ miracle_log( LOG_NOTICE, "Connection established with %s (%d)", address, fd );
+
+ /* Execute the commands received. */
+ if ( connection_initiate( fd ) == 0 )
+ {
+ int error = 0;
+
+ while( !error && connection_read( fd, command, 1024 ) )
+ {
+ response = NULL;
+
+ if ( !strncmp( command, "PUSH ", 5 ) )
+ {
+ char temp[ 20 ];
+ int bytes;
+ char *buffer = NULL;
+ int total = 0;
+ mlt_service service = NULL;
+
+ connection_read( fd, temp, 20 );
+ bytes = atoi( temp );
+ buffer = malloc( bytes + 1 );
+ while ( total < bytes )
+ {
+ int count = read( fd, buffer + total, bytes - total );
+ if ( count >= 0 )
+ total += count;
+ else
+ break;
+ }
+ buffer[ bytes ] = '\0';
+ if ( bytes > 0 && total == bytes )
+ {
+ if ( mlt_properties_get( owner, "push-parser-off" ) == 0 )
+ {
+ service = ( mlt_service )mlt_factory_producer( NULL, "westley-xml", buffer );
+ mlt_events_fire( owner, "push-received", &response, command, service, NULL );
+ if ( response == NULL )
+ response = valerie_parser_push( parser, command, service );
+ }
+ else
+ {
+ response = valerie_parser_received( parser, command, buffer );
+ }
+ }
+ error = connection_send( fd, response );
+ valerie_response_close( response );
+ mlt_service_close( service );
+ free( buffer );
+ }
+ else if ( strncmp( command, "STATUS", 6 ) )
+ {
+ mlt_events_fire( owner, "command-received", &response, command, NULL );
+ if ( response == NULL )
+ response = valerie_parser_execute( parser, command );
+ miracle_log( LOG_INFO, "%s \"%s\" %d", address, command, valerie_response_get_error_code( response ) );
+ error = connection_send( fd, response );
+ valerie_response_close( response );
+ }
+ else
+ {
+ error = connection_status( fd, valerie_parser_get_notifier( parser ) );
+ }
+ }
+ }
+
+ /* Free the resources associated with this connection. */
+ connection_close( fd );
+
+ miracle_log( LOG_NOTICE, "Connection with %s (%d) closed", address, fd );
+
+ free( connection );
+
+ return NULL;
+}
--- /dev/null
+/*
+ * miracle_connection.h -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_CONNECTION_H_
+#define _DV_CONNECTION_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <valerie/valerie_parser.h>
+#include <valerie/valerie_tokeniser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Connection structure
+*/
+
+typedef struct
+{
+ mlt_properties owner;
+ int fd;
+ struct sockaddr_in sin;
+ valerie_parser parser;
+}
+connection_t;
+
+/** Enumeration for responses.
+*/
+
+typedef enum
+{
+ RESPONSE_SUCCESS = 200,
+ RESPONSE_SUCCESS_N = 201,
+ RESPONSE_SUCCESS_1 = 202,
+ RESPONSE_UNKNOWN_COMMAND = 400,
+ RESPONSE_TIMEOUT = 401,
+ RESPONSE_MISSING_ARG = 402,
+ RESPONSE_INVALID_UNIT = 403,
+ RESPONSE_BAD_FILE = 404,
+ RESPONSE_OUT_OF_RANGE = 405,
+ RESPONSE_TOO_MANY_FILES = 406,
+ RESPONSE_ERROR = 500
+}
+response_codes;
+
+/* the following struct is passed as the single argument
+ to all command callback functions */
+
+typedef struct
+{
+ valerie_parser parser;
+ valerie_response response;
+ valerie_tokeniser tokeniser;
+ char *command;
+ int unit;
+ void *argument;
+ char *root_dir;
+}
+command_argument_t, *command_argument;
+
+/* A handler is defined as follows. */
+typedef int (*command_handler_t) ( command_argument );
+
+
+extern void *parser_thread( void *arg );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * miracle_local.c -- Local Miracle Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+/* Needed for backtrace on linux */
+#ifdef linux
+#include <execinfo.h>
+#endif
+
+/* Valerie header files */
+#include <valerie/valerie_util.h>
+
+/* MLT header files. */
+#include <framework/mlt_factory.h>
+
+/* Application header files */
+#include "miracle_local.h"
+#include "miracle_connection.h"
+#include "miracle_commands.h"
+#include "miracle_unit_commands.h"
+#include "miracle_log.h"
+
+/** Private miracle_local structure.
+*/
+
+typedef struct
+{
+ valerie_parser parser;
+ char root_dir[1024];
+}
+*miracle_local, miracle_local_t;
+
+/** Forward declarations.
+*/
+
+static valerie_response miracle_local_connect( miracle_local );
+static valerie_response miracle_local_execute( miracle_local, char * );
+static valerie_response miracle_local_push( miracle_local, char *, mlt_service );
+static valerie_response miracle_local_receive( miracle_local, char *, char * );
+static void miracle_local_close( miracle_local );
+response_codes miracle_help( command_argument arg );
+response_codes miracle_run( command_argument arg );
+response_codes miracle_shutdown( command_argument arg );
+
+/** DV Parser constructor.
+*/
+
+valerie_parser miracle_parser_init_local( )
+{
+ valerie_parser parser = malloc( sizeof( valerie_parser_t ) );
+ miracle_local local = malloc( sizeof( miracle_local_t ) );
+
+ if ( parser != NULL )
+ {
+ memset( parser, 0, sizeof( valerie_parser_t ) );
+
+ parser->connect = (parser_connect)miracle_local_connect;
+ parser->execute = (parser_execute)miracle_local_execute;
+ parser->push = (parser_push)miracle_local_push;
+ parser->received = (parser_received)miracle_local_receive;
+ parser->close = (parser_close)miracle_local_close;
+ parser->real = local;
+
+ if ( local != NULL )
+ {
+ memset( local, 0, sizeof( miracle_local_t ) );
+ local->parser = parser;
+ local->root_dir[0] = '/';
+ }
+
+ // Construct the factory
+ mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
+ }
+ return parser;
+}
+
+/** response status code/message pair
+*/
+
+typedef struct
+{
+ int code;
+ const char *message;
+}
+responses_t;
+
+/** response messages
+*/
+
+static responses_t responses [] =
+{
+ {RESPONSE_SUCCESS, "OK"},
+ {RESPONSE_SUCCESS_N, "OK"},
+ {RESPONSE_SUCCESS_1, "OK"},
+ {RESPONSE_UNKNOWN_COMMAND, "Unknown command"},
+ {RESPONSE_TIMEOUT, "Operation timed out"},
+ {RESPONSE_MISSING_ARG, "Argument missing"},
+ {RESPONSE_INVALID_UNIT, "Unit not found"},
+ {RESPONSE_BAD_FILE, "Failed to locate or open clip"},
+ {RESPONSE_OUT_OF_RANGE, "Argument value out of range"},
+ {RESPONSE_TOO_MANY_FILES, "Too many files open"},
+ {RESPONSE_ERROR, "Server Error"}
+};
+
+/** Argument types.
+*/
+
+typedef enum
+{
+ ATYPE_NONE,
+ ATYPE_FLOAT,
+ ATYPE_STRING,
+ ATYPE_INT,
+ ATYPE_PAIR
+}
+arguments_types;
+
+/** A command definition.
+*/
+
+typedef struct
+{
+/* The command string corresponding to this operation (e.g. "play") */
+ const char *command;
+/* The function associated with it */
+ response_codes (*operation) ( command_argument );
+/* a boolean to indicate if this is a unit or global command
+ unit commands require a unit identifier as first argument */
+ int is_unit;
+/* What type is the argument (RTTI :-) ATYPE_whatever */
+ int type;
+/* online help information */
+ const char *help;
+}
+command_t;
+
+/* The following define the queue of commands available to the user. The
+ first entry is the name of the command (the string which must be typed),
+ the second command is the function associated with it, the third argument
+ is for the type of the argument, and the last argument specifies whether
+ this is something which should be handled immediately or whether it
+ should be queued (only robot motion commands need to be queued). */
+
+static command_t vocabulary[] =
+{
+ {"BYE", NULL, 0, ATYPE_NONE, "Terminates the session. Units are not removed and task queue is not flushed."},
+ {"HELP", miracle_help, 0, ATYPE_NONE, "Display this information!"},
+ {"NLS", miracle_list_nodes, 0, ATYPE_NONE, "List the AV/C nodes on the 1394 bus."},
+ {"UADD", miracle_add_unit, 0, ATYPE_STRING, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."},
+ {"ULS", miracle_list_units, 0, ATYPE_NONE, "Lists the units that have already been added to the server."},
+ {"CLS", miracle_list_clips, 0, ATYPE_STRING, "Lists the clips at directory name argument."},
+ {"SET", miracle_set_global_property, 0, ATYPE_PAIR, "Set a server configuration property."},
+ {"GET", miracle_get_global_property, 0, ATYPE_STRING, "Get a server configuration property."},
+ {"RUN", miracle_run, 0, ATYPE_STRING, "Run a batch file." },
+ {"LIST", miracle_list, 1, ATYPE_NONE, "List the playlist associated to a unit."},
+ {"LOAD", miracle_load, 1, ATYPE_STRING, "Load clip specified in absolute filename argument."},
+ {"INSERT", miracle_insert, 1, ATYPE_STRING, "Insert a clip at the given clip index."},
+ {"REMOVE", miracle_remove, 1, ATYPE_NONE, "Remove a clip at the given clip index."},
+ {"CLEAN", miracle_clean, 1, ATYPE_NONE, "Clean a unit by removing all but the currently playing clip."},
+ {"WIPE", miracle_wipe, 1, ATYPE_NONE, "Clean a unit by removing everything before the currently playing clip."},
+ {"CLEAR", miracle_clear, 1, ATYPE_NONE, "Clear a unit by removing all clips."},
+ {"MOVE", miracle_move, 1, ATYPE_INT, "Move a clip to another clip index."},
+ {"APND", miracle_append, 1, ATYPE_STRING, "Append a clip specified in absolute filename argument."},
+ {"PLAY", miracle_play, 1, ATYPE_NONE, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
+ {"STOP", miracle_stop, 1, ATYPE_NONE, "Stop a loaded and playing clip."},
+ {"PAUSE", miracle_pause, 1, ATYPE_NONE, "Pause a playing clip."},
+ {"REW", miracle_rewind, 1, ATYPE_NONE, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
+ {"FF", miracle_ff, 1, ATYPE_NONE, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
+ {"STEP", miracle_step, 1, ATYPE_INT, "Step argument number of frames forward or backward."},
+ {"GOTO", miracle_goto, 1, ATYPE_INT, "Jump to frame number supplied as argument."},
+ {"SIN", miracle_set_in_point, 1, ATYPE_INT, "Set the IN point of the loaded clip to frame number argument. -1 = reset in point to 0"},
+ {"SOUT", miracle_set_out_point, 1, ATYPE_INT, "Set the OUT point of the loaded clip to frame number argument. -1 = reset out point to maximum."},
+ {"USTA", miracle_get_unit_status, 1, ATYPE_NONE, "Report information about the unit."},
+ {"USET", miracle_set_unit_property, 1, ATYPE_PAIR, "Set a unit configuration property."},
+ {"UGET", miracle_get_unit_property, 1, ATYPE_STRING, "Get a unit configuration property."},
+ {"XFER", miracle_transfer, 1, ATYPE_STRING, "Transfer the unit's clip to another unit specified as argument."},
+ {"SHUTDOWN", miracle_shutdown, 0, ATYPE_NONE, "Shutdown the server."},
+ {NULL, NULL, 0, ATYPE_NONE, NULL}
+};
+
+/** Usage message
+*/
+
+static char helpstr [] =
+ "Miracle -- A Multimedia Playout Server\n"
+ " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
+ " Authors:\n"
+ " Dan Dennedy <dan@dennedy.org>\n"
+ " Charles Yates <charles.yates@pandora.be>\n"
+ "Available commands:\n";
+
+/** Lookup the response message for a status code.
+*/
+
+inline const char *get_response_msg( int code )
+{
+ int i = 0;
+ for ( i = 0; responses[ i ].message != NULL && code != responses[ i ].code; i ++ ) ;
+ return responses[ i ].message;
+}
+
+/** Tell the user the miracle command set
+*/
+
+response_codes miracle_help( command_argument cmd_arg )
+{
+ int i = 0;
+
+ valerie_response_printf( cmd_arg->response, 10240, "%s", helpstr );
+
+ for ( i = 0; vocabulary[ i ].command != NULL; i ++ )
+ valerie_response_printf( cmd_arg->response, 1024,
+ "%-10.10s%s\n",
+ vocabulary[ i ].command,
+ vocabulary[ i ].help );
+
+ valerie_response_printf( cmd_arg->response, 2, "\n" );
+
+ return RESPONSE_SUCCESS_N;
+}
+
+/** Execute a batch file.
+*/
+
+response_codes miracle_run( command_argument cmd_arg )
+{
+ valerie_response temp = valerie_parser_run( cmd_arg->parser, (char *)cmd_arg->argument );
+
+ if ( temp != NULL )
+ {
+ int index = 0;
+
+ valerie_response_set_error( cmd_arg->response,
+ valerie_response_get_error_code( temp ),
+ valerie_response_get_error_string( temp ) );
+
+ for ( index = 1; index < valerie_response_count( temp ); index ++ )
+ valerie_response_printf( cmd_arg->response, 10240, "%s\n", valerie_response_get_line( temp, index ) );
+
+ valerie_response_close( temp );
+ }
+
+ return valerie_response_get_error_code( cmd_arg->response );
+}
+
+response_codes miracle_shutdown( command_argument cmd_arg )
+{
+ exit( 0 );
+ return RESPONSE_SUCCESS;
+}
+
+/** Processes 'thread' id
+*/
+
+static pthread_t self;
+
+/* Signal handler to deal with various shutdown signals. Basically this
+ should clean up and power down the motor. Note that the death of any
+ child thread will kill all thrads. */
+
+void signal_handler( int sig )
+{
+ if ( pthread_equal( self, pthread_self( ) ) )
+ {
+
+#ifdef _GNU_SOURCE
+ miracle_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) );
+#else
+ miracle_log( LOG_DEBUG, "Received signal %i - shutting down.", sig );
+#endif
+
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static void sigsegv_handler()
+{
+#ifdef linux
+ void *array[ 10 ];
+ size_t size;
+ char **strings;
+ size_t i;
+
+ miracle_log( LOG_CRIT, "\a\nMiracle experienced a segmentation fault.\n"
+ "Dumping stack from the offending thread\n\n" );
+ size = backtrace( array, 10 );
+ strings = backtrace_symbols( array, size );
+
+ miracle_log( LOG_CRIT, "Obtained %zd stack frames.\n", size );
+
+ for ( i = 0; i < size; i++ )
+ miracle_log( LOG_CRIT, "%s", strings[ i ] );
+
+ free( strings );
+
+ miracle_log( LOG_CRIT, "\nDone dumping - exiting.\n" );
+#else
+ miracle_log( LOG_CRIT, "\a\nMiracle experienced a segmentation fault.\n" );
+#endif
+ exit( EXIT_FAILURE );
+}
+
+
+
+/** Local 'connect' function.
+*/
+
+static valerie_response miracle_local_connect( miracle_local local )
+{
+ valerie_response response = valerie_response_init( );
+
+ self = pthread_self( );
+
+ valerie_response_set_error( response, 100, "VTR Ready" );
+
+ signal( SIGHUP, signal_handler );
+ signal( SIGINT, signal_handler );
+ signal( SIGTERM, SIG_DFL );
+ signal( SIGSTOP, signal_handler );
+ signal( SIGPIPE, signal_handler );
+ signal( SIGALRM, signal_handler );
+ signal( SIGCHLD, SIG_IGN );
+ if ( getenv( "MLT_SIGSEGV" ) )
+ signal( SIGSEGV, sigsegv_handler );
+
+ return response;
+}
+
+/** Set the error and determine the message associated to this command.
+*/
+
+void miracle_command_set_error( command_argument cmd, response_codes code )
+{
+ valerie_response_set_error( cmd->response, code, get_response_msg( code ) );
+}
+
+/** Parse the unit argument.
+*/
+
+int miracle_command_parse_unit( command_argument cmd, int argument )
+{
+ int unit = -1;
+ char *string = valerie_tokeniser_get_string( cmd->tokeniser, argument );
+ if ( string != NULL && ( string[ 0 ] == 'U' || string[ 0 ] == 'u' ) && strlen( string ) > 1 )
+ unit = atoi( string + 1 );
+ return unit;
+}
+
+/** Parse a normal argument.
+*/
+
+void *miracle_command_parse_argument( command_argument cmd, int argument, arguments_types type, char *command )
+{
+ void *ret = NULL;
+ char *value = valerie_tokeniser_get_string( cmd->tokeniser, argument );
+
+ if ( value != NULL )
+ {
+ switch( type )
+ {
+ case ATYPE_NONE:
+ break;
+
+ case ATYPE_FLOAT:
+ ret = malloc( sizeof( float ) );
+ if ( ret != NULL )
+ *( float * )ret = atof( value );
+ break;
+
+ case ATYPE_STRING:
+ ret = strdup( value );
+ break;
+
+ case ATYPE_PAIR:
+ if ( strchr( command, '=' ) )
+ {
+ char *ptr = strchr( command, '=' );
+ while ( *( ptr - 1 ) != ' ' )
+ ptr --;
+ ret = strdup( ptr );
+ ptr = ret;
+ while( ptr[ strlen( ptr ) - 1 ] == ' ' )
+ ptr[ strlen( ptr ) - 1 ] = '\0';
+ }
+ break;
+
+ case ATYPE_INT:
+ ret = malloc( sizeof( int ) );
+ if ( ret != NULL )
+ *( int * )ret = atoi( value );
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/** Get the error code - note that we simply the success return.
+*/
+
+response_codes miracle_command_get_error( command_argument cmd )
+{
+ response_codes ret = valerie_response_get_error_code( cmd->response );
+ if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 )
+ ret = RESPONSE_SUCCESS;
+ return ret;
+}
+
+/** Execute the command.
+*/
+
+static valerie_response miracle_local_execute( miracle_local local, char *command )
+{
+ command_argument_t cmd;
+ cmd.parser = local->parser;
+ cmd.response = valerie_response_init( );
+ cmd.tokeniser = valerie_tokeniser_init( );
+ cmd.command = command;
+ cmd.unit = -1;
+ cmd.argument = NULL;
+ cmd.root_dir = local->root_dir;
+
+ /* Set the default error */
+ miracle_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND );
+
+ /* Parse the command */
+ if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
+ {
+ int index = 0;
+ char *value = valerie_tokeniser_get_string( cmd.tokeniser, 0 );
+ int found = 0;
+
+ /* Strip quotes from all tokens */
+ for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
+ valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
+
+ /* Search the vocabulary array for value */
+ for ( index = 1; !found && vocabulary[ index ].command != NULL; index ++ )
+ if ( ( found = !strcasecmp( vocabulary[ index ].command, value ) ) )
+ break;
+
+ /* If we found something, the handle the args and call the handler. */
+ if ( found )
+ {
+ int position = 1;
+
+ miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+ if ( vocabulary[ index ].is_unit )
+ {
+ cmd.unit = miracle_command_parse_unit( &cmd, position );
+ if ( cmd.unit == -1 )
+ miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+ position ++;
+ }
+
+ if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
+ {
+ cmd.argument = miracle_command_parse_argument( &cmd, position, vocabulary[ index ].type, command );
+ if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE )
+ miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+ position ++;
+ }
+
+ if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
+ {
+ response_codes error = vocabulary[ index ].operation( &cmd );
+ miracle_command_set_error( &cmd, error );
+ }
+
+ free( cmd.argument );
+ }
+ }
+
+ valerie_tokeniser_close( cmd.tokeniser );
+
+ return cmd.response;
+}
+
+static valerie_response miracle_local_receive( miracle_local local, char *command, char *doc )
+{
+ command_argument_t cmd;
+ cmd.parser = local->parser;
+ cmd.response = valerie_response_init( );
+ cmd.tokeniser = valerie_tokeniser_init( );
+ cmd.command = command;
+ cmd.unit = -1;
+ cmd.argument = NULL;
+ cmd.root_dir = local->root_dir;
+
+ /* Set the default error */
+ miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+ /* Parse the command */
+ if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
+ {
+ int index = 0;
+ int position = 1;
+
+ /* Strip quotes from all tokens */
+ for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
+ valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
+
+ cmd.unit = miracle_command_parse_unit( &cmd, position );
+ if ( cmd.unit == -1 )
+ miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+ position ++;
+
+ miracle_receive( &cmd, doc );
+ miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+ free( cmd.argument );
+ }
+
+ valerie_tokeniser_close( cmd.tokeniser );
+
+ return cmd.response;
+}
+
+static valerie_response miracle_local_push( miracle_local local, char *command, mlt_service service )
+{
+ command_argument_t cmd;
+ cmd.parser = local->parser;
+ cmd.response = valerie_response_init( );
+ cmd.tokeniser = valerie_tokeniser_init( );
+ cmd.command = command;
+ cmd.unit = -1;
+ cmd.argument = NULL;
+ cmd.root_dir = local->root_dir;
+
+ /* Set the default error */
+ miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+ /* Parse the command */
+ if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
+ {
+ int index = 0;
+ int position = 1;
+
+ /* Strip quotes from all tokens */
+ for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
+ valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
+
+ cmd.unit = miracle_command_parse_unit( &cmd, position );
+ if ( cmd.unit == -1 )
+ miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+ position ++;
+
+ miracle_push( &cmd, service );
+ miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+ free( cmd.argument );
+ }
+
+ valerie_tokeniser_close( cmd.tokeniser );
+
+ return cmd.response;
+}
+
+/** Close the parser.
+*/
+
+static void miracle_local_close( miracle_local local )
+{
+ miracle_delete_all_units();
+#ifdef linux
+ //pthread_kill_other_threads_np();
+ miracle_log( LOG_DEBUG, "Clean shutdown." );
+ //free( local );
+ //mlt_factory_close( );
+#endif
+}
--- /dev/null
+/*
+ * miracle_local.h -- Local Miracle Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MIRACLE_LOCAL_H_
+#define _MIRACLE_LOCAL_H_
+
+/* Application header files */
+#include <valerie/valerie_parser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Local parser API.
+*/
+
+extern valerie_parser miracle_parser_init_local( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * miracle_log.c -- logging facility implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdio.h>
+
+#include "miracle_log.h"
+
+static int log_output = log_stderr;
+static int threshold = LOG_DEBUG;
+
+void miracle_log_init( enum log_output method, int new_threshold )
+{
+ log_output = method;
+ threshold = new_threshold;
+ if (method == log_syslog)
+ openlog( "miracle", LOG_CONS, LOG_DAEMON );
+
+}
+
+void miracle_log( int priority, const char *format, ... )
+{
+ va_list list;
+ va_start( list, format );
+ if ( LOG_PRI(priority) <= threshold )
+ {
+ if ( log_output == log_syslog )
+ {
+ vsyslog( priority, format, list );
+ }
+ else
+ {
+ char line[1024];
+ if ( snprintf( line, 1024, "(%d) %s\n", priority, format ) != 0 )
+ vfprintf( stderr, line, list );
+ }
+ }
+ va_end( list );
+}
--- /dev/null
+/*
+ * miracle_log.h -- logging facility header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <syslog.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum log_output {
+ log_stderr,
+ log_syslog
+};
+
+void miracle_log_init( enum log_output method, int threshold );
+void miracle_log( int priority, const char *format, ... );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * miracle_server.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+/* Application header files */
+#include "miracle_server.h"
+#include "miracle_connection.h"
+#include "miracle_local.h"
+#include "miracle_log.h"
+#include "miracle_commands.h"
+#include <valerie/valerie_remote.h>
+#include <valerie/valerie_tokeniser.h>
+
+#define VERSION "0.0.1"
+
+static void miracle_command_received( mlt_listener listener, mlt_properties owner, miracle_server this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( valerie_response ** )args[ 0 ], ( char * )args[ 1 ] );
+}
+
+static void miracle_doc_received( mlt_listener listener, mlt_properties owner, miracle_server this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( valerie_response ** )args[ 0 ], ( char * )args[ 1 ], ( char * )args[ 2 ] );
+}
+
+static void miracle_push_received( mlt_listener listener, mlt_properties owner, miracle_server this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( valerie_response ** )args[ 0 ], ( char * )args[ 1 ], ( mlt_service )args[ 2 ] );
+}
+
+/** Initialise a server structure.
+*/
+
+miracle_server miracle_server_init( char *id )
+{
+ miracle_server server = malloc( sizeof( miracle_server_t ) );
+ if ( server != NULL )
+ memset( server, 0, sizeof( miracle_server_t ) );
+ if ( server != NULL && mlt_properties_init( &server->parent, server ) == 0 )
+ {
+ server->id = id;
+ server->port = DEFAULT_TCP_PORT;
+ server->socket = -1;
+ server->shutdown = 1;
+ mlt_events_init( &server->parent );
+ mlt_events_register( &server->parent, "command-received", ( mlt_transmitter )miracle_command_received );
+ mlt_events_register( &server->parent, "doc-received", ( mlt_transmitter )miracle_doc_received );
+ mlt_events_register( &server->parent, "push-received", ( mlt_transmitter )miracle_push_received );
+ }
+ return server;
+}
+
+const char *miracle_server_id( miracle_server server )
+{
+ return server != NULL && server->id != NULL ? server->id : "miracle";
+}
+
+void miracle_server_set_config( miracle_server server, const char *config )
+{
+ if ( server != NULL )
+ {
+ free( server->config );
+ server->config = config != NULL ? strdup( config ) : NULL;
+ }
+}
+
+/** Set the port of the server.
+*/
+
+void miracle_server_set_port( miracle_server server, int port )
+{
+ server->port = port;
+}
+
+void miracle_server_set_proxy( miracle_server server, char *proxy )
+{
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ server->proxy = 1;
+ server->remote_port = DEFAULT_TCP_PORT;
+ valerie_tokeniser_parse_new( tokeniser, proxy, ":" );
+ strcpy( server->remote_server, valerie_tokeniser_get_string( tokeniser, 0 ) );
+ if ( valerie_tokeniser_count( tokeniser ) == 2 )
+ server->remote_port = atoi( valerie_tokeniser_get_string( tokeniser, 1 ) );
+ valerie_tokeniser_close( tokeniser );
+}
+
+/** Wait for a connection.
+*/
+
+static int miracle_server_wait_for_connect( miracle_server server )
+{
+ struct timeval tv;
+ fd_set rfds;
+
+ /* Wait for a 1 second. */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ FD_ZERO( &rfds );
+ FD_SET( server->socket, &rfds );
+
+ return select( server->socket + 1, &rfds, NULL, NULL, &tv);
+}
+
+/** Run the server thread.
+*/
+
+static void *miracle_server_run( void *arg )
+{
+ miracle_server server = arg;
+ pthread_t cmd_parse_info;
+ connection_t *tmp = NULL;
+ pthread_attr_t thread_attributes;
+ socklen_t socksize;
+
+ socksize = sizeof( struct sockaddr );
+
+ miracle_log( LOG_NOTICE, "%s version %s listening on port %i", server->id, VERSION, server->port );
+
+ /* Create the initial thread. We want all threads to be created detached so
+ their resources get freed automatically. (CY: ... hmmph...) */
+ pthread_attr_init( &thread_attributes );
+ pthread_attr_setdetachstate( &thread_attributes, PTHREAD_CREATE_DETACHED );
+
+ while ( !server->shutdown )
+ {
+ /* Wait for a new connection. */
+ if ( miracle_server_wait_for_connect( server ) )
+ {
+ /* Create a new block of data to hold a copy of the incoming connection for
+ our server thread. The thread should free this when it terminates. */
+
+ tmp = (connection_t*) malloc( sizeof(connection_t) );
+ tmp->owner = &server->parent;
+ tmp->parser = server->parser;
+ tmp->fd = accept( server->socket, (struct sockaddr*) &(tmp->sin), &socksize );
+
+ /* Pass the connection to a parser thread :-/ */
+ if ( tmp->fd != -1 )
+ pthread_create( &cmd_parse_info, &thread_attributes, parser_thread, tmp );
+ }
+ }
+
+ miracle_log( LOG_NOTICE, "%s version %s server terminated.", server->id, VERSION );
+
+ return NULL;
+}
+
+/** Execute the server thread.
+*/
+
+int miracle_server_execute( miracle_server server )
+{
+ int error = 0;
+ valerie_response response = NULL;
+ int index = 0;
+ struct sockaddr_in ServerAddr;
+ int flag = 1;
+
+ server->shutdown = 0;
+
+ ServerAddr.sin_family = AF_INET;
+ ServerAddr.sin_port = htons( server->port );
+ ServerAddr.sin_addr.s_addr = INADDR_ANY;
+
+ /* Create socket, and bind to port. Listen there. Backlog = 5
+ should be sufficient for listen (). */
+ server->socket = socket( AF_INET, SOCK_STREAM, 0 );
+
+ if ( server->socket == -1 )
+ {
+ server->shutdown = 1;
+ perror( "socket" );
+ miracle_log( LOG_ERR, "%s unable to create socket.", server->id );
+ return -1;
+ }
+
+ setsockopt( server->socket, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof( int ) );
+
+ if ( bind( server->socket, (struct sockaddr *) &ServerAddr, sizeof (ServerAddr) ) != 0 )
+ {
+ server->shutdown = 1;
+ perror( "bind" );
+ miracle_log( LOG_ERR, "%s unable to bind to port %d.", server->id, server->port );
+ return -1;
+ }
+
+ if ( listen( server->socket, 5 ) != 0 )
+ {
+ server->shutdown = 1;
+ perror( "listen" );
+ miracle_log( LOG_ERR, "%s unable to listen on port %d.", server->id, server->port );
+ return -1;
+ }
+
+ fcntl( server->socket, F_SETFL, O_NONBLOCK );
+
+ if ( !server->proxy )
+ {
+ miracle_log( LOG_NOTICE, "Starting server on %d.", server->port );
+ server->parser = miracle_parser_init_local( );
+ }
+ else
+ {
+ miracle_log( LOG_NOTICE, "Starting proxy for %s:%d on %d.", server->remote_server, server->remote_port, server->port );
+ server->parser = valerie_parser_init_remote( server->remote_server, server->remote_port );
+ }
+
+ response = valerie_parser_connect( server->parser );
+
+ if ( response != NULL && valerie_response_get_error_code( response ) == 100 )
+ {
+ /* read configuration file */
+ if ( response != NULL && !server->proxy && server->config != NULL )
+ {
+ valerie_response_close( response );
+ response = valerie_parser_run( server->parser, server->config );
+
+ if ( valerie_response_count( response ) > 1 )
+ {
+ if ( valerie_response_get_error_code( response ) > 299 )
+ miracle_log( LOG_ERR, "Error evaluating server configuration. Processing stopped." );
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ miracle_log( LOG_DEBUG, "%4d: %s", index, valerie_response_get_line( response, index ) );
+ }
+ }
+
+ if ( response != NULL )
+ {
+ int result;
+ valerie_response_close( response );
+ result = pthread_create( &server->thread, NULL, miracle_server_run, server );
+ if ( result )
+ {
+ miracle_log( LOG_CRIT, "Failed to launch TCP listener thread" );
+ error = -1;
+ }
+ }
+ }
+ else
+ {
+ miracle_log( LOG_ERR, "Error connecting to parser. Processing stopped." );
+ server->shutdown = 1;
+ error = -1;
+ }
+
+ return error;
+}
+
+/** Fetch a units properties
+*/
+
+mlt_properties miracle_server_fetch_unit( miracle_server server, int index )
+{
+ miracle_unit unit = miracle_get_unit( index );
+ return unit != NULL ? unit->properties : NULL;
+}
+
+/** Shutdown the server.
+*/
+
+void miracle_server_shutdown( miracle_server server )
+{
+ if ( server != NULL && !server->shutdown )
+ {
+ server->shutdown = 1;
+ pthread_join( server->thread, NULL );
+ miracle_server_set_config( server, NULL );
+ valerie_parser_close( server->parser );
+ server->parser = NULL;
+ close( server->socket );
+ }
+}
+
+/** Close the server.
+*/
+
+void miracle_server_close( miracle_server server )
+{
+ if ( server != NULL && mlt_properties_dec_ref( &server->parent ) <= 0 )
+ {
+ mlt_properties_close( &server->parent );
+ miracle_server_shutdown( server );
+ free( server );
+ }
+}
--- /dev/null
+/*
+ * miracle_server.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MIRACLE_SERVER_H_
+#define _MIRACLE_SERVER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include <valerie/valerie_parser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Servers default port
+*/
+
+#define DEFAULT_TCP_PORT 5250
+
+/** Structure for the server
+*/
+
+typedef struct
+{
+ struct mlt_properties_s parent;
+ char *id;
+ int port;
+ int socket;
+ valerie_parser parser;
+ pthread_t thread;
+ int shutdown;
+ int proxy;
+ char remote_server[ 50 ];
+ int remote_port;
+ char *config;
+}
+*miracle_server, miracle_server_t;
+
+/** API for the server
+*/
+
+extern miracle_server miracle_server_init( char * );
+extern const char *miracle_server_id( miracle_server );
+extern void miracle_server_set_config( miracle_server, const char * );
+extern void miracle_server_set_port( miracle_server, int );
+extern void miracle_server_set_proxy( miracle_server, char * );
+extern int miracle_server_execute( miracle_server );
+extern mlt_properties miracle_server_fetch_unit( miracle_server, int );
+extern void miracle_server_shutdown( miracle_server );
+extern void miracle_server_close( miracle_server );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * miracle_unit.c -- Transmission Unit Implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+
+#include <sys/mman.h>
+
+#include "miracle_unit.h"
+#include "miracle_log.h"
+#include "miracle_local.h"
+
+#include <framework/mlt.h>
+
+/* Forward references */
+static void miracle_unit_status_communicate( miracle_unit );
+
+/** Allocate a new DV transmission unit.
+
+ \return A new miracle_unit handle.
+*/
+
+miracle_unit miracle_unit_init( int index, char *constructor )
+{
+ miracle_unit this = NULL;
+ mlt_consumer consumer = NULL;
+
+ char *id = strdup( constructor );
+ char *arg = strchr( id, ':' );
+
+ if ( arg != NULL )
+ *arg ++ = '\0';
+
+ consumer = mlt_factory_consumer( NULL, id, arg );
+
+ if ( consumer != NULL )
+ {
+ mlt_playlist playlist = mlt_playlist_init( );
+ this = calloc( sizeof( miracle_unit_t ), 1 );
+ this->properties = mlt_properties_new( );
+ mlt_properties_init( this->properties, this );
+ mlt_properties_set_int( this->properties, "unit", index );
+ mlt_properties_set_int( this->properties, "generation", 0 );
+ mlt_properties_set( this->properties, "constructor", constructor );
+ mlt_properties_set( this->properties, "id", id );
+ mlt_properties_set( this->properties, "arg", arg );
+ mlt_properties_set_data( this->properties, "consumer", consumer, 0, ( mlt_destructor )mlt_consumer_close, NULL );
+ mlt_properties_set_data( this->properties, "playlist", playlist, 0, ( mlt_destructor )mlt_playlist_close, NULL );
+ mlt_consumer_connect( consumer, MLT_PLAYLIST_SERVICE( playlist ) );
+ }
+
+ return this;
+}
+
+static char *strip_root( miracle_unit unit, char *file )
+{
+ mlt_properties properties = unit->properties;
+ char *root = mlt_properties_get( properties, "root" );
+ if ( file != NULL && root != NULL )
+ {
+ int length = strlen( root );
+ if ( root[ length - 1 ] == '/' )
+ length --;
+ if ( !strncmp( file, root, length ) )
+ file += length;
+ }
+ return file;
+}
+
+/** Communicate the current status to all threads waiting on the notifier.
+*/
+
+static void miracle_unit_status_communicate( miracle_unit unit )
+{
+ if ( unit != NULL )
+ {
+ mlt_properties properties = unit->properties;
+ char *root_dir = mlt_properties_get( properties, "root" );
+ valerie_notifier notifier = mlt_properties_get_data( properties, "notifier", NULL );
+ valerie_status_t status;
+
+ if ( root_dir != NULL && notifier != NULL )
+ {
+ if ( miracle_unit_get_status( unit, &status ) == 0 )
+ /* if ( !( ( status.status == unit_playing || status.status == unit_paused ) &&
+ strcmp( status.clip, "" ) &&
+ !strcmp( status.tail_clip, "" ) &&
+ status.position == 0 &&
+ status.in == 0 &&
+ status.out == 0 ) ) */
+ valerie_notifier_put( notifier, &status );
+ }
+ }
+}
+
+/** Set the notifier info
+*/
+
+void miracle_unit_set_notifier( miracle_unit this, valerie_notifier notifier, char *root_dir )
+{
+ mlt_properties properties = this->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_properties playlist_properties = MLT_PLAYLIST_PROPERTIES( playlist );
+
+ mlt_properties_set( properties, "root", root_dir );
+ mlt_properties_set_data( properties, "notifier", notifier, 0, NULL, NULL );
+ mlt_properties_set_data( playlist_properties, "notifier_arg", this, 0, NULL, NULL );
+ mlt_properties_set_data( playlist_properties, "notifier", miracle_unit_status_communicate, 0, NULL, NULL );
+
+ miracle_unit_status_communicate( this );
+}
+
+/** Create or locate a producer for the file specified.
+*/
+
+static mlt_producer locate_producer( miracle_unit unit, char *file )
+{
+ // Try to get the profile from the consumer
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ mlt_profile profile = NULL;
+
+ if ( consumer != NULL )
+ {
+ profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) );
+ }
+ return mlt_factory_producer( profile, "fezzik", file );
+}
+
+/** Update the generation count.
+*/
+
+static void update_generation( miracle_unit unit )
+{
+ mlt_properties properties = unit->properties;
+ int generation = mlt_properties_get_int( properties, "generation" );
+ mlt_properties_set_int( properties, "generation", ++ generation );
+}
+
+/** Wipe all clips on the playlist for this unit.
+*/
+
+static void clear_unit( miracle_unit unit )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_clear( playlist );
+ mlt_producer_seek( producer, 0 );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+
+ update_generation( unit );
+}
+
+/** Wipe all but the playing clip from the unit.
+*/
+
+static void clean_unit( miracle_unit unit )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_playlist_clip_info info;
+ int current = mlt_playlist_current_clip( playlist );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+ mlt_position position = mlt_producer_frame( producer );
+ double speed = mlt_producer_get_speed( producer );
+ mlt_playlist_get_clip_info( playlist, &info, current );
+
+ if ( info.producer != NULL )
+ {
+ mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( info.producer ) );
+ position -= info.start;
+ clear_unit( unit );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_append_io( playlist, info.producer, info.frame_in, info.frame_out );
+ mlt_producer_seek( producer, position );
+ mlt_producer_set_speed( producer, speed );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_producer_close( info.producer );
+ }
+
+ update_generation( unit );
+}
+
+/** Remove everything up to the current clip from the unit.
+*/
+
+static void wipe_unit( miracle_unit unit )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_playlist_clip_info info;
+ int current = mlt_playlist_current_clip( playlist );
+ mlt_playlist_get_clip_info( playlist, &info, current );
+
+ if ( info.producer != NULL && info.start > 0 )
+ {
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_remove_region( playlist, 0, info.start - 1 );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ }
+
+ update_generation( unit );
+}
+
+/** Generate a report on all loaded clips.
+*/
+
+void miracle_unit_report_list( miracle_unit unit, valerie_response response )
+{
+ int i;
+ mlt_properties properties = unit->properties;
+ int generation = mlt_properties_get_int( properties, "generation" );
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+
+ valerie_response_printf( response, 1024, "%d\n", generation );
+
+ for ( i = 0; i < mlt_playlist_count( playlist ); i ++ )
+ {
+ mlt_playlist_clip_info info;
+ char *title;
+ mlt_playlist_get_clip_info( playlist , &info, i );
+ title = mlt_properties_get( MLT_PRODUCER_PROPERTIES( info.producer ), "title" );
+ if ( title == NULL )
+ title = strip_root( unit, info.resource );
+ valerie_response_printf( response, 10240, "%d \"%s\" %d %d %d %d %.2f\n",
+ i,
+ title,
+ info.frame_in,
+ info.frame_out,
+ info.frame_count,
+ info.length,
+ info.fps );
+ }
+ valerie_response_printf( response, 1024, "\n" );
+}
+
+/** Load a clip into the unit clearing existing play list.
+
+ \todo error handling
+ \param unit A miracle_unit handle.
+ \param clip The absolute file name of the clip to load.
+ \param in The starting frame (-1 for 0)
+ \param out The ending frame (-1 for maximum)
+*/
+
+valerie_error_code miracle_unit_load( miracle_unit unit, char *clip, int32_t in, int32_t out, int flush )
+{
+ // Now try to create a producer
+ mlt_producer instance = locate_producer( unit, clip );
+
+ if ( instance != NULL )
+ {
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ int original = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_append_io( playlist, instance, in, out );
+ mlt_playlist_remove_region( playlist, 0, original );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ miracle_log( LOG_DEBUG, "loaded clip %s", clip );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ mlt_producer_close( instance );
+ return valerie_ok;
+ }
+
+ return valerie_invalid_file;
+}
+
+valerie_error_code miracle_unit_insert( miracle_unit unit, char *clip, int index, int32_t in, int32_t out )
+{
+ mlt_producer instance = locate_producer( unit, clip );
+
+ if ( instance != NULL )
+ {
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ fprintf( stderr, "inserting clip %s before %d\n", clip, index );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_insert( playlist, instance, index, in, out );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ miracle_log( LOG_DEBUG, "inserted clip %s at %d", clip, index );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ mlt_producer_close( instance );
+ return valerie_ok;
+ }
+
+ return valerie_invalid_file;
+}
+
+valerie_error_code miracle_unit_remove( miracle_unit unit, int index )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_remove( playlist, index );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ miracle_log( LOG_DEBUG, "removed clip at %d", index );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ return valerie_ok;
+}
+
+valerie_error_code miracle_unit_clean( miracle_unit unit )
+{
+ clean_unit( unit );
+ miracle_log( LOG_DEBUG, "Cleaned playlist" );
+ miracle_unit_status_communicate( unit );
+ return valerie_ok;
+}
+
+valerie_error_code miracle_unit_wipe( miracle_unit unit )
+{
+ wipe_unit( unit );
+ miracle_log( LOG_DEBUG, "Wiped playlist" );
+ miracle_unit_status_communicate( unit );
+ return valerie_ok;
+}
+
+valerie_error_code miracle_unit_clear( miracle_unit unit )
+{
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ clear_unit( unit );
+ mlt_consumer_purge( consumer );
+ miracle_log( LOG_DEBUG, "Cleared playlist" );
+ miracle_unit_status_communicate( unit );
+ return valerie_ok;
+}
+
+valerie_error_code miracle_unit_move( miracle_unit unit, int src, int dest )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_move( playlist, src, dest );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ miracle_log( LOG_DEBUG, "moved clip %d to %d", src, dest );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ return valerie_ok;
+}
+
+/** Add a clip to the unit play list.
+
+ \todo error handling
+ \param unit A miracle_unit handle.
+ \param clip The absolute file name of the clip to load.
+ \param in The starting frame (-1 for 0)
+ \param out The ending frame (-1 for maximum)
+*/
+
+valerie_error_code miracle_unit_append( miracle_unit unit, char *clip, int32_t in, int32_t out )
+{
+ mlt_producer instance = locate_producer( unit, clip );
+
+ if ( instance != NULL )
+ {
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_append_io( playlist, instance, in, out );
+ miracle_log( LOG_DEBUG, "appended clip %s", clip );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ mlt_producer_close( instance );
+ return valerie_ok;
+ }
+
+ return valerie_invalid_file;
+}
+
+/** Add an mlt_service to the playlist
+
+ \param unit A miracle_unit handle.
+ \param service the service to add
+*/
+
+valerie_error_code miracle_unit_append_service( miracle_unit unit, mlt_service service )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ mlt_playlist_append( playlist, ( mlt_producer )service );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ miracle_log( LOG_DEBUG, "appended clip" );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ return valerie_ok;
+}
+
+/** Start playing the unit.
+
+ \todo error handling
+ \param unit A miracle_unit handle.
+ \param speed An integer that specifies the playback rate as a
+ percentage multiplied by 100.
+*/
+
+void miracle_unit_play( miracle_unit_t *unit, int speed )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ mlt_producer_set_speed( producer, ( double )speed / 1000 );
+ mlt_consumer_start( consumer );
+ miracle_unit_status_communicate( unit );
+}
+
+/** Stop playback.
+
+ Terminates the dv_pump and halts dv1394 transmission.
+
+ \param unit A miracle_unit handle.
+*/
+
+void miracle_unit_terminate( miracle_unit unit )
+{
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ mlt_playlist playlist = mlt_properties_get_data( unit->properties, "playlist", NULL );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+ mlt_producer_set_speed( producer, 0 );
+ mlt_consumer_stop( consumer );
+ miracle_unit_status_communicate( unit );
+}
+
+/** Query the status of unit playback.
+
+ \param unit A miracle_unit handle.
+ \return 1 if the unit is not playing, 0 if playing.
+*/
+
+int miracle_unit_has_terminated( miracle_unit unit )
+{
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ return mlt_consumer_is_stopped( consumer );
+}
+
+/** Transfer the currently loaded clip to another unit
+*/
+
+int miracle_unit_transfer( miracle_unit dest_unit, miracle_unit src_unit )
+{
+ int i;
+ mlt_properties dest_properties = dest_unit->properties;
+ mlt_playlist dest_playlist = mlt_properties_get_data( dest_properties, "playlist", NULL );
+ mlt_properties src_properties = src_unit->properties;
+ mlt_playlist src_playlist = mlt_properties_get_data( src_properties, "playlist", NULL );
+ mlt_playlist tmp_playlist = mlt_playlist_init( );
+
+ for ( i = 0; i < mlt_playlist_count( src_playlist ); i ++ )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( src_playlist, &info, i );
+ if ( info.producer != NULL )
+ mlt_playlist_append_io( tmp_playlist, info.producer, info.frame_in, info.frame_out );
+ }
+
+ clear_unit( src_unit );
+
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( dest_playlist ) );
+
+ for ( i = 0; i < mlt_playlist_count( tmp_playlist ); i ++ )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( tmp_playlist, &info, i );
+ if ( info.producer != NULL )
+ mlt_playlist_append_io( dest_playlist, info.producer, info.frame_in, info.frame_out );
+ }
+
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( dest_playlist ) );
+
+ update_generation( dest_unit );
+ miracle_unit_status_communicate( dest_unit );
+
+ mlt_playlist_close( tmp_playlist );
+
+ return 0;
+}
+
+/** Determine if unit is offline.
+*/
+
+int miracle_unit_is_offline( miracle_unit unit )
+{
+ return 0;
+}
+
+/** Obtain the status for a given unit
+*/
+
+int miracle_unit_get_status( miracle_unit unit, valerie_status status )
+{
+ int error = unit == NULL;
+
+ memset( status, 0, sizeof( valerie_status_t ) );
+
+ if ( !error )
+ {
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+ mlt_producer clip = mlt_playlist_current( playlist );
+
+ mlt_playlist_clip_info info;
+ int clip_index = mlt_playlist_current_clip( playlist );
+ mlt_playlist_get_clip_info( playlist, &info, clip_index );
+
+ if ( info.resource != NULL && strcmp( info.resource, "" ) )
+ {
+ char *title = mlt_properties_get( MLT_PRODUCER_PROPERTIES( info.producer ), "title" );
+ if ( title == NULL )
+ title = strip_root( unit, info.resource );
+ strncpy( status->clip, title, sizeof( status->clip ) );
+ status->speed = (int)( mlt_producer_get_speed( producer ) * 1000.0 );
+ status->fps = mlt_producer_get_fps( producer );
+ status->in = info.frame_in;
+ status->out = info.frame_out;
+ status->position = mlt_producer_frame( clip );
+ status->length = mlt_producer_get_length( clip );
+ strncpy( status->tail_clip, title, sizeof( status->tail_clip ) );
+ status->tail_in = info.frame_in;
+ status->tail_out = info.frame_out;
+ status->tail_position = mlt_producer_frame( clip );
+ status->tail_length = mlt_producer_get_length( clip );
+ status->clip_index = mlt_playlist_current_clip( playlist );
+ status->seek_flag = 1;
+ }
+
+ status->generation = mlt_properties_get_int( properties, "generation" );
+
+ if ( miracle_unit_has_terminated( unit ) )
+ status->status = unit_stopped;
+ else if ( !strcmp( status->clip, "" ) )
+ status->status = unit_not_loaded;
+ else if ( status->speed == 0 )
+ status->status = unit_paused;
+ else
+ status->status = unit_playing;
+ }
+ else
+ {
+ status->status = unit_undefined;
+ }
+
+ status->unit = mlt_properties_get_int( unit->properties, "unit" );
+
+ return error;
+}
+
+/** Change position in the playlist.
+*/
+
+void miracle_unit_change_position( miracle_unit unit, int clip, int32_t position )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+ mlt_playlist_clip_info info;
+
+ if ( clip < 0 )
+ {
+ clip = 0;
+ position = 0;
+ }
+ else if ( clip >= mlt_playlist_count( playlist ) )
+ {
+ clip = mlt_playlist_count( playlist ) - 1;
+ position = INT_MAX;
+ }
+
+ if ( mlt_playlist_get_clip_info( playlist, &info, clip ) == 0 )
+ {
+ int32_t frame_start = info.start;
+ int32_t frame_offset = position;
+
+ if ( frame_offset < 0 )
+ frame_offset = info.frame_out;
+ if ( frame_offset < info.frame_in )
+ frame_offset = info.frame_in;
+ if ( frame_offset >= info.frame_out )
+ frame_offset = info.frame_out;
+
+ mlt_producer_seek( producer, frame_start + frame_offset - info.frame_in );
+ }
+
+ miracle_unit_status_communicate( unit );
+}
+
+/** Get the index of the current clip.
+*/
+
+int miracle_unit_get_current_clip( miracle_unit unit )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ int clip_index = mlt_playlist_current_clip( playlist );
+ return clip_index;
+}
+
+/** Set a clip's in point
+*/
+
+int miracle_unit_set_clip_in( miracle_unit unit, int index, int32_t position )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_playlist_clip_info info;
+ int error = mlt_playlist_get_clip_info( playlist, &info, index );
+
+ if ( error == 0 )
+ {
+ miracle_unit_play( unit, 0 );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ error = mlt_playlist_resize_clip( playlist, index, position, info.frame_out );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ update_generation( unit );
+ miracle_unit_change_position( unit, index, 0 );
+ }
+
+ return error;
+}
+
+/** Set a clip's out point.
+*/
+
+int miracle_unit_set_clip_out( miracle_unit unit, int index, int32_t position )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_playlist_clip_info info;
+ int error = mlt_playlist_get_clip_info( playlist, &info, index );
+
+ if ( error == 0 )
+ {
+ miracle_unit_play( unit, 0 );
+ mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+ error = mlt_playlist_resize_clip( playlist, index, info.frame_in, position );
+ mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+ update_generation( unit );
+ miracle_unit_status_communicate( unit );
+ miracle_unit_change_position( unit, index, -1 );
+ }
+
+ return error;
+}
+
+/** Step by specified position.
+*/
+
+void miracle_unit_step( miracle_unit unit, int32_t offset )
+{
+ mlt_properties properties = unit->properties;
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+ mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+ mlt_position position = mlt_producer_frame( producer );
+ mlt_producer_seek( producer, position + offset );
+}
+
+/** Set the unit's clip mode regarding in and out points.
+*/
+
+//void miracle_unit_set_mode( miracle_unit unit, dv_player_clip_mode mode )
+//{
+ //dv_player player = miracle_unit_get_dv_player( unit );
+ //if ( player != NULL )
+ //dv_player_set_clip_mode( player, mode );
+ //miracle_unit_status_communicate( unit );
+//}
+
+/** Get the unit's clip mode regarding in and out points.
+*/
+
+//dv_player_clip_mode miracle_unit_get_mode( miracle_unit unit )
+//{
+ //dv_player player = miracle_unit_get_dv_player( unit );
+ //return dv_player_get_clip_mode( player );
+//}
+
+/** Set the unit's clip mode regarding eof handling.
+*/
+
+//void miracle_unit_set_eof_action( miracle_unit unit, dv_player_eof_action action )
+//{
+ //dv_player player = miracle_unit_get_dv_player( unit );
+ //dv_player_set_eof_action( player, action );
+ //miracle_unit_status_communicate( unit );
+//}
+
+/** Get the unit's clip mode regarding eof handling.
+*/
+
+//dv_player_eof_action miracle_unit_get_eof_action( miracle_unit unit )
+//{
+ //dv_player player = miracle_unit_get_dv_player( unit );
+ //return dv_player_get_eof_action( player );
+//}
+
+int miracle_unit_set( miracle_unit unit, char *name_value )
+{
+ mlt_properties properties = NULL;
+
+ if ( strncmp( name_value, "consumer.", 9 ) )
+ {
+ mlt_playlist playlist = mlt_properties_get_data( unit->properties, "playlist", NULL );
+ properties = MLT_PLAYLIST_PROPERTIES( playlist );
+ }
+ else
+ {
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ properties = MLT_CONSUMER_PROPERTIES( consumer );
+ name_value += 9;
+ }
+
+ return mlt_properties_parse( properties, name_value );
+}
+
+char *miracle_unit_get( miracle_unit unit, char *name )
+{
+ mlt_playlist playlist = mlt_properties_get_data( unit->properties, "playlist", NULL );
+ mlt_properties properties = MLT_PLAYLIST_PROPERTIES( playlist );
+ return mlt_properties_get( properties, name );
+}
+
+/** Release the unit
+
+ \todo error handling
+ \param unit A miracle_unit handle.
+*/
+
+void miracle_unit_close( miracle_unit unit )
+{
+ if ( unit != NULL )
+ {
+ miracle_log( LOG_DEBUG, "closing unit..." );
+ miracle_unit_terminate( unit );
+ mlt_properties_close( unit->properties );
+ free( unit );
+ miracle_log( LOG_DEBUG, "... unit closed." );
+ }
+}
+
--- /dev/null
+/*
+ * dvunit.h -- Transmission Unit Header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_UNIT_H_
+#define _DV_UNIT_H_
+
+#include <pthread.h>
+
+#include <framework/mlt_properties.h>
+#include <valerie/valerie.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct
+{
+ mlt_properties properties;
+}
+miracle_unit_t, *miracle_unit;
+
+extern miracle_unit miracle_unit_init( int index, char *arg );
+extern void miracle_unit_report_list( miracle_unit unit, valerie_response response );
+extern void miracle_unit_allow_stdin( miracle_unit unit, int flag );
+extern valerie_error_code miracle_unit_load( miracle_unit unit, char *clip, int32_t in, int32_t out, int flush );
+extern valerie_error_code miracle_unit_insert( miracle_unit unit, char *clip, int index, int32_t in, int32_t out );
+extern valerie_error_code miracle_unit_append( miracle_unit unit, char *clip, int32_t in, int32_t out );
+extern valerie_error_code miracle_unit_append_service( miracle_unit unit, mlt_service service );
+extern valerie_error_code miracle_unit_remove( miracle_unit unit, int index );
+extern valerie_error_code miracle_unit_clean( miracle_unit unit );
+extern valerie_error_code miracle_unit_wipe( miracle_unit unit );
+extern valerie_error_code miracle_unit_clear( miracle_unit unit );
+extern valerie_error_code miracle_unit_move( miracle_unit unit, int src, int dest );
+extern int miracle_unit_transfer( miracle_unit dest_unit, miracle_unit src_unit );
+extern void miracle_unit_play( miracle_unit_t *unit, int speed );
+extern void miracle_unit_terminate( miracle_unit );
+extern int miracle_unit_has_terminated( miracle_unit );
+extern int miracle_unit_get_nodeid( miracle_unit unit );
+extern int miracle_unit_get_channel( miracle_unit unit );
+extern int miracle_unit_is_offline( miracle_unit unit );
+extern void miracle_unit_set_notifier( miracle_unit, valerie_notifier, char * );
+extern int miracle_unit_get_status( miracle_unit, valerie_status );
+extern void miracle_unit_change_position( miracle_unit, int, int32_t position );
+extern void miracle_unit_change_speed( miracle_unit unit, int speed );
+extern int miracle_unit_set_clip_in( miracle_unit unit, int index, int32_t position );
+extern int miracle_unit_set_clip_out( miracle_unit unit, int index, int32_t position );
+//extern void miracle_unit_set_mode( miracle_unit unit, dv_player_clip_mode mode );
+//extern dv_player_clip_mode miracle_unit_get_mode( miracle_unit unit );
+//extern void miracle_unit_set_eof_action( miracle_unit unit, dv_player_eof_action mode );
+//extern dv_player_eof_action miracle_unit_get_eof_action( miracle_unit unit );
+extern void miracle_unit_step( miracle_unit unit, int32_t offset );
+extern void miracle_unit_close( miracle_unit unit );
+extern void miracle_unit_suspend( miracle_unit );
+extern void miracle_unit_restore( miracle_unit );
+extern int miracle_unit_set( miracle_unit, char *name_value );
+extern char * miracle_unit_get( miracle_unit, char *name );
+extern int miracle_unit_get_current_clip( miracle_unit );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * unit_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "miracle_unit.h"
+#include "miracle_commands.h"
+#include "miracle_log.h"
+
+int miracle_load( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ char *filename = (char*) cmd_arg->argument;
+ char fullname[1024];
+ int flush = 1;
+ char *service;
+
+ if ( filename[0] == '!' )
+ {
+ flush = 0;
+ filename ++;
+ }
+
+ service = strchr( filename, ':' );
+ if ( service != NULL )
+ {
+ service = filename;
+ filename = strchr( service, ':' );
+ *filename ++ = '\0';
+
+ if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+ filename++;
+
+ snprintf( fullname, 1023, "%s:%s%s", service, cmd_arg->root_dir, filename );
+ }
+ else
+ {
+ if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+ filename++;
+
+ snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename );
+ }
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ int32_t in = -1, out = -1;
+ if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 5 )
+ {
+ in = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 3 ) );
+ out = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 4 ) );
+ }
+ if ( miracle_unit_load( unit, fullname, in, out, flush ) != valerie_ok )
+ return RESPONSE_BAD_FILE;
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_list( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit( cmd_arg->unit );
+
+ if ( unit != NULL )
+ {
+ miracle_unit_report_list( unit, cmd_arg->response );
+ return RESPONSE_SUCCESS;
+ }
+
+ return RESPONSE_INVALID_UNIT;
+}
+
+static int parse_clip( command_argument cmd_arg, int arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ int clip = miracle_unit_get_current_clip( unit );
+
+ if ( valerie_tokeniser_count( cmd_arg->tokeniser ) > arg )
+ {
+ char *token = valerie_tokeniser_get_string( cmd_arg->tokeniser, arg );
+ if ( token[ 0 ] == '+' )
+ clip += atoi( token + 1 );
+ else if ( token[ 0 ] == '-' )
+ clip -= atoi( token + 1 );
+ else
+ clip = atoi( token );
+ }
+
+ return clip;
+}
+
+int miracle_insert( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ char *filename = (char*) cmd_arg->argument;
+ char fullname[1024];
+
+ if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+ filename++;
+
+ snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename );
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ long in = -1, out = -1;
+ int index = parse_clip( cmd_arg, 3 );
+
+ if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 6 )
+ {
+ in = atoi( valerie_tokeniser_get_string( cmd_arg->tokeniser, 4 ) );
+ out = atoi( valerie_tokeniser_get_string( cmd_arg->tokeniser, 5 ) );
+ }
+
+ switch( miracle_unit_insert( unit, fullname, index, in, out ) )
+ {
+ case valerie_ok:
+ return RESPONSE_SUCCESS;
+ default:
+ return RESPONSE_BAD_FILE;
+ }
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_remove( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ int index = parse_clip( cmd_arg, 2 );
+
+ if ( miracle_unit_remove( unit, index ) != valerie_ok )
+ return RESPONSE_BAD_FILE;
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_clean( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ if ( miracle_unit_clean( unit ) != valerie_ok )
+ return RESPONSE_BAD_FILE;
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_wipe( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ if ( miracle_unit_wipe( unit ) != valerie_ok )
+ return RESPONSE_BAD_FILE;
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_clear( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ if ( miracle_unit_clear( unit ) != valerie_ok )
+ return RESPONSE_BAD_FILE;
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_move( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if ( unit != NULL )
+ {
+ if ( valerie_tokeniser_count( cmd_arg->tokeniser ) > 2 )
+ {
+ int src = parse_clip( cmd_arg, 2 );
+ int dest = parse_clip( cmd_arg, 3 );
+
+ if ( miracle_unit_move( unit, src, dest ) != valerie_ok )
+ return RESPONSE_BAD_FILE;
+ }
+ else
+ {
+ return RESPONSE_MISSING_ARG;
+ }
+ }
+ else
+ {
+ return RESPONSE_INVALID_UNIT;
+ }
+
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_append( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ char *filename = (char*) cmd_arg->argument;
+ char fullname[1024];
+
+ if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+ filename++;
+
+ snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename );
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ int32_t in = -1, out = -1;
+ if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 5 )
+ {
+ in = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 3 ) );
+ out = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 4 ) );
+ }
+ switch ( miracle_unit_append( unit, fullname, in, out ) )
+ {
+ case valerie_ok:
+ return RESPONSE_SUCCESS;
+ default:
+ return RESPONSE_BAD_FILE;
+ }
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_push( command_argument cmd_arg, mlt_service service )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ if ( unit != NULL && service != NULL )
+ if ( miracle_unit_append_service( unit, service ) == valerie_ok )
+ return RESPONSE_SUCCESS;
+ return RESPONSE_BAD_FILE;
+}
+
+int miracle_receive( command_argument cmd_arg, char *doc )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ if ( unit != NULL )
+ {
+ // Get the consumer's profile
+ mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) );
+ mlt_producer producer = mlt_factory_producer( profile, "westley-xml", doc );
+ if ( producer != NULL )
+ {
+ if ( miracle_unit_append_service( unit, MLT_PRODUCER_SERVICE( producer ) ) == valerie_ok )
+ {
+ mlt_producer_close( producer );
+ return RESPONSE_SUCCESS;
+ }
+ mlt_producer_close( producer );
+ }
+ }
+ return RESPONSE_BAD_FILE;
+}
+
+int miracle_play( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if ( unit == NULL )
+ {
+ return RESPONSE_INVALID_UNIT;
+ }
+ else
+ {
+ int speed = 1000;
+ if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 3 )
+ speed = atoi( valerie_tokeniser_get_string( cmd_arg->tokeniser, 2 ) );
+ miracle_unit_play( unit, speed );
+ }
+
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_stop( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ if ( unit == NULL )
+ return RESPONSE_INVALID_UNIT;
+ else
+ miracle_unit_terminate( unit );
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_pause( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ if ( unit == NULL )
+ return RESPONSE_INVALID_UNIT;
+ else
+ miracle_unit_play( unit, 0 );
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_rewind( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ if ( unit == NULL )
+ return RESPONSE_INVALID_UNIT;
+ else
+ miracle_unit_play( unit, -2000 );
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_step( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ miracle_unit_play( unit, 0 );
+ miracle_unit_step( unit, *(int*) cmd_arg->argument );
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_goto( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ int clip = parse_clip( cmd_arg, 3 );
+
+ if (unit == NULL || miracle_unit_is_offline(unit))
+ return RESPONSE_INVALID_UNIT;
+ else
+ miracle_unit_change_position( unit, clip, *(int*) cmd_arg->argument );
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_ff( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ if ( unit == NULL )
+ return RESPONSE_INVALID_UNIT;
+ else
+ miracle_unit_play( unit, 2000 );
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_set_in_point( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ int clip = parse_clip( cmd_arg, 3 );
+
+ if ( unit == NULL )
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ int position = *(int *) cmd_arg->argument;
+
+ switch( miracle_unit_set_clip_in( unit, clip, position ) )
+ {
+ case -1:
+ return RESPONSE_BAD_FILE;
+ case -2:
+ return RESPONSE_OUT_OF_RANGE;
+ }
+ }
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_set_out_point( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ int clip = parse_clip( cmd_arg, 3 );
+
+ if ( unit == NULL )
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ int position = *(int *) cmd_arg->argument;
+
+ switch( miracle_unit_set_clip_out( unit, clip, position ) )
+ {
+ case -1:
+ return RESPONSE_BAD_FILE;
+ case -2:
+ return RESPONSE_OUT_OF_RANGE;
+ }
+ }
+
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_get_unit_status( command_argument cmd_arg )
+{
+ valerie_status_t status;
+ int error = miracle_unit_get_status( miracle_get_unit( cmd_arg->unit ), &status );
+
+ if ( error == -1 )
+ return RESPONSE_INVALID_UNIT;
+ else
+ {
+ char text[ 10240 ];
+ valerie_response_printf( cmd_arg->response, sizeof( text ), valerie_status_serialise( &status, text, sizeof( text ) ) );
+ return RESPONSE_SUCCESS_1;
+ }
+ return 0;
+}
+
+
+int miracle_set_unit_property( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ char *name_value = (char*) cmd_arg->argument;
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else
+ miracle_unit_set( unit, name_value );
+ return RESPONSE_SUCCESS;
+}
+
+int miracle_get_unit_property( command_argument cmd_arg )
+{
+ miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+ char *name = (char*) cmd_arg->argument;
+ char *value = miracle_unit_get( unit, name );
+ if (unit == NULL)
+ return RESPONSE_INVALID_UNIT;
+ else if ( value != NULL )
+ valerie_response_printf( cmd_arg->response, 1024, "%s\n", value );
+ return RESPONSE_SUCCESS;
+}
+
+
+int miracle_transfer( command_argument cmd_arg )
+{
+ miracle_unit src_unit = miracle_get_unit(cmd_arg->unit);
+ int dest_unit_id = -1;
+ char *string = (char*) cmd_arg->argument;
+ if ( string != NULL && ( string[ 0 ] == 'U' || string[ 0 ] == 'u' ) && strlen( string ) > 1 )
+ dest_unit_id = atoi( string + 1 );
+
+ if ( src_unit != NULL && dest_unit_id != -1 )
+ {
+ miracle_unit dest_unit = miracle_get_unit( dest_unit_id );
+ if ( dest_unit != NULL && !miracle_unit_is_offline(dest_unit) && dest_unit != src_unit )
+ {
+ miracle_unit_transfer( dest_unit, src_unit );
+ return RESPONSE_SUCCESS;
+ }
+ }
+ return RESPONSE_INVALID_UNIT;
+}
--- /dev/null
+/*
+ * unit_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _UNIT_COMMANDS_H_
+#define _UNIT_COMMANDS_H_
+
+#include "miracle_connection.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern response_codes miracle_list( command_argument );
+extern response_codes miracle_load( command_argument );
+extern response_codes miracle_insert( command_argument );
+extern response_codes miracle_remove( command_argument );
+extern response_codes miracle_clean( command_argument );
+extern response_codes miracle_wipe( command_argument );
+extern response_codes miracle_clear( command_argument );
+extern response_codes miracle_move( command_argument );
+extern response_codes miracle_append( command_argument );
+extern response_codes miracle_play( command_argument );
+extern response_codes miracle_stop( command_argument );
+extern response_codes miracle_pause( command_argument );
+extern response_codes miracle_rewind( command_argument );
+extern response_codes miracle_step( command_argument );
+extern response_codes miracle_goto( command_argument );
+extern response_codes miracle_ff( command_argument );
+extern response_codes miracle_set_in_point( command_argument );
+extern response_codes miracle_set_out_point( command_argument );
+extern response_codes miracle_get_unit_status( command_argument );
+extern response_codes miracle_set_unit_property( command_argument );
+extern response_codes miracle_get_unit_property( command_argument );
+extern response_codes miracle_transfer( command_argument );
+extern response_codes miracle_push( command_argument, mlt_service );
+extern response_codes miracle_receive( command_argument, char * );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+include ../../config.mak
+include make.inc
+
+all clean depend:
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \
+ then $(MAKE) -C $$subdir $@ || exit 1; \
+ fi \
+ done
+
+distclean:
+ rm -f consumers.dat filters.dat producers.dat transitions.dat make.inc; \
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \
+ then $(MAKE) -C $$subdir $@ || exit 1; \
+ fi \
+ done
+
+install:
+ list='$(SUBDIRS)'; \
+ for subdir in $$list; do \
+ if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \
+ then $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \
+ fi \
+ done
+
+uninstall:
+ rm -rf "$(DESTDIR)$(libdir)/mlt"
+
--- /dev/null
+include ../../../config.mak
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+include config.mak
+
+LDFLAGS += -lavformat$(AVFORMAT_SUFFIX)
+LDFLAGS += -lavcodec$(AVFORMAT_SUFFIX)
+LDFLAGS += -lavutil$(AVFORMAT_SUFFIX)
+LDFLAGS += -lavdevice$(AVFORMAT_SUFFIX) $(EXTRA_LIBS)
+
+ifndef CODECS
+TARGET = ../libmltffmpeg$(LIBSUF)
+else
+TARGET = ../libmltavformat$(LIBSUF)
+endif
+
+OBJS = factory.o
+
+ifdef FILTERS
+OBJS += filter_avcolour_space.o \
+ filter_avresample.o \
+ filter_avdeinterlace.o
+ifdef SWSCALE
+OBJS += filter_swscale.o
+endif
+CFLAGS += -DFILTERS
+endif
+
+ifdef CODECS
+OBJS += producer_avformat.o \
+ consumer_avformat.o
+CFLAGS += -DCODECS
+endif
+
+ifdef SWSCALE
+CFLAGS += -DSWSCALE
+LDFLAGS += -lswscale$(AVFORMAT_SUFFIX)
+endif
+
+ifdef LOCAL_FFMPEG
+LOCAL_FFMPEG_OBJS = ffmpeg/libavformat/libavformat$(AVFORMAT_SUFFIX) \
+ ffmpeg/libavcodec/libavcodec$(AVFORMAT_SUFFIX) \
+ ffmpeg/libavutil/libavutil$(AVFORMAT_SUFFIX) \
+ ffmpeg/libavutil/libavdevice$(AVFORMAT_SUFFIX)
+endif
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(LOCAL_FFMPEG_OBJS):
+ if [ $(LOCAL_FFMPEG) ] ; then \
+ $(MAKE) -C ffmpeg ffmpeg ; \
+ fi
+
+$(TARGET): $(OBJS) $(LOCAL_FFMPEG_OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ if [ $(LOCAL_FFMPEG) ] ; then $(MAKE) -C ffmpeg dep ; fi
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ if [ $(LOCAL_FFMPEG) ] ; then $(MAKE) -C ffmpeg distclean ; fi
+ rm -f .depend
+
+clean:
+ #if [ $(LOCAL_FFMPEG) ] ; then $(MAKE) -C ffmpeg clean ; fi
+ rm -f $(OBJS) ../libmltffmpeg$(LIBSUF) ../libmltavformat$(LIBSUF)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+ install -d "$(DESTDIR)$(prefix)/share/mlt/avformat"
+ install -m 644 producer_avformat.yml "$(DESTDIR)$(prefix)/share/mlt/avformat"
+
+uninstall:
+ rm "$(DESTDIR)$(libdir)/mlt/libmltavformat$(LIBSUF)" 2> /dev/null || true
+ rm "$(DESTDIR)$(libdir)/mlt/libmltffmpeg$(LIBSUF)" 2> /dev/null || true
+ rm -rf "$(DESTDIR)$(prefix)/share/mlt/avformat"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * audio conversion
+ * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (c) 2008 Peter Ross
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg is distributed in the hope that 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_AUDIOCONVERT_H
+#define AVCODEC_AUDIOCONVERT_H
+
+/**
+ * @file audioconvert.h
+ * Audio format conversion routines
+ */
+
+
+#include "avcodec.h"
+
+
+/**
+ * Generate string corresponding to the sample format with
+ * number sample_fmt, or a header if sample_fmt is negative.
+ *
+ * @param[in] buf the buffer where to write the string
+ * @param[in] buf_size the size of buf
+ * @param[in] sample_fmt the number of the sample format to print the corresponding info string, or
+ * a negative value to print the corresponding header.
+ * Meaningful values for obtaining a sample format info vary from 0 to SAMPLE_FMT_NB -1.
+ */
+void avcodec_sample_fmt_string(char *buf, int buf_size, int sample_fmt);
+
+/**
+ * @return NULL on error
+ */
+const char *avcodec_get_sample_fmt_name(int sample_fmt);
+
+/**
+ * @return SAMPLE_FMT_NONE on error
+ */
+enum SampleFormat avcodec_get_sample_fmt(const char* name);
+
+/**
+ * @return NULL on error
+ */
+const char *avcodec_get_channel_name(int channel_id);
+
+/**
+ * Return description of channel layout
+ */
+void avcodec_get_channel_layout_string(char *buf, int buf_size, int nb_channels, int64_t channel_layout);
+
+/**
+ * Guess the channel layout
+ * @param nb_channels
+ * @param codec_id Codec identifier, or CODEC_ID_NONE if unknown
+ * @param fmt_name Format name, or NULL if unknown
+ * @return Channel layout mask
+ */
+int64_t avcodec_guess_channel_layout(int nb_channels, enum CodecID codec_id, const char *fmt_name);
+
+
+struct AVAudioConvert;
+typedef struct AVAudioConvert AVAudioConvert;
+
+/**
+ * Create an audio sample format converter context
+ * @param out_fmt Output sample format
+ * @param out_channels Number of output channels
+ * @param in_fmt Input sample format
+ * @param in_channels Number of input channels
+ * @param[in] matrix Channel mixing matrix (of dimension in_channel*out_channels). Set to NULL to ignore.
+ * @param flags See FF_MM_xx
+ * @return NULL on error
+ */
+AVAudioConvert *av_audio_convert_alloc(enum SampleFormat out_fmt, int out_channels,
+ enum SampleFormat in_fmt, int in_channels,
+ const float *matrix, int flags);
+
+/**
+ * Free audio sample format converter context
+ */
+void av_audio_convert_free(AVAudioConvert *ctx);
+
+/**
+ * Convert between audio sample formats
+ * @param[in] out array of output buffers for each channel. set to NULL to ignore processing of the given channel.
+ * @param[in] out_stride distance between consecutive input samples (measured in bytes)
+ * @param[in] in array of input buffers for each channel
+ * @param[in] in_stride distance between consecutive output samples (measured in bytes)
+ * @param len length of audio frame size (measured in samples)
+ */
+int av_audio_convert(AVAudioConvert *ctx,
+ void * const out[6], const int out_stride[6],
+ const void * const in[6], const int in_stride[6], int len);
+
+#endif /* AVCODEC_AUDIOCONVERT_H */
--- /dev/null
+#!/bin/sh
+
+# Determine whether to recommend/use the HEAD revision of FFmpeg (unreleased)
+# or a specific revision based upon whether the last digit of our version
+# is even or odd. An odd MLT version number always represents unreleased.
+svn_rev="17887"
+micro_version=$(echo $version | cut -d . -f 3)
+odd_version=$(($micro_version % 2))
+[ "$odd_version" -eq "1" ] && svn_rev="HEAD"
+
+if [ "$help" = "1" ]
+then
+ cat << EOF
+FFMPEG/avformat options:
+
+ --avformat-svn - Obtain ffmpeg from Subversion
+ --avformat-svn-extra - Add extra configure options for --avformat-svn
+ --avformat-shared=path - Link against a shared installation of ffmpeg (default)
+ --avformat-static=path - Link against a static ffmpeg dev tree
+ --avformat-ldextra=libs - Provide additional libs to link with
+ --avformat-suffix=suff - Specify a custom suffix for an ffmpeg shared build
+ --avformat-swscale - Use ffmpeg libswcale instead of img_convert
+ --avformat-no-codecs - Disable the producer and consumer to avoid the FFmpeg codecs
+ --avformat-no-filters - Disable the filters to make a codecs+muxers-only plugin
+
+ NOTE: The recommended version of FFmpeg is SVN-r$svn_rev.
+
+EOF
+
+else
+ targetos=$(uname -s)
+ case $targetos in
+ Darwin)
+ export LIBSUF=.dylib
+ ;;
+ Linux|FreeBSD)
+ export LIBSUF=.so
+ ;;
+ *)
+ ;;
+ esac
+
+ bits=$(uname -m)
+ case $bits in
+ x86_64)
+ [ -d /usr/lib/lib64 ] && export LIBDIR=lib64 || export LIBDIR=lib
+ ;;
+ *)
+ export LIBDIR=lib
+ ;;
+ esac
+
+ echo > config.mak
+
+ export static_ffmpeg=
+ export shared_ffmpeg=$(pkg-config --variable=prefix libavformat)
+ export extra_libs=
+ export svn_ffmpeg=
+ export svn_ffmpeg_extra=
+ export avformat_suffix=
+ export swscale=
+ export codecs=true
+ export filters=true
+
+ for i in "$@"
+ do
+ case $i in
+ --avformat-static=* ) static_ffmpeg="${i#--avformat-static=}" ;;
+ --avformat-shared=* ) shared_ffmpeg="${i#--avformat-shared=}" ;;
+ --avformat-ldextra=* ) extra_libs="${i#--avformat-ldextra=}" ;;
+ --avformat-svn ) svn_ffmpeg=true ;;
+ --avformat-svn-extra=* ) svn_ffmpeg_extra="${i#--avformat-svn-extra=}" ;;
+ --avformat-cvs ) svn_ffmpeg=true ;;
+ --avformat-suffix=* ) avformat_suffix="${i#--avformat-suffix=}" ;;
+ --avformat-swscale ) swscale=true ;;
+ --avformat-swscaler ) swscale=true ;;
+ --avformat-no-codecs ) codecs=false ;;
+ --avformat-no-filters ) filters=false ;;
+ esac
+ done
+
+ if [ "$svn_ffmpeg" != "" ]
+ then
+ if [ "$gpl" = "true" ]
+ then
+ enable_gpl="--enable-gpl"
+ [ "$swscale" != "" ] && [ "$svn_rev" = "17887" ] &&
+ enable_swscale="--enable-swscale"
+ fi
+ if [ ! -d "ffmpeg" ]
+ then
+ echo
+ echo "Checking out ffmpeg/avformat revision $svn_rev - no password required"
+ echo
+ if [ "$svn_rev" = "17887" ]; then
+ svn checkout -r $svn_rev svn://svn.mplayerhq.hu/ffmpeg/branches/0.5 ffmpeg
+ else
+ svn checkout -r $svn_rev svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg
+ fi
+ fi
+ [ -d "ffmpeg" ] && ( cd ffmpeg ; ./configure $enable_gpl $enable_swscale $svn_ffmpeg_extra )
+ #[ ! -f "ffmpeg/ffmpeg.patch" ] && ( cd ffmpeg ; cp ../ffmpeg.patch . ; patch -p0 < ffmpeg.patch )
+ echo "CFLAGS+=-I`pwd`/ffmpeg -I`pwd`/ffmpeg/libavformat -I`pwd`/ffmpeg/libavcodec -I`pwd`/ffmpeg/libavutil -I`pwd`/ffmpeg/libavdevice" >> config.mak
+ echo "LDFLAGS+=-L`pwd`/ffmpeg/libavformat -L`pwd`/ffmpeg/libavcodec -L`pwd`/ffmpeg/libavutil -L`pwd`/ffmpeg/libavdevice" >> config.mak
+ if [ "$swscale" != "" ] || [ "$svn_rev" = "HEAD" ]
+ then
+ echo "CFLAGS+=-I`pwd`/ffmpeg/libswscale" >> config.mak
+ echo "LDFLAGS+=-L`pwd`/ffmpeg/libswscale" >> config.mak
+ echo "SWSCALE=1" >> config.mak
+ fi
+ [ $targetos = "Darwin" ] &&
+ echo "LDFLAGS+=-single_module" >> config.mak
+ echo "LOCAL_FFMPEG=1" >> config.mak
+ echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak
+ extra_libs="$extra_libs -lz -lbz2"
+ elif [ "$static_ffmpeg" != "" ]
+ then
+ if [ -d "$static_ffmpeg" ]
+ then
+ echo "CFLAGS+=-I$static_ffmpeg/libavformat -I$static_ffmpeg/libavcodec -I$static_ffmpeg/libavutil -I$static_ffmpeg/libavdevice" >> config.mak
+ echo "LDFLAGS+=-L$static_ffmpeg/libavformat -L$static_ffmpeg/libavcodec -L$static_ffmpeg/libavutil -L$static_ffmpeg/libavdevice" >> config.mak
+ [ $targetos = "Darwin" ] &&
+ echo "LDFLAGS+=-single_module" >> config.mak
+ if [ "$swscale" != "" ]
+ then
+ echo "CFLAGS+=-I$static_ffmpeg/libswscale" >> config.mak
+ echo "LDFLAGS+=-L$static_ffmpeg/libswscale" >> config.mak
+ echo "SWSCALE=1" >> config.mak
+ fi
+ echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak
+ extra_libs="$extra_libs -lz"
+ else
+ echo "avformat: Invalid path specified: $static_ffmpeg"
+ touch ../disable-avformat
+ echo 0
+ fi
+ elif [ "$shared_ffmpeg" != "" ]
+ then
+ echo "PREFIX=$shared_ffmpeg" >> config.mak
+ echo "CFLAGS+=$(pkg-config --cflags libavformat) $TMP_CFLAGS" >> config.mak
+ echo "LDFLAGS+=$(pkg-config --libs libavformat)" >> config.mak
+ [ -d "$shared_ffmpeg/include/ffmpeg/libavformat" ] &&
+ echo "CFLAGS+=-I$shared_ffmpeg/include/ffmpeg/libavformat -I$shared_ffmpeg/include/ffmpeg/libavcodec" >> config.mak
+ [ -d "$shared_ffmpeg/include/libavformat" ] &&
+ echo "CFLAGS+=-I$shared_ffmpeg/include/libavformat -I$shared_ffmpeg/include/libavcodec" >> config.mak
+ avcodec_version=$(pkg-config --modversion libavcodec)
+ if [ "$swscale" != "" ] || ( [ $(echo $avcodec_version | cut -d. -f1) -ge 52 ] && [ $(echo $avcodec_version | cut -d. -f2) -ge 21 ] )
+ then
+ [ -d "$shared_ffmpeg/include/ffmpeg/libswscale" ] &&
+ echo "CFLAGS+=-I$shared_ffmpeg/include/ffmpeg/libswscale" >> config.mak
+ [ -d "$shared_ffmpeg/include/libswscale" ] &&
+ echo "CFLAGS+=-I$shared_ffmpeg/include/libswscale" >> config.mak
+ echo "SWSCALE=1" >> config.mak
+ fi
+ else
+ echo "avformat: No build environment found. "
+ echo " Try configuring mlt with --avformat-svn."
+ touch ../disable-avformat
+ exit 0
+ fi
+
+ echo "EXTRA_LIBS=$extra_libs" >> config.mak
+ echo "AVFORMAT_SUFFIX=$avformat_suffix" >> config.mak
+ [ "$codecs" = "true" ] && echo "CODECS=1" >> config.mak
+ [ "$filters" = "true" ] && echo "FILTERS=1" >> config.mak
+ exit 0
+
+fi
--- /dev/null
+/*
+ * consumer_avformat.c -- an encoder based on avformat
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// mlt Header files
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+
+// System header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <math.h>
+#include <unistd.h>
+
+// avformat header files
+#include <avformat.h>
+#ifdef SWSCALE
+#include <swscale.h>
+#endif
+#include <opt.h>
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_RGB32 PIX_FMT_RGBA32
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+//
+// This structure should be extended and made globally available in mlt
+//
+
+typedef struct
+{
+ int16_t *buffer;
+ int size;
+ int used;
+ double time;
+ int frequency;
+ int channels;
+}
+*sample_fifo, sample_fifo_s;
+
+sample_fifo sample_fifo_init( int frequency, int channels )
+{
+ sample_fifo this = calloc( 1, sizeof( sample_fifo_s ) );
+ this->frequency = frequency;
+ this->channels = channels;
+ return this;
+}
+
+// sample_fifo_clear and check are temporarily aborted (not working as intended)
+
+void sample_fifo_clear( sample_fifo this, double time )
+{
+ int words = ( float )( time - this->time ) * this->frequency * this->channels;
+ if ( ( int )( ( float )time * 100 ) < ( int )( ( float )this->time * 100 ) && this->used > words && words > 0 )
+ {
+ memmove( this->buffer, &this->buffer[ words ], ( this->used - words ) * sizeof( int16_t ) );
+ this->used -= words;
+ this->time = time;
+ }
+ else if ( ( int )( ( float )time * 100 ) != ( int )( ( float )this->time * 100 ) )
+ {
+ this->used = 0;
+ this->time = time;
+ }
+}
+
+void sample_fifo_check( sample_fifo this, double time )
+{
+ if ( this->used == 0 )
+ {
+ if ( ( int )( ( float )time * 100 ) < ( int )( ( float )this->time * 100 ) )
+ this->time = time;
+ }
+}
+
+void sample_fifo_append( sample_fifo this, int16_t *samples, int count )
+{
+ if ( ( this->size - this->used ) < count )
+ {
+ this->size += count * 5;
+ this->buffer = realloc( this->buffer, this->size * sizeof( int16_t ) );
+ }
+
+ memcpy( &this->buffer[ this->used ], samples, count * sizeof( int16_t ) );
+ this->used += count;
+}
+
+int sample_fifo_used( sample_fifo this )
+{
+ return this->used;
+}
+
+int sample_fifo_fetch( sample_fifo this, int16_t *samples, int count )
+{
+ if ( count > this->used )
+ count = this->used;
+
+ memcpy( samples, this->buffer, count * sizeof( int16_t ) );
+ this->used -= count;
+ memmove( this->buffer, &this->buffer[ count ], this->used * sizeof( int16_t ) );
+
+ this->time += ( double )count / this->channels / this->frequency;
+
+ return count;
+}
+
+void sample_fifo_close( sample_fifo this )
+{
+ free( this->buffer );
+ free( this );
+}
+
+// Forward references.
+static int consumer_start( mlt_consumer this );
+static int consumer_stop( mlt_consumer this );
+static int consumer_is_stopped( mlt_consumer this );
+static void *consumer_thread( void *arg );
+static void consumer_close( mlt_consumer this );
+
+/** Initialise the dv consumer.
+*/
+
+mlt_consumer consumer_avformat_init( mlt_profile profile, char *arg )
+{
+ // Allocate the consumer
+ mlt_consumer this = mlt_consumer_new( profile );
+
+ // If memory allocated and initialises without error
+ if ( this != NULL )
+ {
+ // Get properties from the consumer
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Assign close callback
+ this->close = consumer_close;
+
+ // Interpret the argument
+ if ( arg != NULL )
+ mlt_properties_set( properties, "target", arg );
+
+ // sample and frame queue
+ mlt_properties_set_data( properties, "frame_queue", mlt_deque_init( ), 0, ( mlt_destructor )mlt_deque_close, NULL );
+
+ // Audio options not fully handled by AVOptions
+#define QSCALE_NONE (-99999)
+ mlt_properties_set_int( properties, "aq", QSCALE_NONE );
+
+ // Video options not fully handled by AVOptions
+ mlt_properties_set_int( properties, "dc", 8 );
+
+ // Muxer options not fully handled by AVOptions
+ mlt_properties_set_double( properties, "muxdelay", 0.7 );
+ mlt_properties_set_double( properties, "muxpreload", 0.5 );
+
+ // Ensure termination at end of the stream
+ mlt_properties_set_int( properties, "terminate_on_pause", 1 );
+
+ // Default to separate processing threads for producer and consumer with no frame dropping!
+ mlt_properties_set_int( properties, "real_time", -1 );
+ mlt_properties_set_int( properties, "prefill", 1 );
+
+ // Set up start/stop/terminated callbacks
+ this->start = consumer_start;
+ this->stop = consumer_stop;
+ this->is_stopped = consumer_is_stopped;
+ }
+
+ // Return this
+ return this;
+}
+
+/** Start the consumer.
+*/
+
+static int consumer_start( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ int error = 0;
+
+ // Report information about available muxers and codecs as YAML Tiny
+ char *s = mlt_properties_get( properties, "f" );
+ if ( s && strcmp( s, "list" ) == 0 )
+ {
+ fprintf( stderr, "---\nformats:\n" );
+ AVOutputFormat *format = NULL;
+ while ( ( format = av_oformat_next( format ) ) )
+ fprintf( stderr, " - %s\n", format->name );
+ fprintf( stderr, "...\n" );
+ error = 1;
+ }
+ s = mlt_properties_get( properties, "acodec" );
+ if ( s && strcmp( s, "list" ) == 0 )
+ {
+ fprintf( stderr, "---\naudio_codecs:\n" );
+ AVCodec *codec = NULL;
+ while ( ( codec = av_codec_next( codec ) ) )
+ if ( codec->encode && codec->type == CODEC_TYPE_AUDIO )
+ fprintf( stderr, " - %s\n", codec->name );
+ fprintf( stderr, "...\n" );
+ error = 1;
+ }
+ s = mlt_properties_get( properties, "vcodec" );
+ if ( s && strcmp( s, "list" ) == 0 )
+ {
+ fprintf( stderr, "---\nvideo_codecs:\n" );
+ AVCodec *codec = NULL;
+ while ( ( codec = av_codec_next( codec ) ) )
+ if ( codec->encode && codec->type == CODEC_TYPE_VIDEO )
+ fprintf( stderr, " - %s\n", codec->name );
+ fprintf( stderr, "...\n" );
+ error = 1;
+ }
+
+ // Check that we're not already running
+ if ( !error && !mlt_properties_get_int( properties, "running" ) )
+ {
+ // Allocate a thread
+ pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
+
+ // Get the width and height
+ int width = mlt_properties_get_int( properties, "width" );
+ int height = mlt_properties_get_int( properties, "height" );
+
+ // Obtain the size property
+ char *size = mlt_properties_get( properties, "s" );
+
+ // Interpret it
+ if ( size != NULL )
+ {
+ int tw, th;
+ if ( sscanf( size, "%dx%d", &tw, &th ) == 2 && tw > 0 && th > 0 )
+ {
+ width = tw;
+ height = th;
+ }
+ else
+ {
+ fprintf( stderr, "%s: Invalid size property %s - ignoring.\n", __FILE__, size );
+ }
+ }
+
+ // Now ensure we honour the multiple of two requested by libavformat
+ width = ( width / 2 ) * 2;
+ height = ( height / 2 ) * 2;
+ mlt_properties_set_int( properties, "width", width );
+ mlt_properties_set_int( properties, "height", height );
+
+ // We need to set these on the profile as well because the s property is
+ // an alias to mlt properties that correspond to profile settings.
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ {
+ profile->width = width;
+ profile->height = height;
+ }
+
+ // Handle the ffmpeg command line "-r" property for frame rate
+ if ( mlt_properties_get( properties, "r" ) )
+ {
+ double frame_rate = mlt_properties_get_double( properties, "r" );
+ AVRational rational = av_d2q( frame_rate, 255 );
+ mlt_properties_set_int( properties, "frame_rate_num", rational.num );
+ mlt_properties_set_int( properties, "frame_rate_den", rational.den );
+ if ( profile )
+ {
+ profile->frame_rate_num = rational.num;
+ profile->frame_rate_den = rational.den;
+ mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+ }
+ }
+
+ // Apply AVOptions that are synonyms for standard mlt_consumer options
+ if ( mlt_properties_get( properties, "ac" ) )
+ mlt_properties_set_int( properties, "channels", mlt_properties_get_int( properties, "ac" ) );
+ if ( mlt_properties_get( properties, "ar" ) )
+ mlt_properties_set_int( properties, "frequency", mlt_properties_get_int( properties, "ar" ) );
+
+ // Assign the thread to properties
+ mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
+
+ // Set the running state
+ mlt_properties_set_int( properties, "running", 1 );
+
+ // Create the thread
+ pthread_create( thread, NULL, consumer_thread, this );
+ }
+ return error;
+}
+
+/** Stop the consumer.
+*/
+
+static int consumer_stop( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Check that we're running
+ if ( mlt_properties_get_int( properties, "running" ) )
+ {
+ // Get the thread
+ pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
+
+ // Stop the thread
+ mlt_properties_set_int( properties, "running", 0 );
+
+ // Wait for termination
+ pthread_join( *thread, NULL );
+ }
+
+ return 0;
+}
+
+/** Determine if the consumer is stopped.
+*/
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ return !mlt_properties_get_int( properties, "running" );
+}
+
+/** Process properties as AVOptions and apply to AV context obj
+*/
+
+static void apply_properties( void *obj, mlt_properties properties, int flags )
+{
+ int i;
+ int count = mlt_properties_count( properties );
+ for ( i = 0; i < count; i++ )
+ {
+ const char *opt_name = mlt_properties_get_name( properties, i );
+ const AVOption *opt = av_find_opt( obj, opt_name, NULL, flags, flags );
+ if ( opt != NULL )
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(7<<8)+0)
+ av_set_string3( obj, opt_name, mlt_properties_get( properties, opt_name), 0, NULL );
+#elif LIBAVCODEC_VERSION_INT >= ((51<<16)+(59<<8)+0)
+ av_set_string2( obj, opt_name, mlt_properties_get( properties, opt_name), 0 );
+#else
+ av_set_string( obj, opt_name, mlt_properties_get( properties, opt_name) );
+#endif
+ }
+}
+
+/** Add an audio output stream
+*/
+
+static AVStream *add_audio_stream( mlt_consumer this, AVFormatContext *oc, int codec_id )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Create a new stream
+ AVStream *st = av_new_stream( oc, 1 );
+
+ // If created, then initialise from properties
+ if ( st != NULL )
+ {
+ AVCodecContext *c = st->codec;
+
+ // Establish defaults from AVOptions
+ avcodec_get_context_defaults2( c, CODEC_TYPE_AUDIO );
+
+ c->codec_id = codec_id;
+ c->codec_type = CODEC_TYPE_AUDIO;
+
+ // Setup multi-threading
+ int thread_count = mlt_properties_get_int( properties, "threads" );
+ if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) )
+ thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) );
+ if ( thread_count > 1 )
+ avcodec_thread_init( c, thread_count );
+
+ if (oc->oformat->flags & AVFMT_GLOBALHEADER)
+ c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+ // Allow the user to override the audio fourcc
+ if ( mlt_properties_get( properties, "atag" ) )
+ {
+ char *tail = NULL;
+ char *arg = mlt_properties_get( properties, "atag" );
+ int tag = strtol( arg, &tail, 0);
+ if( !tail || *tail )
+ tag = arg[ 0 ] + ( arg[ 1 ] << 8 ) + ( arg[ 2 ] << 16 ) + ( arg[ 3 ] << 24 );
+ c->codec_tag = tag;
+ }
+
+ // Process properties as AVOptions
+ apply_properties( c, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM );
+
+ int audio_qscale = mlt_properties_get_int( properties, "aq" );
+ if ( audio_qscale > QSCALE_NONE )
+ {
+ c->flags |= CODEC_FLAG_QSCALE;
+ c->global_quality = st->quality = FF_QP2LAMBDA * audio_qscale;
+ }
+
+ // Set parameters controlled by MLT
+ c->sample_rate = mlt_properties_get_int( properties, "frequency" );
+ c->channels = mlt_properties_get_int( properties, "channels" );
+
+ if ( mlt_properties_get( properties, "alang" ) != NULL )
+ strncpy( st->language, mlt_properties_get( properties, "alang" ), sizeof( st->language ) );
+ }
+ else
+ {
+ fprintf( stderr, "%s: Could not allocate a stream for audio\n", __FILE__ );
+ }
+
+ return st;
+}
+
+static int open_audio( AVFormatContext *oc, AVStream *st, int audio_outbuf_size )
+{
+ // We will return the audio input size from here
+ int audio_input_frame_size = 0;
+
+ // Get the context
+ AVCodecContext *c = st->codec;
+
+ // Find the encoder
+ AVCodec *codec = avcodec_find_encoder( c->codec_id );
+
+ // Continue if codec found and we can open it
+ if ( codec != NULL && avcodec_open( c, codec ) >= 0 )
+ {
+ // ugly hack for PCM codecs (will be removed ASAP with new PCM
+ // support to compute the input frame size in samples
+ if ( c->frame_size <= 1 )
+ {
+ audio_input_frame_size = audio_outbuf_size / c->channels;
+ switch(st->codec->codec_id)
+ {
+ case CODEC_ID_PCM_S16LE:
+ case CODEC_ID_PCM_S16BE:
+ case CODEC_ID_PCM_U16LE:
+ case CODEC_ID_PCM_U16BE:
+ audio_input_frame_size >>= 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ audio_input_frame_size = c->frame_size;
+ }
+
+ // Some formats want stream headers to be seperate (hmm)
+ if( !strcmp( oc->oformat->name, "mp4" ) ||
+ !strcmp( oc->oformat->name, "mov" ) ||
+ !strcmp( oc->oformat->name, "3gp" ) )
+ c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ }
+ else
+ {
+ fprintf( stderr, "%s: Unable to encode audio - disabling audio output.\n", __FILE__ );
+ }
+
+ return audio_input_frame_size;
+}
+
+static void close_audio( AVFormatContext *oc, AVStream *st )
+{
+ avcodec_close( st->codec );
+}
+
+/** Add a video output stream
+*/
+
+static AVStream *add_video_stream( mlt_consumer this, AVFormatContext *oc, int codec_id )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Create a new stream
+ AVStream *st = av_new_stream( oc, 0 );
+
+ if ( st != NULL )
+ {
+ char *pix_fmt = mlt_properties_get( properties, "pix_fmt" );
+ AVCodecContext *c = st->codec;
+
+ // Establish defaults from AVOptions
+ avcodec_get_context_defaults2( c, CODEC_TYPE_VIDEO );
+
+ c->codec_id = codec_id;
+ c->codec_type = CODEC_TYPE_VIDEO;
+
+ // Setup multi-threading
+ int thread_count = mlt_properties_get_int( properties, "threads" );
+ if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) )
+ thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) );
+ if ( thread_count > 1 )
+ avcodec_thread_init( c, thread_count );
+
+ // Process properties as AVOptions
+ apply_properties( c, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM );
+
+ // Set options controlled by MLT
+ c->width = mlt_properties_get_int( properties, "width" );
+ c->height = mlt_properties_get_int( properties, "height" );
+ c->time_base.num = mlt_properties_get_int( properties, "frame_rate_den" );
+ c->time_base.den = mlt_properties_get_int( properties, "frame_rate_num" );
+ if ( st->time_base.den == 0 )
+ st->time_base = c->time_base;
+ c->pix_fmt = pix_fmt ? avcodec_get_pix_fmt( pix_fmt ) : PIX_FMT_YUV420P;
+
+ if ( mlt_properties_get( properties, "aspect" ) )
+ {
+ // "-aspect" on ffmpeg command line is display aspect ratio
+ double ar = mlt_properties_get_double( properties, "aspect" );
+ AVRational rational = av_d2q( ar, 255 );
+
+ // Update the profile and properties as well since this is an alias
+ // for mlt properties that correspond to profile settings
+ mlt_properties_set_int( properties, "display_aspect_num", rational.num );
+ mlt_properties_set_int( properties, "display_aspect_den", rational.den );
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+ if ( profile )
+ {
+ profile->display_aspect_num = rational.num;
+ profile->display_aspect_den = rational.den;
+ mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) );
+ }
+
+ // Now compute the sample aspect ratio
+ rational = av_d2q( ar * c->height / c->width, 255 );
+ c->sample_aspect_ratio = rational;
+ // Update the profile and properties as well since this is an alias
+ // for mlt properties that correspond to profile settings
+ mlt_properties_set_int( properties, "sample_aspect_num", rational.num );
+ mlt_properties_set_int( properties, "sample_aspect_den", rational.den );
+ if ( profile )
+ {
+ profile->sample_aspect_num = rational.num;
+ profile->sample_aspect_den = rational.den;
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
+ }
+ }
+ else
+ {
+ c->sample_aspect_ratio.num = mlt_properties_get_int( properties, "sample_aspect_num" );
+ c->sample_aspect_ratio.den = mlt_properties_get_int( properties, "sample_aspect_den" );
+ }
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+ st->sample_aspect_ratio = c->sample_aspect_ratio;
+#endif
+
+ if ( mlt_properties_get_double( properties, "qscale" ) > 0 )
+ {
+ c->flags |= CODEC_FLAG_QSCALE;
+ st->quality = FF_QP2LAMBDA * mlt_properties_get_double( properties, "qscale" );
+ }
+
+ // Allow the user to override the video fourcc
+ if ( mlt_properties_get( properties, "vtag" ) )
+ {
+ char *tail = NULL;
+ const char *arg = mlt_properties_get( properties, "vtag" );
+ int tag = strtol( arg, &tail, 0);
+ if( !tail || *tail )
+ tag = arg[ 0 ] + ( arg[ 1 ] << 8 ) + ( arg[ 2 ] << 16 ) + ( arg[ 3 ] << 24 );
+ c->codec_tag = tag;
+ }
+
+ // Some formats want stream headers to be seperate
+ if ( oc->oformat->flags & AVFMT_GLOBALHEADER )
+ c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+ // Translate these standard mlt consumer properties to ffmpeg
+ if ( mlt_properties_get_int( properties, "progressive" ) == 0 &&
+ mlt_properties_get_int( properties, "deinterlace" ) == 0 )
+ {
+ if ( ! mlt_properties_get( properties, "ildct" ) || mlt_properties_get_int( properties, "ildct" ) )
+ c->flags |= CODEC_FLAG_INTERLACED_DCT;
+ if ( ! mlt_properties_get( properties, "ilme" ) || mlt_properties_get_int( properties, "ilme" ) )
+ c->flags |= CODEC_FLAG_INTERLACED_ME;
+ }
+
+ // parse the ratecontrol override string
+ int i;
+ char *rc_override = mlt_properties_get( properties, "rc_override" );
+ for ( i = 0; rc_override; i++ )
+ {
+ int start, end, q;
+ int e = sscanf( rc_override, "%d,%d,%d", &start, &end, &q );
+ if ( e != 3 )
+ fprintf( stderr, "%s: Error parsing rc_override\n", __FILE__ );
+ c->rc_override = av_realloc( c->rc_override, sizeof( RcOverride ) * ( i + 1 ) );
+ c->rc_override[i].start_frame = start;
+ c->rc_override[i].end_frame = end;
+ if ( q > 0 )
+ {
+ c->rc_override[i].qscale = q;
+ c->rc_override[i].quality_factor = 1.0;
+ }
+ else
+ {
+ c->rc_override[i].qscale = 0;
+ c->rc_override[i].quality_factor = -q / 100.0;
+ }
+ rc_override = strchr( rc_override, '/' );
+ if ( rc_override )
+ rc_override++;
+ }
+ c->rc_override_count = i;
+ if ( !c->rc_initial_buffer_occupancy )
+ c->rc_initial_buffer_occupancy = c->rc_buffer_size * 3/4;
+ c->intra_dc_precision = mlt_properties_get_int( properties, "dc" ) - 8;
+
+ // Setup dual-pass
+ i = mlt_properties_get_int( properties, "pass" );
+ if ( i == 1 )
+ c->flags |= CODEC_FLAG_PASS1;
+ else if ( i == 2 )
+ c->flags |= CODEC_FLAG_PASS2;
+ if ( codec_id != CODEC_ID_H264 && ( c->flags & ( CODEC_FLAG_PASS1 | CODEC_FLAG_PASS2 ) ) )
+ {
+ char logfilename[1024];
+ FILE *f;
+ int size;
+ char *logbuffer;
+
+ snprintf( logfilename, sizeof(logfilename), "%s_2pass.log",
+ mlt_properties_get( properties, "passlogfile" ) ? mlt_properties_get( properties, "passlogfile" ) : mlt_properties_get( properties, "target" ) );
+ if ( c->flags & CODEC_FLAG_PASS1 )
+ {
+ f = fopen( logfilename, "w" );
+ if ( !f )
+ perror( logfilename );
+ else
+ mlt_properties_set_data( properties, "_logfile", f, 0, ( mlt_destructor )fclose, NULL );
+ }
+ else
+ {
+ /* read the log file */
+ f = fopen( logfilename, "r" );
+ if ( !f )
+ {
+ perror(logfilename);
+ }
+ else
+ {
+ mlt_properties_set( properties, "_logfilename", logfilename );
+ fseek( f, 0, SEEK_END );
+ size = ftell( f );
+ fseek( f, 0, SEEK_SET );
+ logbuffer = av_malloc( size + 1 );
+ if ( !logbuffer )
+ fprintf( stderr, "%s: Could not allocate log buffer\n", __FILE__ );
+ else
+ {
+ size = fread( logbuffer, 1, size, f );
+ fclose( f );
+ logbuffer[size] = '\0';
+ c->stats_in = logbuffer;
+ mlt_properties_set_data( properties, "_logbuffer", logbuffer, 0, ( mlt_destructor )av_free, NULL );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ fprintf( stderr, "%s: Could not allocate a stream for video\n", __FILE__ );
+ }
+
+ return st;
+}
+
+static AVFrame *alloc_picture( int pix_fmt, int width, int height )
+{
+ // Allocate a frame
+ AVFrame *picture = avcodec_alloc_frame();
+
+ // Determine size of the
+ int size = avpicture_get_size(pix_fmt, width, height);
+
+ // Allocate the picture buf
+ uint8_t *picture_buf = av_malloc(size);
+
+ // If we have both, then fill the image
+ if ( picture != NULL && picture_buf != NULL )
+ {
+ // Fill the frame with the allocated buffer
+ avpicture_fill( (AVPicture *)picture, picture_buf, pix_fmt, width, height);
+ }
+ else
+ {
+ // Something failed - clean up what we can
+ av_free( picture );
+ av_free( picture_buf );
+ picture = NULL;
+ }
+
+ return picture;
+}
+
+static int open_video(AVFormatContext *oc, AVStream *st)
+{
+ // Get the codec
+ AVCodecContext *video_enc = st->codec;
+
+ // find the video encoder
+ AVCodec *codec = avcodec_find_encoder( video_enc->codec_id );
+
+ if( codec && codec->pix_fmts )
+ {
+ const enum PixelFormat *p = codec->pix_fmts;
+ for( ; *p!=-1; p++ )
+ {
+ if( *p == video_enc->pix_fmt )
+ break;
+ }
+ if( *p == -1 )
+ video_enc->pix_fmt = codec->pix_fmts[ 0 ];
+ }
+
+ // Open the codec safely
+ return codec != NULL && avcodec_open( video_enc, codec ) >= 0;
+}
+
+void close_video(AVFormatContext *oc, AVStream *st)
+{
+ avcodec_close(st->codec);
+}
+
+static inline long time_difference( struct timeval *time1 )
+{
+ struct timeval time2;
+ gettimeofday( &time2, NULL );
+ return time2.tv_sec * 1000000 + time2.tv_usec - time1->tv_sec * 1000000 - time1->tv_usec;
+}
+
+/** The main thread - the argument is simply the consumer.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Map the argument to the object
+ mlt_consumer this = arg;
+
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get the terminate on pause property
+ int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
+ int terminated = 0;
+
+ // Determine if feed is slow (for realtime stuff)
+ int real_time_output = mlt_properties_get_int( properties, "real_time" );
+
+ // Time structures
+ struct timeval ante;
+
+ // Get the frame rate
+ double fps = mlt_properties_get_double( properties, "fps" );
+
+ // Get width and height
+ int width = mlt_properties_get_int( properties, "width" );
+ int height = mlt_properties_get_int( properties, "height" );
+ int img_width = width;
+ int img_height = height;
+
+ // Get default audio properties
+ mlt_audio_format aud_fmt = mlt_audio_pcm;
+ int channels = mlt_properties_get_int( properties, "channels" );
+ int frequency = mlt_properties_get_int( properties, "frequency" );
+ int16_t *pcm = NULL;
+ int samples = 0;
+
+ // AVFormat audio buffer and frame size
+ int audio_outbuf_size = 10000;
+ uint8_t *audio_outbuf = av_malloc( audio_outbuf_size );
+ int audio_input_frame_size = 0;
+
+ // AVFormat video buffer and frame count
+ int frame_count = 0;
+ int video_outbuf_size = ( 1024 * 1024 );
+ uint8_t *video_outbuf = av_malloc( video_outbuf_size );
+
+ // Used for the frame properties
+ mlt_frame frame = NULL;
+ mlt_properties frame_properties = NULL;
+
+ // Get the queues
+ mlt_deque queue = mlt_properties_get_data( properties, "frame_queue", NULL );
+ sample_fifo fifo = mlt_properties_get_data( properties, "sample_fifo", NULL );
+
+ // Need two av pictures for converting
+ AVFrame *output = NULL;
+ AVFrame *input = alloc_picture( PIX_FMT_YUYV422, width, height );
+
+ // For receiving images from an mlt_frame
+ uint8_t *image;
+ mlt_image_format img_fmt = mlt_image_yuv422;
+
+ // For receiving audio samples back from the fifo
+ int16_t *buffer = av_malloc( 48000 * 2 );
+ int count = 0;
+
+ // Allocate the context
+ AVFormatContext *oc = av_alloc_format_context( );
+
+ // Streams
+ AVStream *audio_st = NULL;
+ AVStream *video_st = NULL;
+
+ // Time stamps
+ double audio_pts = 0;
+ double video_pts = 0;
+
+ // Loop variable
+ int i;
+
+ // Frames despatched
+ long int frames = 0;
+ long int total_time = 0;
+
+ // Determine the format
+ AVOutputFormat *fmt = NULL;
+ const char *filename = mlt_properties_get( properties, "target" );
+ char *format = mlt_properties_get( properties, "f" );
+ char *vcodec = mlt_properties_get( properties, "vcodec" );
+ char *acodec = mlt_properties_get( properties, "acodec" );
+
+ // Used to store and override codec ids
+ int audio_codec_id;
+ int video_codec_id;
+
+ // Check for user selected format first
+ if ( format != NULL )
+ fmt = guess_format( format, NULL, NULL );
+
+ // Otherwise check on the filename
+ if ( fmt == NULL && filename != NULL )
+ fmt = guess_format( NULL, filename, NULL );
+
+ // Otherwise default to mpeg
+ if ( fmt == NULL )
+ fmt = guess_format( "mpeg", NULL, NULL );
+
+ // We need a filename - default to stdout?
+ if ( filename == NULL || !strcmp( filename, "" ) )
+ filename = "pipe:";
+
+ // Get the codec ids selected
+ audio_codec_id = fmt->audio_codec;
+ video_codec_id = fmt->video_codec;
+
+ // Check for audio codec overides
+ if ( ( acodec && strcmp( "acodec", "none" ) == 0 ) || mlt_properties_get_int( properties, "an" ) )
+ audio_codec_id = CODEC_ID_NONE;
+ else if ( acodec )
+ {
+ AVCodec *p = avcodec_find_encoder_by_name( acodec );
+ if ( p != NULL )
+ audio_codec_id = p->id;
+ else
+ fprintf( stderr, "%s: audio codec %s unrecognised - ignoring\n", __FILE__, acodec );
+ }
+
+ // Check for video codec overides
+ if ( ( vcodec && strcmp( "vcodec", "none" ) == 0 ) || mlt_properties_get_int( properties, "vn" ) )
+ video_codec_id = CODEC_ID_NONE;
+ else if ( vcodec )
+ {
+ AVCodec *p = avcodec_find_encoder_by_name( vcodec );
+ if ( p != NULL )
+ video_codec_id = p->id;
+ else
+ fprintf( stderr, "%s: video codec %s unrecognised - ignoring\n", __FILE__, vcodec );
+ }
+
+ // Write metadata
+ char *tmp = NULL;
+ int metavalue;
+
+ tmp = mlt_properties_get( properties, "meta.attr.title.markup");
+ if (tmp != NULL) snprintf( oc->title, sizeof(oc->title), "%s", tmp );
+
+ tmp = mlt_properties_get( properties, "meta.attr.comment.markup");
+ if (tmp != NULL) snprintf( oc->comment, sizeof(oc->comment), "%s", tmp );
+
+ tmp = mlt_properties_get( properties, "meta.attr.author.markup");
+ if (tmp != NULL) snprintf( oc->author, sizeof(oc->author), "%s", tmp );
+
+ tmp = mlt_properties_get( properties, "meta.attr.copyright.markup");
+ if (tmp != NULL) snprintf( oc->copyright, sizeof(oc->copyright), "%s", tmp );
+
+ tmp = mlt_properties_get( properties, "meta.attr.album.markup");
+ if (tmp != NULL) snprintf( oc->album, sizeof(oc->album), "%s", tmp );
+
+ metavalue = mlt_properties_get_int( properties, "meta.attr.year.markup");
+ if (metavalue != 0) oc->year = metavalue;
+
+ metavalue = mlt_properties_get_int( properties, "meta.attr.track.markup");
+ if (metavalue != 0) oc->track = metavalue;
+
+ oc->oformat = fmt;
+ snprintf( oc->filename, sizeof(oc->filename), "%s", filename );
+
+ // Add audio and video streams
+ if ( video_codec_id != CODEC_ID_NONE )
+ video_st = add_video_stream( this, oc, video_codec_id );
+ if ( audio_codec_id != CODEC_ID_NONE )
+ audio_st = add_audio_stream( this, oc, audio_codec_id );
+
+ // Set the parameters (even though we have none...)
+ if ( av_set_parameters(oc, NULL) >= 0 )
+ {
+ oc->preload = ( int )( mlt_properties_get_double( properties, "muxpreload" ) * AV_TIME_BASE );
+ oc->max_delay= ( int )( mlt_properties_get_double( properties, "muxdelay" ) * AV_TIME_BASE );
+
+ // Process properties as AVOptions
+ apply_properties( oc, properties, AV_OPT_FLAG_ENCODING_PARAM );
+
+ if ( video_st && !open_video( oc, video_st ) )
+ video_st = NULL;
+ if ( audio_st )
+ audio_input_frame_size = open_audio( oc, audio_st, audio_outbuf_size );
+
+ // Open the output file, if needed
+ if ( !( fmt->flags & AVFMT_NOFILE ) )
+ {
+ if ( url_fopen( &oc->pb, filename, URL_WRONLY ) < 0 )
+ {
+ fprintf( stderr, "%s: Could not open '%s'\n", __FILE__, filename );
+ mlt_properties_set_int( properties, "running", 0 );
+ }
+ }
+
+ // Write the stream header, if any
+ if ( mlt_properties_get_int( properties, "running" ) )
+ av_write_header( oc );
+ }
+ else
+ {
+ fprintf( stderr, "%s: Invalid output format parameters\n", __FILE__ );
+ mlt_properties_set_int( properties, "running", 0 );
+ }
+
+ // Allocate picture
+ if ( video_st )
+ output = alloc_picture( video_st->codec->pix_fmt, width, height );
+
+ // Last check - need at least one stream
+ if ( audio_st == NULL && video_st == NULL )
+ mlt_properties_set_int( properties, "running", 0 );
+
+ // Get the starting time (can ignore the times above)
+ gettimeofday( &ante, NULL );
+
+ // Loop while running
+ while( mlt_properties_get_int( properties, "running" ) && !terminated )
+ {
+ // Get the frame
+ frame = mlt_consumer_rt_frame( this );
+
+ // Check that we have a frame to work with
+ if ( frame != NULL )
+ {
+ // Increment frames despatched
+ frames ++;
+
+ // Default audio args
+ frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Check for the terminated condition
+ terminated = terminate_on_pause && mlt_properties_get_double( frame_properties, "_speed" ) == 0.0;
+
+ // Get audio and append to the fifo
+ if ( !terminated && audio_st )
+ {
+ samples = mlt_sample_calculator( fps, frequency, count ++ );
+ mlt_frame_get_audio( frame, &pcm, &aud_fmt, &frequency, &channels, &samples );
+
+ // Create the fifo if we don't have one
+ if ( fifo == NULL )
+ {
+ fifo = sample_fifo_init( frequency, channels );
+ mlt_properties_set_data( properties, "sample_fifo", fifo, 0, ( mlt_destructor )sample_fifo_close, NULL );
+ }
+
+ if ( mlt_properties_get_double( frame_properties, "_speed" ) != 1.0 )
+ memset( pcm, 0, samples * channels * 2 );
+
+ // Append the samples
+ sample_fifo_append( fifo, pcm, samples * channels );
+ total_time += ( samples * 1000000 ) / frequency;
+ }
+
+ // Encode the image
+ if ( !terminated && video_st )
+ mlt_deque_push_back( queue, frame );
+ else
+ mlt_frame_close( frame );
+ }
+
+ // While we have stuff to process, process...
+ while ( 1 )
+ {
+ if (audio_st)
+ audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
+ else
+ audio_pts = 0.0;
+
+ if (video_st)
+ video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
+ else
+ video_pts = 0.0;
+
+ // Write interleaved audio and video frames
+ if ( !video_st || ( video_st && audio_st && audio_pts < video_pts ) )
+ {
+ if ( channels * audio_input_frame_size < sample_fifo_used( fifo ) )
+ {
+ AVCodecContext *c;
+ AVPacket pkt;
+ av_init_packet( &pkt );
+
+ c = audio_st->codec;
+
+ sample_fifo_fetch( fifo, buffer, channels * audio_input_frame_size );
+
+ pkt.size = avcodec_encode_audio( c, audio_outbuf, audio_outbuf_size, buffer );
+ // Write the compressed frame in the media file
+ if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+ pkt.pts = av_rescale_q( c->coded_frame->pts, c->time_base, audio_st->time_base );
+ pkt.flags |= PKT_FLAG_KEY;
+ pkt.stream_index= audio_st->index;
+ pkt.data= audio_outbuf;
+
+ if ( pkt.size )
+ if ( av_interleaved_write_frame( oc, &pkt ) != 0)
+ fprintf( stderr, "%s: Error while writing audio frame\n", __FILE__ );
+
+ audio_pts += c->frame_size;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if ( video_st )
+ {
+ if ( mlt_deque_count( queue ) )
+ {
+ int out_size, ret;
+ AVCodecContext *c;
+
+ frame = mlt_deque_pop_front( queue );
+ frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ c = video_st->codec;
+
+ if ( mlt_properties_get_int( frame_properties, "rendered" ) )
+ {
+ int i = 0;
+ int j = 0;
+ uint8_t *p;
+ uint8_t *q;
+
+ mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+
+ mlt_frame_get_image( frame, &image, &img_fmt, &img_width, &img_height, 0 );
+
+ q = image;
+
+ // Convert the mlt frame to an AVPicture
+ for ( i = 0; i < height; i ++ )
+ {
+ p = input->data[ 0 ] + i * input->linesize[ 0 ];
+ j = width;
+ while( j -- )
+ {
+ *p ++ = *q ++;
+ *p ++ = *q ++;
+ }
+ }
+
+ // Do the colour space conversion
+#ifdef SWSCALE
+ struct SwsContext *context = sws_getContext( width, height, PIX_FMT_YUYV422,
+ width, height, video_st->codec->pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+ sws_scale( context, input->data, input->linesize, 0, height,
+ output->data, output->linesize);
+ sws_freeContext( context );
+#else
+ img_convert( ( AVPicture * )output, video_st->codec->pix_fmt, ( AVPicture * )input, PIX_FMT_YUYV422, width, height );
+#endif
+
+ // Apply the alpha if applicable
+ if ( video_st->codec->pix_fmt == PIX_FMT_RGB32 )
+ {
+ uint8_t *alpha = mlt_frame_get_alpha_mask( frame );
+ register int n;
+
+ for ( i = 0; i < height; i ++ )
+ {
+ n = ( width + 7 ) / 8;
+ p = output->data[ 0 ] + i * output->linesize[ 0 ];
+
+ #ifndef __DARWIN__
+ p += 3;
+ #endif
+
+ switch( width % 8 )
+ {
+ case 0: do { *p = *alpha++; p += 4;
+ case 7: *p = *alpha++; p += 4;
+ case 6: *p = *alpha++; p += 4;
+ case 5: *p = *alpha++; p += 4;
+ case 4: *p = *alpha++; p += 4;
+ case 3: *p = *alpha++; p += 4;
+ case 2: *p = *alpha++; p += 4;
+ case 1: *p = *alpha++; p += 4;
+ }
+ while( --n );
+ }
+ }
+ }
+ }
+
+ if (oc->oformat->flags & AVFMT_RAWPICTURE)
+ {
+ // raw video case. The API will change slightly in the near future for that
+ AVPacket pkt;
+ av_init_packet(&pkt);
+
+ pkt.flags |= PKT_FLAG_KEY;
+ pkt.stream_index= video_st->index;
+ pkt.data= (uint8_t *)output;
+ pkt.size= sizeof(AVPicture);
+
+ ret = av_write_frame(oc, &pkt);
+ video_pts += c->frame_size;
+ }
+ else
+ {
+ // Set the quality
+ output->quality = video_st->quality;
+
+ // Set frame interlace hints
+ output->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" );
+ output->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" );
+
+ // Encode the image
+ out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, output );
+
+ // If zero size, it means the image was buffered
+ if (out_size > 0)
+ {
+ AVPacket pkt;
+ av_init_packet( &pkt );
+
+ if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+ pkt.pts= av_rescale_q( c->coded_frame->pts, c->time_base, video_st->time_base );
+ if( c->coded_frame && c->coded_frame->key_frame )
+ pkt.flags |= PKT_FLAG_KEY;
+ pkt.stream_index= video_st->index;
+ pkt.data= video_outbuf;
+ pkt.size= out_size;
+
+ // write the compressed frame in the media file
+ ret = av_interleaved_write_frame(oc, &pkt);
+ video_pts += c->frame_size;
+
+ // Dual pass logging
+ if ( mlt_properties_get_data( properties, "_logfile", NULL ) && c->stats_out)
+ fprintf( mlt_properties_get_data( properties, "_logfile", NULL ), "%s", c->stats_out );
+ }
+ else
+ {
+ fprintf( stderr, "%s: error with video encode\n", __FILE__ );
+ }
+ }
+ frame_count++;
+ mlt_frame_close( frame );
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if ( real_time_output == 1 && frames % 12 == 0 )
+ {
+ long passed = time_difference( &ante );
+ if ( fifo != NULL )
+ {
+ long pending = ( ( ( long )sample_fifo_used( fifo ) * 1000 ) / frequency ) * 1000;
+ passed -= pending;
+ }
+ if ( passed < total_time )
+ {
+ long total = ( total_time - passed );
+ struct timespec t = { total / 1000000, ( total % 1000000 ) * 1000 };
+ nanosleep( &t, NULL );
+ }
+ }
+ }
+
+#ifdef FLUSH
+ if ( ! real_time_output )
+ {
+ // Flush audio fifo
+ if ( audio_st && audio_st->codec->frame_size > 1 ) for (;;)
+ {
+ AVCodecContext *c = audio_st->codec;
+ AVPacket pkt;
+ av_init_packet( &pkt );
+ pkt.size = 0;
+
+ if ( /*( c->capabilities & CODEC_CAP_SMALL_LAST_FRAME ) &&*/
+ ( channels * audio_input_frame_size < sample_fifo_used( fifo ) ) )
+ {
+ sample_fifo_fetch( fifo, buffer, channels * audio_input_frame_size );
+ pkt.size = avcodec_encode_audio( c, audio_outbuf, audio_outbuf_size, buffer );
+ }
+ if ( pkt.size <= 0 )
+ pkt.size = avcodec_encode_audio( c, audio_outbuf, audio_outbuf_size, NULL );
+ if ( pkt.size <= 0 )
+ break;
+
+ // Write the compressed frame in the media file
+ if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+ pkt.pts = av_rescale_q( c->coded_frame->pts, c->time_base, audio_st->time_base );
+ pkt.flags |= PKT_FLAG_KEY;
+ pkt.stream_index = audio_st->index;
+ pkt.data = audio_outbuf;
+ if ( av_interleaved_write_frame( oc, &pkt ) != 0 )
+ {
+ fprintf( stderr, "%s: Error while writing flushed audio frame\n", __FILE__ );
+ break;
+ }
+ }
+
+ // Flush video
+ if ( video_st && !( oc->oformat->flags & AVFMT_RAWPICTURE ) ) for (;;)
+ {
+ AVCodecContext *c = video_st->codec;
+ AVPacket pkt;
+ av_init_packet( &pkt );
+
+ // Encode the image
+ pkt.size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, NULL );
+ if ( pkt.size <= 0 )
+ break;
+
+ if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+ pkt.pts= av_rescale_q( c->coded_frame->pts, c->time_base, video_st->time_base );
+ if( c->coded_frame && c->coded_frame->key_frame )
+ pkt.flags |= PKT_FLAG_KEY;
+ pkt.stream_index = video_st->index;
+ pkt.data = video_outbuf;
+
+ // write the compressed frame in the media file
+ if ( av_interleaved_write_frame( oc, &pkt ) != 0 )
+ {
+ fprintf( stderr, "%s: Error while writing flushed video frame\n". __FILE__ );
+ break;
+ }
+ }
+ }
+#endif
+
+ // close each codec
+ if (video_st)
+ close_video(oc, video_st);
+ if (audio_st)
+ close_audio(oc, audio_st);
+
+ // Write the trailer, if any
+ av_write_trailer(oc);
+
+ // Free the streams
+ for(i = 0; i < oc->nb_streams; i++)
+ av_freep(&oc->streams[i]);
+
+ // Close the output file
+ if (!(fmt->flags & AVFMT_NOFILE))
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(0<<8)+0)
+ url_fclose(oc->pb);
+#else
+ url_fclose(&oc->pb);
+#endif
+
+ // Clean up input and output frames
+ if ( output )
+ av_free( output->data[0] );
+ av_free( output );
+ av_free( input->data[0] );
+ av_free( input );
+ av_free( video_outbuf );
+ av_free( buffer );
+
+ // Free the stream
+ av_free(oc);
+
+ // Just in case we terminated on pause
+ mlt_properties_set_int( properties, "running", 0 );
+
+ mlt_consumer_stopped( this );
+
+ if ( mlt_properties_get_int( properties, "pass" ) == 2 )
+ {
+ // Remove the dual pass log file
+ if ( mlt_properties_get( properties, "_logfilename" ) )
+ remove( mlt_properties_get( properties, "_logfilename" ) );
+
+ // Remove the x264 dual pass logs
+ char *cwd = getcwd( NULL, 0 );
+ const char *file = "x264_2pass.log";
+ char *full = malloc( strlen( cwd ) + strlen( file ) + 2 );
+ sprintf( full, "%s/%s", cwd, file );
+ remove( full );
+ free( full );
+ file = "x264_2pass.log.temp";
+ full = malloc( strlen( cwd ) + strlen( file ) + 2 );
+ sprintf( full, "%s/%s", cwd, file );
+ remove( full );
+ free( full );
+ free( cwd );
+ remove( "x264_2pass.log.temp" );
+ }
+
+ return NULL;
+}
+
+/** Close the consumer.
+*/
+
+static void consumer_close( mlt_consumer this )
+{
+ // Stop the consumer
+ mlt_consumer_stop( this );
+
+ // Close the parent
+ mlt_consumer_close( this );
+
+ // Free the memory
+ free( this );
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <pthread.h>
+#include <limits.h>
+
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_avformat_init( mlt_profile profile, char *file );
+extern mlt_filter filter_avcolour_space_init( void *arg );
+extern mlt_filter filter_avdeinterlace_init( void *arg );
+extern mlt_filter filter_avresample_init( char *arg );
+extern mlt_filter filter_swscale_init( mlt_profile profile, char *arg );
+extern mlt_producer producer_avformat_init( mlt_profile profile, char *file );
+
+// ffmpeg Header files
+#include <avformat.h>
+
+// A static flag used to determine if avformat has been initialised
+static int avformat_initialised = 0;
+
+// A locking mutex
+static pthread_mutex_t avformat_mutex;
+
+#if 0
+// These 3 functions should override the alloc functions in libavformat
+// but some formats or codecs seem to crash when used (wmv in particular)
+
+void *av_malloc( unsigned int size )
+{
+ return mlt_pool_alloc( size );
+}
+
+void *av_realloc( void *ptr, unsigned int size )
+{
+ return mlt_pool_realloc( ptr, size );
+}
+
+void av_free( void *ptr )
+{
+ return mlt_pool_release( ptr );
+}
+#endif
+
+void avformat_destroy( void *ignore )
+{
+ // Clean up
+ // av_free_static( ); -XXX this is deprecated
+
+ // Destroy the mutex
+ pthread_mutex_destroy( &avformat_mutex );
+}
+
+void avformat_lock( )
+{
+ // Lock the mutex now
+ pthread_mutex_lock( &avformat_mutex );
+}
+
+void avformat_unlock( )
+{
+ // Unlock the mutex now
+ pthread_mutex_unlock( &avformat_mutex );
+}
+
+static void avformat_init( )
+{
+ // Initialise avformat if necessary
+ if ( avformat_initialised == 0 )
+ {
+ avformat_initialised = 1;
+ pthread_mutex_init( &avformat_mutex, NULL );
+ av_register_all( );
+ mlt_factory_register_for_clean_up( NULL, avformat_destroy );
+ av_log_set_level( mlt_log_get_level() );
+ }
+}
+
+static void *create_service( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
+{
+ avformat_init( );
+#ifdef CODECS
+ if ( !strcmp( id, "avformat" ) )
+ {
+ if ( type == producer_type )
+ return producer_avformat_init( profile, arg );
+ else if ( type == consumer_type )
+ return consumer_avformat_init( profile, arg );
+ }
+#endif
+#ifdef FILTERS
+ if ( !strcmp( id, "avcolour_space" ) )
+ return filter_avcolour_space_init( arg );
+ if ( !strcmp( id, "avdeinterlace" ) )
+ return filter_avdeinterlace_init( arg );
+ if ( !strcmp( id, "avresample" ) )
+ return filter_avresample_init( arg );
+#ifdef SWSCALE
+ if ( !strcmp( id, "swscale" ) )
+ return filter_swscale_init( profile, arg );
+#endif
+#endif
+ return NULL;
+}
+
+static mlt_properties avformat_metadata( mlt_service_type type, const char *id, void *data )
+{
+ char file[ PATH_MAX ];
+ const char *service_type = NULL;
+ switch ( type )
+ {
+ case consumer_type:
+ service_type = "consumer";
+ break;
+ case filter_type:
+ service_type = "filter";
+ break;
+ case producer_type:
+ service_type = "producer";
+ break;
+ case transition_type:
+ service_type = "transition";
+ break;
+ default:
+ return NULL;
+ }
+ snprintf( file, PATH_MAX, "%s/avformat/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
+ return mlt_properties_parse_yaml( file );
+}
+
+MLT_REPOSITORY
+{
+#ifdef CODECS
+ MLT_REGISTER( consumer_type, "avformat", create_service );
+ MLT_REGISTER( producer_type, "avformat", create_service );
+ MLT_REGISTER_METADATA( producer_type, "avformat", avformat_metadata, NULL );
+#endif
+#ifdef FILTERS
+ MLT_REGISTER( filter_type, "avcolour_space", create_service );
+ MLT_REGISTER( filter_type, "avcolor_space", create_service );
+ MLT_REGISTER( filter_type, "avdeinterlace", create_service );
+ MLT_REGISTER( filter_type, "avresample", create_service );
+#ifdef SWSCALE
+ MLT_REGISTER( filter_type, "swscale", create_service );
+#endif
+#endif
+}
--- /dev/null
+/*
+ * filter_avcolour_space.c -- Colour space filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+#ifdef SWSCALE
+#include <swscale.h>
+#endif
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_RGB32 PIX_FMT_RGBA32
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static inline int is_big_endian( )
+{
+ union { int i; char c[ 4 ]; } big_endian_test;
+ big_endian_test.i = 1;
+
+ return big_endian_test.c[ 0 ] != 1;
+}
+
+static inline int convert_mlt_to_av_cs( mlt_image_format format )
+{
+ int value = 0;
+
+ switch( format )
+ {
+ case mlt_image_rgb24:
+ value = PIX_FMT_RGB24;
+ break;
+ case mlt_image_rgb24a:
+ value = PIX_FMT_RGB32;
+ break;
+ case mlt_image_yuv422:
+ value = PIX_FMT_YUYV422;
+ break;
+ case mlt_image_yuv420p:
+ value = PIX_FMT_YUV420P;
+ break;
+ case mlt_image_opengl:
+ case mlt_image_none:
+ fprintf( stderr, "Invalid format...\n" );
+ break;
+ }
+
+ return value;
+}
+
+static inline void convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, int width, int height )
+{
+ AVPicture input;
+ AVPicture output;
+ avpicture_fill( &input, in, in_fmt, width, height );
+ avpicture_fill( &output, out, out_fmt, width, height );
+#ifdef SWSCALE
+ struct SwsContext *context = sws_getContext( width, height, in_fmt,
+ width, height, out_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+ sws_scale( context, input.data, input.linesize, 0, height,
+ output.data, output.linesize);
+ sws_freeContext( context );
+#else
+ img_convert( &output, out_fmt, &input, in_fmt, width, height );
+#endif
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_filter filter = mlt_frame_pop_service( this );
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+ int output_format = *format;
+ mlt_image_format forced = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "forced" );
+ int error = 0;
+
+ // Allow this filter to force processing in a colour space other than requested
+ *format = forced != 0 ? forced : *format;
+
+ error = mlt_frame_get_image( this, image, format, width, height, 0 );
+
+ if ( error == 0 && *format != output_format && *image != NULL && output_format != mlt_image_opengl )
+ {
+ int in_fmt = convert_mlt_to_av_cs( *format );
+ int out_fmt = convert_mlt_to_av_cs( output_format );
+ int size = avpicture_get_size( out_fmt, *width, *height );
+ uint8_t *output = mlt_pool_alloc( size );
+ convert_image( output, *image, out_fmt, in_fmt, *width, *height );
+
+ // Special case for alpha rgb input
+ if ( *format == mlt_image_rgb24a )
+ {
+ register uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+ register int len = *width * *height;
+ register uint8_t *bits = *image;
+ register int n = ( len + 7 ) / 8;
+
+ if( !is_big_endian( ) )
+ bits += 3;
+
+ // Extract alpha mask from the image using Duff's Device
+ switch( len % 8 )
+ {
+ case 0: do { *alpha ++ = *bits; bits += 4;
+ case 7: *alpha ++ = *bits; bits += 4;
+ case 6: *alpha ++ = *bits; bits += 4;
+ case 5: *alpha ++ = *bits; bits += 4;
+ case 4: *alpha ++ = *bits; bits += 4;
+ case 3: *alpha ++ = *bits; bits += 4;
+ case 2: *alpha ++ = *bits; bits += 4;
+ case 1: *alpha ++ = *bits; bits += 4;
+ }
+ while( --n );
+ }
+ }
+
+ // Update the output
+ *image = output;
+ *format = output_format;
+ mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "format", output_format );
+
+ // Special case for alpha rgb output
+ if ( *format == mlt_image_rgb24a )
+ {
+ // Fetch the alpha
+ register uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+
+ if ( alpha != NULL )
+ {
+ register uint8_t *bits = *image;
+ register int len = *width * *height;
+ register int n = ( len + 7 ) / 8;
+
+ if( !is_big_endian( ) )
+ bits += 3;
+
+ // Merge the alpha mask into the RGB image using Duff's Device
+ switch( len % 8 )
+ {
+ case 0: do { *bits = *alpha++; bits += 4;
+ case 7: *bits = *alpha++; bits += 4;
+ case 6: *bits = *alpha++; bits += 4;
+ case 5: *bits = *alpha++; bits += 4;
+ case 4: *bits = *alpha++; bits += 4;
+ case 3: *bits = *alpha++; bits += 4;
+ case 2: *bits = *alpha++; bits += 4;
+ case 1: *bits = *alpha++; bits += 4;
+ }
+ while( --n );
+ }
+ }
+ }
+ }
+ else if ( error == 0 && *format != output_format && *image != NULL && output_format == mlt_image_opengl )
+ {
+ if ( *format == mlt_image_yuv422 )
+ {
+ int size = *width * *height * 4;
+ uint8_t *output = mlt_pool_alloc( size );
+ int h = *height;
+ int w = *width;
+ uint8_t *o = output + size;
+ int ostride = w * 4;
+ uint8_t *p = *image;
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this ) + *width * *height;
+ int r, g, b;
+
+ while( h -- )
+ {
+ w = *width;
+ o -= ostride;
+ alpha -= *width;
+ while( w >= 2 )
+ {
+ YUV2RGB( *p, *( p + 1 ), *( p + 3 ), r, g, b );
+ *o ++ = r;
+ *o ++ = g;
+ *o ++ = b;
+ *o ++ = *alpha ++;
+ YUV2RGB( *( p + 2 ), *( p + 1 ), *( p + 3 ), r, g, b );
+ *o ++ = r;
+ *o ++ = g;
+ *o ++ = b;
+ *o ++ = *alpha ++;
+ w -= 2;
+ p += 4;
+ }
+ o -= ostride;
+ alpha -= *width;
+ }
+
+ mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "format", output_format );
+ *image = output;
+ *format = output_format;
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_avcolour_space_init( void *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ this->process = filter_process;
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_avdeinterlace.c -- deinterlace filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+
+#ifdef USE_MMX
+#include "mmx.h"
+#else
+#define MAX_NEG_CROP 1024
+extern uint8_t ff_cropTbl[256 + 2 * MAX_NEG_CROP];
+#endif
+
+#ifdef USE_MMX
+#define DEINT_INPLACE_LINE_LUM \
+ movd_m2r(lum_m4[0],mm0);\
+ movd_m2r(lum_m3[0],mm1);\
+ movd_m2r(lum_m2[0],mm2);\
+ movd_m2r(lum_m1[0],mm3);\
+ movd_m2r(lum[0],mm4);\
+ punpcklbw_r2r(mm7,mm0);\
+ movd_r2m(mm2,lum_m4[0]);\
+ punpcklbw_r2r(mm7,mm1);\
+ punpcklbw_r2r(mm7,mm2);\
+ punpcklbw_r2r(mm7,mm3);\
+ punpcklbw_r2r(mm7,mm4);\
+ paddw_r2r(mm3,mm1);\
+ psllw_i2r(1,mm2);\
+ paddw_r2r(mm4,mm0);\
+ psllw_i2r(2,mm1);\
+ paddw_r2r(mm6,mm2);\
+ paddw_r2r(mm2,mm1);\
+ psubusw_r2r(mm0,mm1);\
+ psrlw_i2r(3,mm1);\
+ packuswb_r2r(mm7,mm1);\
+ movd_r2m(mm1,lum_m2[0]);
+
+#define DEINT_LINE_LUM \
+ movd_m2r(lum_m4[0],mm0);\
+ movd_m2r(lum_m3[0],mm1);\
+ movd_m2r(lum_m2[0],mm2);\
+ movd_m2r(lum_m1[0],mm3);\
+ movd_m2r(lum[0],mm4);\
+ punpcklbw_r2r(mm7,mm0);\
+ punpcklbw_r2r(mm7,mm1);\
+ punpcklbw_r2r(mm7,mm2);\
+ punpcklbw_r2r(mm7,mm3);\
+ punpcklbw_r2r(mm7,mm4);\
+ paddw_r2r(mm3,mm1);\
+ psllw_i2r(1,mm2);\
+ paddw_r2r(mm4,mm0);\
+ psllw_i2r(2,mm1);\
+ paddw_r2r(mm6,mm2);\
+ paddw_r2r(mm2,mm1);\
+ psubusw_r2r(mm0,mm1);\
+ psrlw_i2r(3,mm1);\
+ packuswb_r2r(mm7,mm1);\
+ movd_r2m(mm1,dst[0]);
+#endif
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+/* filter parameters: [-1 4 2 4 -1] // 8 */
+static inline void deinterlace_line(uint8_t *dst,
+ const uint8_t *lum_m4, const uint8_t *lum_m3,
+ const uint8_t *lum_m2, const uint8_t *lum_m1,
+ const uint8_t *lum,
+ int size)
+{
+#ifndef USE_MMX
+ uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
+ int sum;
+
+ for(;size > 0;size--) {
+ sum = -lum_m4[0];
+ sum += lum_m3[0] << 2;
+ sum += lum_m2[0] << 1;
+ sum += lum_m1[0] << 2;
+ sum += -lum[0];
+ dst[0] = cm[(sum + 4) >> 3];
+ lum_m4++;
+ lum_m3++;
+ lum_m2++;
+ lum_m1++;
+ lum++;
+ dst++;
+ }
+#else
+
+ {
+ mmx_t rounder;
+ rounder.uw[0]=4;
+ rounder.uw[1]=4;
+ rounder.uw[2]=4;
+ rounder.uw[3]=4;
+ pxor_r2r(mm7,mm7);
+ movq_m2r(rounder,mm6);
+ }
+ for (;size > 3; size-=4) {
+ DEINT_LINE_LUM
+ lum_m4+=4;
+ lum_m3+=4;
+ lum_m2+=4;
+ lum_m1+=4;
+ lum+=4;
+ dst+=4;
+ }
+#endif
+}
+static inline void deinterlace_line_inplace(uint8_t *lum_m4, uint8_t *lum_m3, uint8_t *lum_m2, uint8_t *lum_m1, uint8_t *lum,
+ int size)
+{
+#ifndef USE_MMX
+ uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
+ int sum;
+
+ for(;size > 0;size--) {
+ sum = -lum_m4[0];
+ sum += lum_m3[0] << 2;
+ sum += lum_m2[0] << 1;
+ lum_m4[0]=lum_m2[0];
+ sum += lum_m1[0] << 2;
+ sum += -lum[0];
+ lum_m2[0] = cm[(sum + 4) >> 3];
+ lum_m4++;
+ lum_m3++;
+ lum_m2++;
+ lum_m1++;
+ lum++;
+ }
+#else
+
+ {
+ mmx_t rounder;
+ rounder.uw[0]=4;
+ rounder.uw[1]=4;
+ rounder.uw[2]=4;
+ rounder.uw[3]=4;
+ pxor_r2r(mm7,mm7);
+ movq_m2r(rounder,mm6);
+ }
+ for (;size > 3; size-=4) {
+ DEINT_INPLACE_LINE_LUM
+ lum_m4+=4;
+ lum_m3+=4;
+ lum_m2+=4;
+ lum_m1+=4;
+ lum+=4;
+ }
+#endif
+}
+
+/* deinterlacing : 2 temporal taps, 3 spatial taps linear filter. The
+ top field is copied as is, but the bottom field is deinterlaced
+ against the top field. */
+static inline void deinterlace_bottom_field(uint8_t *dst, int dst_wrap,
+ const uint8_t *src1, int src_wrap,
+ int width, int height)
+{
+ const uint8_t *src_m2, *src_m1, *src_0, *src_p1, *src_p2;
+ int y;
+
+ src_m2 = src1;
+ src_m1 = src1;
+ src_0=&src_m1[src_wrap];
+ src_p1=&src_0[src_wrap];
+ src_p2=&src_p1[src_wrap];
+ for(y=0;y<(height-2);y+=2) {
+ memcpy(dst,src_m1,width);
+ dst += dst_wrap;
+ deinterlace_line(dst,src_m2,src_m1,src_0,src_p1,src_p2,width);
+ src_m2 = src_0;
+ src_m1 = src_p1;
+ src_0 = src_p2;
+ src_p1 += 2*src_wrap;
+ src_p2 += 2*src_wrap;
+ dst += dst_wrap;
+ }
+ memcpy(dst,src_m1,width);
+ dst += dst_wrap;
+ /* do last line */
+ deinterlace_line(dst,src_m2,src_m1,src_0,src_0,src_0,width);
+}
+
+static inline void deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap,
+ int width, int height)
+{
+ uint8_t *src_m1, *src_0, *src_p1, *src_p2;
+ int y;
+ uint8_t *buf;
+ buf = (uint8_t*)av_malloc(width);
+
+ src_m1 = src1;
+ memcpy(buf,src_m1,width);
+ src_0=&src_m1[src_wrap];
+ src_p1=&src_0[src_wrap];
+ src_p2=&src_p1[src_wrap];
+ for(y=0;y<(height-2);y+=2) {
+ deinterlace_line_inplace(buf,src_m1,src_0,src_p1,src_p2,width);
+ src_m1 = src_p1;
+ src_0 = src_p2;
+ src_p1 += 2*src_wrap;
+ src_p2 += 2*src_wrap;
+ }
+ /* do last line */
+ deinterlace_line_inplace(buf,src_m1,src_0,src_0,src_0,width);
+ av_free(buf);
+}
+
+
+/* deinterlace - if not supported return -1 */
+static int mlt_avpicture_deinterlace(AVPicture *dst, const AVPicture *src,
+ int pix_fmt, int width, int height)
+{
+ int i;
+
+ if (pix_fmt != PIX_FMT_YUV420P &&
+ pix_fmt != PIX_FMT_YUV422P &&
+ pix_fmt != PIX_FMT_YUYV422 &&
+ pix_fmt != PIX_FMT_YUV444P &&
+ pix_fmt != PIX_FMT_YUV411P)
+ return -1;
+ if ((width & 3) != 0 || (height & 3) != 0)
+ return -1;
+
+ if ( pix_fmt != PIX_FMT_YUYV422 )
+ {
+ for(i=0;i<3;i++) {
+ if (i == 1) {
+ switch(pix_fmt) {
+ case PIX_FMT_YUV420P:
+ width >>= 1;
+ height >>= 1;
+ break;
+ case PIX_FMT_YUV422P:
+ width >>= 1;
+ break;
+ case PIX_FMT_YUV411P:
+ width >>= 2;
+ break;
+ default:
+ break;
+ }
+ }
+ if (src == dst) {
+ deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i],
+ width, height);
+ } else {
+ deinterlace_bottom_field(dst->data[i],dst->linesize[i],
+ src->data[i], src->linesize[i],
+ width, height);
+ }
+ }
+ }
+ else {
+ if (src == dst) {
+ deinterlace_bottom_field_inplace(dst->data[0], dst->linesize[0],
+ width<<1, height);
+ } else {
+ deinterlace_bottom_field(dst->data[0],dst->linesize[0],
+ src->data[0], src->linesize[0],
+ width<<1, height);
+ }
+ }
+
+#ifdef USE_MMX
+ emms();
+#endif
+ return 0;
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = 0;
+ int deinterlace = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "consumer_deinterlace" );
+
+ // Determine if we need a writable version or not
+ if ( deinterlace && !writable )
+ writable = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" );
+
+ // Get the input image
+ error = mlt_frame_get_image( this, image, format, width, height, writable );
+
+ // Check that we want progressive and we aren't already progressive
+ if ( deinterlace && *format == mlt_image_yuv422 && *image != NULL && !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" ) )
+ {
+ // Create a picture
+ AVPicture *output = mlt_pool_alloc( sizeof( AVPicture ) );
+
+ // Fill the picture
+ if ( *format == mlt_image_yuv422 )
+ {
+ avpicture_fill( output, *image, PIX_FMT_YUYV422, *width, *height );
+ mlt_avpicture_deinterlace( output, output, PIX_FMT_YUYV422, *width, *height );
+ }
+
+ // Free the picture
+ mlt_pool_release( output );
+
+ // Make sure that others know the frame is deinterlaced
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "progressive", 1 );
+ }
+
+ return error;
+}
+
+/** Deinterlace filter processing - this should be lazy evaluation here...
+*/
+
+static mlt_frame deinterlace_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the get_image method on to the stack
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_avdeinterlace_init( void *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ this->process = deinterlace_process;
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_avresample.c -- adjust audio sample frequency
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+
+/** Get the audio.
+*/
+
+static int resample_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get the filter service
+ mlt_filter filter = mlt_frame_pop_audio( frame );
+
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the resample information
+ int output_rate = mlt_properties_get_int( filter_properties, "frequency" );
+ int16_t *sample_buffer = mlt_properties_get_data( filter_properties, "buffer", NULL );
+
+ // Obtain the resample context if it exists
+ ReSampleContext *resample = mlt_properties_get_data( filter_properties, "audio_resample", NULL );
+
+ // Used to return number of channels in the source
+ int channels_avail = *channels;
+
+ // Loop variable
+ int i;
+
+ // If no resample frequency is specified, default to requested value
+ if ( output_rate == 0 )
+ output_rate = *frequency;
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples );
+
+ // Duplicate channels as necessary
+ if ( channels_avail < *channels )
+ {
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+ int j, k = 0;
+
+ // Duplicate the existing channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * channels_avail ) + k ];
+ k = ( k + 1 ) % channels_avail;
+ }
+ }
+
+ // Update the audio buffer now - destroys the old
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ *buffer = new_buffer;
+ }
+ else if ( channels_avail == 6 && *channels == 2 )
+ {
+ // Nasty hack for ac3 5.1 audio - may be a cause of failure?
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+
+ // Drop all but the first *channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ new_buffer[ ( i * *channels ) + 0 ] = (*buffer)[ ( i * channels_avail ) + 2 ];
+ new_buffer[ ( i * *channels ) + 1 ] = (*buffer)[ ( i * channels_avail ) + 3 ];
+ }
+
+ // Update the audio buffer now - destroys the old
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ *buffer = new_buffer;
+ }
+
+ // Return now if no work to do
+ if ( output_rate != *frequency )
+ {
+ // Will store number of samples created
+ int used = 0;
+
+ // Create a resampler if nececessary
+ if ( resample == NULL || *frequency != mlt_properties_get_int( filter_properties, "last_frequency" ) )
+ {
+ // Create the resampler
+ resample = audio_resample_init( *channels, *channels, output_rate, *frequency );
+
+ // And store it on properties
+ mlt_properties_set_data( filter_properties, "audio_resample", resample, 0, ( mlt_destructor )audio_resample_close, NULL );
+
+ // And remember what it was created for
+ mlt_properties_set_int( filter_properties, "last_frequency", *frequency );
+ }
+
+ // Resample the audio
+ used = audio_resample( resample, sample_buffer, *buffer, *samples );
+
+ // Resize if necessary
+ if ( used > *samples )
+ {
+ *buffer = mlt_pool_realloc( *buffer, *samples * *channels * sizeof( int16_t ) );
+ mlt_properties_set_data( properties, "audio", *buffer, *channels * used * sizeof( int16_t ), mlt_pool_release, NULL );
+ }
+
+ // Copy samples
+ memcpy( *buffer, sample_buffer, *channels * used * sizeof( int16_t ) );
+
+ // Update output variables
+ *samples = used;
+ *frequency = output_rate;
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Only call this if we have a means to get audio
+ if ( mlt_frame_is_test_audio( frame ) == 0 )
+ {
+ // Push the filter on to the stack
+ mlt_frame_push_audio( frame, this );
+
+ // Assign our get_audio method
+ mlt_frame_push_audio( frame, resample_get_audio );
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_avresample_init( char *arg )
+{
+ // Create a filter
+ mlt_filter this = mlt_filter_new( );
+
+ // Initialise if successful
+ if ( this != NULL )
+ {
+ // Calculate size of the buffer
+ int size = AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t );
+
+ // Allocate the buffer
+ int16_t *buffer = mlt_pool_alloc( size );
+
+ // Assign the process method
+ this->process = filter_process;
+
+ // Deal with argument
+ if ( arg != NULL )
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "frequency", arg );
+
+ // Default to 2 channel output
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", 2 );
+
+ // Store the buffer
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "buffer", buffer, size, mlt_pool_release, NULL );
+ }
+
+ return this;
+}
--- /dev/null
+/*
+ * filter_swscale.c -- image scaling filter
+ * Copyright (C) 2008-2009 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_factory.h>
+
+
+// ffmpeg Header files
+#include <avformat.h>
+#include <swscale.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_RGB32 PIX_FMT_RGBA32
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+static inline int is_big_endian( )
+{
+ union { int i; char c[ 4 ]; } big_endian_test;
+ big_endian_test.i = 1;
+
+ return big_endian_test.c[ 0 ] != 1;
+}
+
+static inline int convert_mlt_to_av_cs( mlt_image_format format )
+{
+ int value = 0;
+
+ switch( format )
+ {
+ case mlt_image_rgb24:
+ value = PIX_FMT_RGB24;
+ break;
+ case mlt_image_rgb24a:
+ value = PIX_FMT_RGB32;
+ break;
+ case mlt_image_yuv422:
+ value = PIX_FMT_YUYV422;
+ break;
+ case mlt_image_yuv420p:
+ value = PIX_FMT_YUV420P;
+ break;
+ case mlt_image_opengl:
+ case mlt_image_none:
+ fprintf( stderr, "Invalid format...\n" );
+ break;
+ }
+
+ return value;
+}
+
+static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight )
+{
+ // Get the properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the requested interpolation method
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+
+ // Convert to the SwScale flag
+ int interp = SWS_BILINEAR;
+ if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
+ interp = SWS_POINT;
+ else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
+ interp = SWS_FAST_BILINEAR;
+ else if ( strcmp( interps, "bilinear" ) == 0 )
+ interp = SWS_BILINEAR;
+ else if ( strcmp( interps, "bicubic" ) == 0 )
+ interp = SWS_BICUBIC;
+ else if ( strcmp( interps, "bicublin" ) == 0 )
+ interp = SWS_BICUBLIN;
+ else if ( strcmp( interps, "gauss" ) == 0 )
+ interp = SWS_GAUSS;
+ else if ( strcmp( interps, "sinc" ) == 0 )
+ interp = SWS_SINC;
+ else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "lanczos" ) == 0 )
+ interp = SWS_LANCZOS;
+ else if ( strcmp( interps, "spline" ) == 0 )
+ interp = SWS_SPLINE;
+
+ AVPicture input;
+ AVPicture output;
+ uint8_t *outbuf = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+ // Convert the pixel formats
+ iformat = convert_mlt_to_av_cs( iformat );
+ oformat = convert_mlt_to_av_cs( oformat );
+
+ avpicture_fill( &input, *image, iformat, iwidth, iheight );
+ avpicture_fill( &output, outbuf, oformat, owidth, oheight );
+
+ // Extract the alpha channel
+ if ( iformat == PIX_FMT_RGB32 && oformat == PIX_FMT_YUYV422 )
+ {
+ // Allocate the alpha mask
+ uint8_t *alpha = mlt_pool_alloc( iwidth * ( iheight + 1 ) );
+ if ( alpha )
+ {
+ // Convert the image and extract alpha
+ mlt_convert_rgb24a_to_yuv422( *image, iwidth, iheight, iwidth * 4, outbuf, alpha );
+ mlt_properties_set_data( properties, "alpha", alpha, iwidth * ( iheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+ iformat = PIX_FMT_YUYV422;
+ avpicture_fill( &input, outbuf, iformat, iwidth, iheight );
+ avpicture_fill( &output, *image, oformat, owidth, oheight );
+ }
+ }
+
+ // Create the context and output image
+ struct SwsContext *context = sws_getContext( iwidth, iheight, iformat, owidth, oheight, oformat, interp, NULL, NULL, NULL);
+ assert(context);
+
+ // Perform the scaling
+ sws_scale( context, input.data, input.linesize, 0, iheight, output.data, output.linesize);
+ sws_freeContext( context );
+
+ // Now update the frame
+ mlt_properties_set_data( properties, "image", output.data[0], owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+
+ // Return the output
+ *image = output.data[0];
+
+ // Scale the alpha channel only if exists and not correct size
+ int alpha_size = 0;
+ mlt_properties_get_data( properties, "alpha", &alpha_size );
+ if ( alpha_size > 0 && alpha_size != ( owidth * oheight ) )
+ {
+ // Create the context and output image
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+ if ( alpha )
+ {
+ iformat = oformat = PIX_FMT_GRAY8;
+ struct SwsContext *context = sws_getContext( iwidth, iheight, iformat, owidth, oheight, oformat, interp, NULL, NULL, NULL);
+ avpicture_fill( &input, alpha, iformat, iwidth, iheight );
+ outbuf = mlt_pool_alloc( owidth * oheight );
+ avpicture_fill( &output, outbuf, oformat, owidth, oheight );
+
+ // Perform the scaling
+ sws_scale( context, input.data, input.linesize, 0, iheight, output.data, output.linesize);
+ sws_freeContext( context );
+
+ // Set it back on the frame
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", output.data[0], owidth * oheight, mlt_pool_release, NULL );
+ }
+ }
+
+ return 0;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_swscale_init( mlt_profile profile, void *arg )
+{
+ // Create a new scaler
+ mlt_filter this = mlt_factory_filter( profile, "rescale", arg );
+
+ // If successful, then initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Set the inerpolation
+ mlt_properties_set( properties, "interpolation", arg == NULL ? "bilinear" : arg );
+
+ // Set the method
+ mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL );
+ }
+
+ return this;
+}
--- /dev/null
+/*
+ * mmx.h
+ * Copyright (C) 1997-2001 H. Dietz and R. Fisher
+ */
+#ifndef AVCODEC_I386MMX_H
+#define AVCODEC_I386MMX_H
+
+/*
+ * The type of an value that fits in an MMX register (note that long
+ * long constant values MUST be suffixed by LL and unsigned long long
+ * values by ULL, lest they be truncated by the compiler)
+ */
+
+typedef union {
+ long long q; /* Quadword (64-bit) value */
+ unsigned long long uq; /* Unsigned Quadword */
+ int d[2]; /* 2 Doubleword (32-bit) values */
+ unsigned int ud[2]; /* 2 Unsigned Doubleword */
+ short w[4]; /* 4 Word (16-bit) values */
+ unsigned short uw[4]; /* 4 Unsigned Word */
+ char b[8]; /* 8 Byte (8-bit) values */
+ unsigned char ub[8]; /* 8 Unsigned Byte */
+ float s[2]; /* Single-precision (32-bit) value */
+} mmx_t; /* On an 8-byte (64-bit) boundary */
+
+
+#define mmx_i2r(op,imm,reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "i" (imm) )
+
+#define mmx_m2r(op,mem,reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "m" (mem))
+
+#define mmx_r2m(op,reg,mem) \
+ __asm__ __volatile__ (#op " %%" #reg ", %0" \
+ : "=m" (mem) \
+ : /* nothing */ )
+
+#define mmx_r2r(op,regs,regd) \
+ __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+
+#define emms() __asm__ __volatile__ ("emms")
+
+#define movd_m2r(var,reg) mmx_m2r (movd, var, reg)
+#define movd_r2m(reg,var) mmx_r2m (movd, reg, var)
+#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd)
+
+#define movq_m2r(var,reg) mmx_m2r (movq, var, reg)
+#define movq_r2m(reg,var) mmx_r2m (movq, reg, var)
+#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd)
+
+#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg)
+#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd)
+#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg)
+#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd)
+
+#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg)
+#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd)
+
+#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg)
+#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd)
+#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg)
+#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd)
+#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg)
+#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd)
+
+#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg)
+#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd)
+#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg)
+#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd)
+
+#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg)
+#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd)
+#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg)
+#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd)
+
+#define pand_m2r(var,reg) mmx_m2r (pand, var, reg)
+#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd)
+
+#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg)
+#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd)
+
+#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg)
+#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd)
+#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg)
+#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd)
+#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg)
+#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd)
+
+#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg)
+#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd)
+#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg)
+#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd)
+#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg)
+#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd)
+
+#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg)
+#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd)
+
+#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg)
+#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd)
+
+#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg)
+#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd)
+
+#define por_m2r(var,reg) mmx_m2r (por, var, reg)
+#define por_r2r(regs,regd) mmx_r2r (por, regs, regd)
+
+#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg)
+#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg)
+#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd)
+#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg)
+#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg)
+#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd)
+#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg)
+#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg)
+#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd)
+
+#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg)
+#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg)
+#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd)
+#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg)
+#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg)
+#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd)
+
+#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg)
+#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg)
+#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd)
+#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg)
+#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg)
+#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd)
+#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg)
+#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg)
+#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd)
+
+#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg)
+#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd)
+#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg)
+#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd)
+#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg)
+#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd)
+
+#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg)
+#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd)
+#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg)
+#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd)
+
+#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg)
+#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd)
+#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg)
+#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd)
+
+#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg)
+#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd)
+#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg)
+#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd)
+#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg)
+#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd)
+
+#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg)
+#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd)
+#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg)
+#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd)
+#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg)
+#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd)
+
+#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg)
+#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd)
+
+
+/* 3DNOW extensions */
+
+#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg)
+#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd)
+
+
+/* AMD MMX extensions - also available in intel SSE */
+
+
+#define mmx_m2ri(op,mem,reg,imm) \
+ __asm__ __volatile__ (#op " %1, %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (mem), "X" (imm))
+#define mmx_r2ri(op,regs,regd,imm) \
+ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+ : /* nothing */ \
+ : "X" (imm) )
+
+#define mmx_fetch(mem,hint) \
+ __asm__ __volatile__ ("prefetch" #hint " %0" \
+ : /* nothing */ \
+ : "X" (mem))
+
+
+#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg)
+
+#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var)
+
+#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg)
+#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd)
+#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg)
+#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd)
+
+#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm)
+
+#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm)
+
+#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg)
+#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd)
+
+#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg)
+#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd)
+
+#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg)
+#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd)
+
+#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg)
+#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd)
+
+#define pmovmskb(mmreg,reg) \
+ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
+
+#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg)
+#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd)
+
+#define prefetcht0(mem) mmx_fetch (mem, t0)
+#define prefetcht1(mem) mmx_fetch (mem, t1)
+#define prefetcht2(mem) mmx_fetch (mem, t2)
+#define prefetchnta(mem) mmx_fetch (mem, nta)
+
+#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg)
+#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd)
+
+#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm)
+#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm)
+
+#define sfence() __asm__ __volatile__ ("sfence\n\t")
+
+#endif /* AVCODEC_I386MMX_H */
--- /dev/null
+/*
+ * producer_avformat.c -- avformat producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// MLT Header files
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+#include <opt.h>
+#ifdef SWSCALE
+# include <swscale.h>
+#endif
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+# include "audioconvert.h"
+#endif
+
+// System header files
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+void avformat_lock( );
+void avformat_unlock( );
+
+// Forward references.
+static int producer_open( mlt_producer this, mlt_profile profile, char *file );
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+
+/** Constructor for libavformat.
+*/
+
+mlt_producer producer_avformat_init( mlt_profile profile, char *file )
+{
+ int error = 0;
+
+ // Report information about available demuxers and codecs as YAML Tiny
+ if ( file && strstr( file, "f-list" ) )
+ {
+ fprintf( stderr, "---\nformats:\n" );
+ AVInputFormat *format = NULL;
+ while ( ( format = av_iformat_next( format ) ) )
+ fprintf( stderr, " - %s\n", format->name );
+ fprintf( stderr, "...\n" );
+ error = 1;
+ }
+ if ( file && strstr( file, "acodec-list" ) )
+ {
+ fprintf( stderr, "---\naudio_codecs:\n" );
+ AVCodec *codec = NULL;
+ while ( ( codec = av_codec_next( codec ) ) )
+ if ( codec->decode && codec->type == CODEC_TYPE_AUDIO )
+ fprintf( stderr, " - %s\n", codec->name );
+ fprintf( stderr, "...\n" );
+ error = 1;
+ }
+ if ( file && strstr( file, "vcodec-list" ) )
+ {
+ fprintf( stderr, "---\nvideo_codecs:\n" );
+ AVCodec *codec = NULL;
+ while ( ( codec = av_codec_next( codec ) ) )
+ if ( codec->decode && codec->type == CODEC_TYPE_VIDEO )
+ fprintf( stderr, " - %s\n", codec->name );
+ fprintf( stderr, "...\n" );
+ error = 1;
+ }
+ if ( error )
+ return NULL;
+
+ mlt_producer this = NULL;
+
+ // Check that we have a non-NULL argument
+ if ( file != NULL )
+ {
+ // Construct the producer
+ this = calloc( 1, sizeof( struct mlt_producer_s ) );
+
+ // Initialise it
+ if ( mlt_producer_init( this, NULL ) == 0 )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Set the resource property (required for all producers)
+ mlt_properties_set( properties, "resource", file );
+
+ // Register our get_frame implementation
+ this->get_frame = producer_get_frame;
+
+ // Open the file
+ if ( producer_open( this, profile, file ) != 0 )
+ {
+ // Clean up
+ mlt_producer_close( this );
+ this = NULL;
+ }
+ else
+ {
+ // Close the file to release resources for large playlists - reopen later as needed
+ mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "audio_context", NULL, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "video_context", NULL, 0, NULL, NULL );
+
+ // Default the user-selectable indices from the auto-detected indices
+ mlt_properties_set_int( properties, "audio_index", mlt_properties_get_int( properties, "_audio_index" ) );
+ mlt_properties_set_int( properties, "video_index", mlt_properties_get_int( properties, "_video_index" ) );
+ }
+ }
+ }
+
+ return this;
+}
+
+/** Find the default streams.
+*/
+
+static mlt_properties find_default_streams( mlt_properties meta_media, AVFormatContext *context, int *audio_index, int *video_index )
+{
+ int i;
+ char key[200];
+
+ mlt_properties_set_int( meta_media, "meta.media.nb_streams", context->nb_streams );
+
+ // Allow for multiple audio and video streams in the file and select first of each (if available)
+ for( i = 0; i < context->nb_streams; i++ )
+ {
+ // Get the codec context
+ AVStream *stream = context->streams[ i ];
+ if ( ! stream ) continue;
+ AVCodecContext *codec_context = stream->codec;
+ if ( ! codec_context ) continue;
+ AVCodec *codec = avcodec_find_decoder( codec_context->codec_id );
+ if ( ! codec ) continue;
+
+ snprintf( key, sizeof(key), "meta.media.%d.stream.type", i );
+
+ // Determine the type and obtain the first index of each type
+ switch( codec_context->codec_type )
+ {
+ case CODEC_TYPE_VIDEO:
+ if ( *video_index < 0 )
+ *video_index = i;
+ mlt_properties_set( meta_media, key, "video" );
+ snprintf( key, sizeof(key), "meta.media.%d.stream.frame_rate", i );
+ mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->r_frame_rate ) );
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+ snprintf( key, sizeof(key), "meta.media.%d.stream.sample_aspect_ratio", i );
+ mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->sample_aspect_ratio ) );
+#endif
+ snprintf( key, sizeof(key), "meta.media.%d.codec.pix_fmt", i );
+ mlt_properties_set( meta_media, key, avcodec_get_pix_fmt_name( codec_context->pix_fmt ) );
+ snprintf( key, sizeof(key), "meta.media.%d.codec.sample_aspect_ratio", i );
+ mlt_properties_set_double( meta_media, key, av_q2d( codec_context->sample_aspect_ratio ) );
+ break;
+ case CODEC_TYPE_AUDIO:
+ if ( *audio_index < 0 )
+ *audio_index = i;
+ mlt_properties_set( meta_media, key, "audio" );
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+ snprintf( key, sizeof(key), "meta.media.%d.codec.sample_fmt", i );
+ mlt_properties_set( meta_media, key, avcodec_get_sample_fmt_name( codec_context->sample_fmt ) );
+#endif
+ snprintf( key, sizeof(key), "meta.media.%d.codec.sample_rate", i );
+ mlt_properties_set_int( meta_media, key, codec_context->sample_rate );
+ snprintf( key, sizeof(key), "meta.media.%d.codec.channels", i );
+ mlt_properties_set_int( meta_media, key, codec_context->channels );
+ break;
+ default:
+ break;
+ }
+// snprintf( key, sizeof(key), "meta.media.%d.stream.time_base", i );
+// mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->time_base ) );
+ snprintf( key, sizeof(key), "meta.media.%d.codec.name", i );
+ mlt_properties_set( meta_media, key, codec->name );
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(55<<8)+0))
+ snprintf( key, sizeof(key), "meta.media.%d.codec.long_name", i );
+ mlt_properties_set( meta_media, key, codec->long_name );
+#endif
+ snprintf( key, sizeof(key), "meta.media.%d.codec.bit_rate", i );
+ mlt_properties_set_int( meta_media, key, codec_context->bit_rate );
+// snprintf( key, sizeof(key), "meta.media.%d.codec.time_base", i );
+// mlt_properties_set_double( meta_media, key, av_q2d( codec_context->time_base ) );
+ snprintf( key, sizeof(key), "meta.media.%d.codec.profile", i );
+ mlt_properties_set_int( meta_media, key, codec_context->profile );
+ snprintf( key, sizeof(key), "meta.media.%d.codec.level", i );
+ mlt_properties_set_int( meta_media, key, codec_context->level );
+ }
+
+ return meta_media;
+}
+
+/** Producer file destructor.
+*/
+
+static void producer_file_close( void *context )
+{
+ if ( context != NULL )
+ {
+ // Lock the mutex now
+ avformat_lock( );
+
+ // Close the file
+ av_close_input_file( context );
+
+ // Unlock the mutex now
+ avformat_unlock( );
+ }
+}
+
+/** Producer file destructor.
+*/
+
+static void producer_codec_close( void *codec )
+{
+ if ( codec != NULL )
+ {
+ // Lock the mutex now
+ avformat_lock( );
+
+ // Close the file
+ avcodec_close( codec );
+
+ // Unlock the mutex now
+ avformat_unlock( );
+ }
+}
+
+static inline int dv_is_pal( AVPacket *pkt )
+{
+ return pkt->data[3] & 0x80;
+}
+
+static int dv_is_wide( AVPacket *pkt )
+{
+ int i = 80 /* block size */ *3 /* VAUX starts at block 3 */ +3 /* skip block header */;
+
+ for ( ; i < pkt->size; i += 5 /* packet size */ )
+ {
+ if ( pkt->data[ i ] == 0x61 )
+ {
+ uint8_t x = pkt->data[ i + 2 ] & 0x7;
+ return ( x == 2 ) || ( x == 7 );
+ }
+ }
+ return 0;
+}
+
+static double get_aspect_ratio( AVStream *stream, AVCodecContext *codec_context, AVPacket *pkt )
+{
+ double aspect_ratio = 1.0;
+
+ if ( codec_context->codec_id == CODEC_ID_DVVIDEO )
+ {
+ if ( pkt )
+ {
+ if ( dv_is_pal( pkt ) )
+ {
+ aspect_ratio = dv_is_wide( pkt )
+ ? 64.0/45.0 // 16:9 PAL
+ : 16.0/15.0; // 4:3 PAL
+ }
+ else
+ {
+ aspect_ratio = dv_is_wide( pkt )
+ ? 32.0/27.0 // 16:9 NTSC
+ : 8.0/9.0; // 4:3 NTSC
+ }
+ }
+ else
+ {
+ AVRational ar =
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+ stream->sample_aspect_ratio;
+#else
+ codec_context->sample_aspect_ratio;
+#endif
+ // Override FFmpeg's notion of DV aspect ratios, which are
+ // based upon a width of 704. Since we do not have a normaliser
+ // that crops (nor is cropping 720 wide ITU-R 601 video always desirable)
+ // we just coerce the values to facilitate a passive behaviour through
+ // the rescale normaliser when using equivalent producers and consumers.
+ // = display_aspect / (width * height)
+ if ( ar.num == 10 && ar.den == 11 )
+ aspect_ratio = 8.0/9.0; // 4:3 NTSC
+ else if ( ar.num == 59 && ar.den == 54 )
+ aspect_ratio = 16.0/15.0; // 4:3 PAL
+ else if ( ar.num == 40 && ar.den == 33 )
+ aspect_ratio = 32.0/27.0; // 16:9 NTSC
+ else if ( ar.num == 118 && ar.den == 81 )
+ aspect_ratio = 64.0/45.0; // 16:9 PAL
+ }
+ }
+ else
+ {
+ AVRational codec_sar = codec_context->sample_aspect_ratio;
+ AVRational stream_sar =
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+ stream->sample_aspect_ratio;
+#else
+ { 0, 1 };
+#endif
+ if ( codec_sar.num > 0 )
+ aspect_ratio = av_q2d( codec_sar );
+ else if ( stream_sar.num > 0 )
+ aspect_ratio = av_q2d( stream_sar );
+ }
+ return aspect_ratio;
+}
+
+/** Open the file.
+*/
+
+static int producer_open( mlt_producer this, mlt_profile profile, char *file )
+{
+ // Return an error code (0 == no error)
+ int error = 0;
+
+ // Context for avformat
+ AVFormatContext *context = NULL;
+
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // We will treat everything with the producer fps
+ double fps = mlt_profile_fps( profile );
+
+ // Lock the mutex now
+ avformat_lock( );
+
+ // If "MRL", then create AVInputFormat
+ AVInputFormat *format = NULL;
+ AVFormatParameters *params = NULL;
+ char *standard = NULL;
+ char *mrl = strchr( file, ':' );
+
+ // AV option (0 = both, 1 = video, 2 = audio)
+ int av = 0;
+
+ // Setting lowest log level
+ av_log_set_level( -1 );
+
+ // Only if there is not a protocol specification that avformat can handle
+ if ( mrl && !url_exist( file ) )
+ {
+ // 'file' becomes format abbreviation
+ mrl[0] = 0;
+
+ // Lookup the format
+ format = av_find_input_format( file );
+
+ // Eat the format designator
+ file = ++mrl;
+
+ if ( format )
+ {
+ // Allocate params
+ params = calloc( sizeof( AVFormatParameters ), 1 );
+
+ // These are required by video4linux (defaults)
+ params->width = 640;
+ params->height = 480;
+ params->time_base= (AVRational){1,25};
+ // params->device = file;
+ params->channels = 2;
+ params->sample_rate = 48000;
+ }
+
+ // XXX: this does not work anymore since avdevice
+ // TODO: make producer_avddevice?
+ // Parse out params
+ mrl = strchr( file, '?' );
+ while ( mrl )
+ {
+ mrl[0] = 0;
+ char *name = strdup( ++mrl );
+ char *value = strchr( name, ':' );
+ if ( value )
+ {
+ value[0] = 0;
+ value++;
+ char *t = strchr( value, '&' );
+ if ( t )
+ t[0] = 0;
+ if ( !strcmp( name, "frame_rate" ) )
+ params->time_base.den = atoi( value );
+ else if ( !strcmp( name, "frame_rate_base" ) )
+ params->time_base.num = atoi( value );
+ else if ( !strcmp( name, "sample_rate" ) )
+ params->sample_rate = atoi( value );
+ else if ( !strcmp( name, "channels" ) )
+ params->channels = atoi( value );
+ else if ( !strcmp( name, "width" ) )
+ params->width = atoi( value );
+ else if ( !strcmp( name, "height" ) )
+ params->height = atoi( value );
+ else if ( !strcmp( name, "standard" ) )
+ {
+ standard = strdup( value );
+ params->standard = standard;
+ }
+ else if ( !strcmp( name, "av" ) )
+ av = atoi( value );
+ }
+ free( name );
+ mrl = strchr( mrl, '&' );
+ }
+ }
+
+ // Now attempt to open the file
+ error = av_open_input_file( &context, file, format, 0, params ) < 0;
+
+ // Cleanup AVFormatParameters
+ free( standard );
+ free( params );
+
+ // If successful, then try to get additional info
+ if ( error == 0 )
+ {
+ // Get the stream info
+ error = av_find_stream_info( context ) < 0;
+
+ // Continue if no error
+ if ( error == 0 )
+ {
+ // We will default to the first audio and video streams found
+ int audio_index = -1;
+ int video_index = -1;
+ int av_bypass = 0;
+
+ // Now set properties where we can (use default unknowns if required)
+ if ( context->duration != AV_NOPTS_VALUE )
+ {
+ // This isn't going to be accurate for all formats
+ mlt_position frames = ( mlt_position )( ( ( double )context->duration / ( double )AV_TIME_BASE ) * fps + 0.5 );
+ mlt_properties_set_position( properties, "out", frames - 1 );
+ mlt_properties_set_position( properties, "length", frames );
+ }
+
+ // Find default audio and video streams
+ find_default_streams( properties, context, &audio_index, &video_index );
+
+ if ( context->start_time != AV_NOPTS_VALUE )
+ mlt_properties_set_double( properties, "_start_time", context->start_time );
+
+ // Check if we're seekable (something funny about mpeg here :-/)
+ if ( strcmp( file, "pipe:" ) && strncmp( file, "http://", 6 ) && strncmp( file, "udp:", 4 ) && strncmp( file, "tcp:", 4 ) && strncmp( file, "rtsp:", 5 ) && strncmp( file, "rtp:", 4 ) )
+ {
+ mlt_properties_set_int( properties, "seekable", av_seek_frame( context, -1, mlt_properties_get_double( properties, "_start_time" ), AVSEEK_FLAG_BACKWARD ) >= 0 );
+ mlt_properties_set_data( properties, "dummy_context", context, 0, producer_file_close, NULL );
+ av_open_input_file( &context, file, NULL, 0, NULL );
+ av_find_stream_info( context );
+ }
+ else
+ av_bypass = 1;
+
+ // Store selected audio and video indexes on properties
+ mlt_properties_set_int( properties, "_audio_index", audio_index );
+ mlt_properties_set_int( properties, "_video_index", video_index );
+ mlt_properties_set_int( properties, "_last_position", -1 );
+
+ // Fetch the width, height and aspect ratio
+ if ( video_index != -1 )
+ {
+ AVCodecContext *codec_context = context->streams[ video_index ]->codec;
+ mlt_properties_set_int( properties, "width", codec_context->width );
+ mlt_properties_set_int( properties, "height", codec_context->height );
+
+ if ( codec_context->codec_id == CODEC_ID_DVVIDEO )
+ {
+ // Fetch the first frame of DV so we can read it directly
+ AVPacket pkt;
+ int ret = 0;
+ while ( ret >= 0 )
+ {
+ ret = av_read_frame( context, &pkt );
+ if ( ret >= 0 && pkt.stream_index == video_index && pkt.size > 0 )
+ {
+ mlt_properties_set_double( properties, "aspect_ratio",
+ get_aspect_ratio( context->streams[ video_index ], codec_context, &pkt ) );
+ break;
+ }
+ }
+ }
+ else
+ {
+ mlt_properties_set_double( properties, "aspect_ratio",
+ get_aspect_ratio( context->streams[ video_index ], codec_context, NULL ) );
+ }
+ }
+
+ // Read Metadata
+ if (context->title != NULL)
+ mlt_properties_set(properties, "meta.attr.title.markup", context->title );
+ if (context->author != NULL)
+ mlt_properties_set(properties, "meta.attr.author.markup", context->author );
+ if (context->copyright != NULL)
+ mlt_properties_set(properties, "meta.attr.copyright.markup", context->copyright );
+ if (context->comment != NULL)
+ mlt_properties_set(properties, "meta.attr.comment.markup", context->comment );
+ if (context->album != NULL)
+ mlt_properties_set(properties, "meta.attr.album.markup", context->album );
+ if (context->year != 0)
+ mlt_properties_set_int(properties, "meta.attr.year.markup", context->year );
+ if (context->track != 0)
+ mlt_properties_set_int(properties, "meta.attr.track.markup", context->track );
+
+ // We're going to cheat here - for a/v files, we will have two contexts (reasoning will be clear later)
+ if ( av == 0 && audio_index != -1 && video_index != -1 )
+ {
+ // We'll use the open one as our video_context
+ mlt_properties_set_data( properties, "video_context", context, 0, producer_file_close, NULL );
+
+ // And open again for our audio context
+ av_open_input_file( &context, file, NULL, 0, NULL );
+ av_find_stream_info( context );
+
+ // Audio context
+ mlt_properties_set_data( properties, "audio_context", context, 0, producer_file_close, NULL );
+ }
+ else if ( av != 2 && video_index != -1 )
+ {
+ // We only have a video context
+ mlt_properties_set_data( properties, "video_context", context, 0, producer_file_close, NULL );
+ }
+ else if ( audio_index != -1 )
+ {
+ // We only have an audio context
+ mlt_properties_set_data( properties, "audio_context", context, 0, producer_file_close, NULL );
+ }
+ else
+ {
+ // Something has gone wrong
+ error = -1;
+ }
+
+ mlt_properties_set_int( properties, "av_bypass", av_bypass );
+ }
+ }
+
+ // Unlock the mutex now
+ avformat_unlock( );
+
+ return error;
+}
+
+/** Convert a frame position to a time code.
+*/
+
+static double producer_time_of_frame( mlt_producer this, mlt_position position )
+{
+ return ( double )position / mlt_producer_get_fps( this );
+}
+
+static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, mlt_image_format format, int width, int height )
+{
+#ifdef SWSCALE
+ if ( format == mlt_image_yuv420p )
+ {
+ struct SwsContext *context = sws_getContext( width, height, pix_fmt,
+ width, height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+ AVPicture output;
+ output.data[0] = buffer;
+ output.data[1] = buffer + width * height;
+ output.data[2] = buffer + ( 3 * width * height ) / 2;
+ output.linesize[0] = width;
+ output.linesize[1] = width >> 1;
+ output.linesize[2] = width >> 1;
+ sws_scale( context, frame->data, frame->linesize, 0, height,
+ output.data, output.linesize);
+ sws_freeContext( context );
+ }
+ else if ( format == mlt_image_rgb24 )
+ {
+ struct SwsContext *context = sws_getContext( width, height, pix_fmt,
+ width, height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+ AVPicture output;
+ avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height );
+ sws_scale( context, frame->data, frame->linesize, 0, height,
+ output.data, output.linesize);
+ sws_freeContext( context );
+ }
+ else
+ {
+ struct SwsContext *context = sws_getContext( width, height, pix_fmt,
+ width, height, PIX_FMT_YUYV422, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+ AVPicture output;
+ avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height );
+ sws_scale( context, frame->data, frame->linesize, 0, height,
+ output.data, output.linesize);
+ sws_freeContext( context );
+ }
+#else
+ if ( format == mlt_image_yuv420p )
+ {
+ AVPicture pict;
+ pict.data[0] = buffer;
+ pict.data[1] = buffer + width * height;
+ pict.data[2] = buffer + ( 3 * width * height ) / 2;
+ pict.linesize[0] = width;
+ pict.linesize[1] = width >> 1;
+ pict.linesize[2] = width >> 1;
+ img_convert( &pict, PIX_FMT_YUV420P, (AVPicture *)frame, pix_fmt, width, height );
+ }
+ else if ( format == mlt_image_rgb24 )
+ {
+ AVPicture output;
+ avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height );
+ img_convert( &output, PIX_FMT_RGB24, (AVPicture *)frame, pix_fmt, width, height );
+ }
+ else
+ {
+ AVPicture output;
+ avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height );
+ img_convert( &output, PIX_FMT_YUYV422, (AVPicture *)frame, pix_fmt, width, height );
+ }
+#endif
+}
+
+/** Allocate the image buffer and set it on the frame.
+*/
+
+static int allocate_buffer( mlt_properties frame_properties, AVCodecContext *codec_context, uint8_t **buffer, mlt_image_format *format, int *width, int *height )
+{
+ int size = 0;
+
+ if ( codec_context->width == 0 || codec_context->height == 0 )
+ return size;
+
+ *width = codec_context->width;
+ *height = codec_context->height;
+ mlt_properties_set_int( frame_properties, "width", *width );
+ mlt_properties_set_int( frame_properties, "height", *height );
+
+ switch ( *format )
+ {
+ case mlt_image_yuv420p:
+ size = *width * 3 * ( *height + 1 ) / 2;
+ break;
+ case mlt_image_rgb24:
+ size = *width * ( *height + 1 ) * 3;
+ break;
+ default:
+ *format = mlt_image_yuv422;
+ size = *width * ( *height + 1 ) * 2;
+ break;
+ }
+
+ // Construct the output image
+ *buffer = mlt_pool_alloc( size );
+ if ( *buffer )
+ mlt_properties_set_data( frame_properties, "image", *buffer, size, (mlt_destructor)mlt_pool_release, NULL );
+ else
+ size = 0;
+
+ return size;
+}
+
+/** Get an image from a frame.
+*/
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the properties from the frame
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the frame number of this frame
+ mlt_position position = mlt_properties_get_position( frame_properties, "avformat_position" );
+
+ // Get the producer
+ mlt_producer this = mlt_properties_get_data( frame_properties, "avformat_producer", NULL );
+
+ // Get the producer properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Fetch the video_context
+ AVFormatContext *context = mlt_properties_get_data( properties, "video_context", NULL );
+
+ // Get the video_index
+ int index = mlt_properties_get_int( properties, "video_index" );
+
+ // Obtain the expected frame numer
+ mlt_position expected = mlt_properties_get_position( properties, "_video_expected" );
+
+ // Get the video stream
+ AVStream *stream = context->streams[ index ];
+
+ // Get codec context
+ AVCodecContext *codec_context = stream->codec;
+
+ // Packet
+ AVPacket pkt;
+
+ // Get the conversion frame
+ AVFrame *av_frame = mlt_properties_get_data( properties, "av_frame", NULL );
+
+ // Special case pause handling flag
+ int paused = 0;
+
+ // Special case ffwd handling
+ int ignore = 0;
+
+ // We may want to use the source fps if available
+ double source_fps = mlt_properties_get_double( properties, "source_fps" );
+ double fps = mlt_producer_get_fps( this );
+
+ // This is the physical frame position in the source
+ int req_position = ( int )( position / fps * source_fps + 0.5 );
+
+ // Get the seekable status
+ int seekable = mlt_properties_get_int( properties, "seekable" );
+
+ // Hopefully provide better support for streams...
+ int av_bypass = mlt_properties_get_int( properties, "av_bypass" );
+
+ // Determines if we have to decode all frames in a sequence
+ int must_decode = 1;
+
+ // Temporary hack to improve intra frame only
+ must_decode = strcmp( codec_context->codec->name, "dnxhd" ) &&
+ strcmp( codec_context->codec->name, "dvvideo" ) &&
+ strcmp( codec_context->codec->name, "huffyuv" ) &&
+ strcmp( codec_context->codec->name, "mjpeg" ) &&
+ strcmp( codec_context->codec->name, "rawvideo" );
+
+ // Seek if necessary
+ if ( position != expected )
+ {
+ if ( av_frame != NULL && position + 1 == expected )
+ {
+ // We're paused - use last image
+ paused = 1;
+ }
+ else if ( !seekable && position > expected && ( position - expected ) < 250 )
+ {
+ // Fast forward - seeking is inefficient for small distances - just ignore following frames
+ ignore = ( int )( ( position - expected ) / fps * source_fps );
+ }
+ else if ( seekable && ( position < expected || position - expected >= 12 ) )
+ {
+ // Calculate the timestamp for the requested frame
+ int64_t timestamp = ( int64_t )( ( double )req_position / source_fps * AV_TIME_BASE + 0.5 );
+ if ( ( uint64_t )context->start_time != AV_NOPTS_VALUE )
+ timestamp += context->start_time;
+ if ( must_decode )
+ timestamp -= AV_TIME_BASE;
+ if ( timestamp < 0 )
+ timestamp = 0;
+
+ // Set to the timestamp
+ av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD );
+
+ // Remove the cached info relating to the previous position
+ mlt_properties_set_int( properties, "_current_position", -1 );
+ mlt_properties_set_int( properties, "_last_position", -1 );
+ mlt_properties_set_data( properties, "av_frame", NULL, 0, NULL, NULL );
+ av_frame = NULL;
+ }
+ }
+
+ // Duplicate the last image if necessary (see comment on rawvideo below)
+ int current_position = mlt_properties_get_int( properties, "_current_position" );
+ int got_picture = mlt_properties_get_int( properties, "_got_picture" );
+ if ( av_frame != NULL && got_picture && ( paused || current_position >= req_position ) && av_bypass == 0 )
+ {
+ // Duplicate it
+ if ( allocate_buffer( frame_properties, codec_context, buffer, format, width, height ) )
+ convert_image( av_frame, *buffer, codec_context->pix_fmt, *format, *width, *height );
+ else
+ mlt_frame_get_image( frame, buffer, format, width, height, writable );
+ }
+ else
+ {
+ int ret = 0;
+ int int_position = 0;
+ got_picture = 0;
+
+ av_init_packet( &pkt );
+
+ // Construct an AVFrame for YUV422 conversion
+ if ( av_frame == NULL )
+ av_frame = avcodec_alloc_frame( );
+
+ while( ret >= 0 && !got_picture )
+ {
+ // Read a packet
+ ret = av_read_frame( context, &pkt );
+
+ // We only deal with video from the selected video_index
+ if ( ret >= 0 && pkt.stream_index == index && pkt.size > 0 )
+ {
+ // Determine time code of the packet
+ int_position = ( int )( av_q2d( stream->time_base ) * pkt.dts * source_fps + 0.5 );
+ if ( context->start_time != AV_NOPTS_VALUE )
+ int_position -= ( int )( context->start_time * source_fps / AV_TIME_BASE + 0.5 );
+ int last_position = mlt_properties_get_int( properties, "_last_position" );
+ if ( int_position == last_position )
+ int_position = last_position + 1;
+ mlt_properties_set_int( properties, "_last_position", int_position );
+
+ // Decode the image
+ if ( must_decode || int_position >= req_position )
+ ret = avcodec_decode_video( codec_context, av_frame, &got_picture, pkt.data, pkt.size );
+
+ if ( got_picture )
+ {
+ // Handle ignore
+ if ( int_position < req_position )
+ {
+ ignore = 0;
+ got_picture = 0;
+ }
+ else if ( int_position >= req_position )
+ {
+ ignore = 0;
+ }
+ else if ( ignore -- )
+ {
+ got_picture = 0;
+ }
+ }
+ av_free_packet( &pkt );
+ }
+ else if ( ret >= 0 )
+ {
+ av_free_packet( &pkt );
+ }
+
+ // Now handle the picture if we have one
+ if ( got_picture )
+ {
+ if ( allocate_buffer( frame_properties, codec_context, buffer, format, width, height ) )
+ {
+ convert_image( av_frame, *buffer, codec_context->pix_fmt, *format, *width, *height );
+ mlt_properties_set_int( frame_properties, "progressive", !av_frame->interlaced_frame );
+ mlt_properties_set_int( properties, "top_field_first", av_frame->top_field_first );
+ mlt_properties_set_int( properties, "_current_position", int_position );
+ mlt_properties_set_int( properties, "_got_picture", 1 );
+ mlt_properties_set_data( properties, "av_frame", av_frame, 0, av_free, NULL );
+ }
+ else
+ {
+ got_picture = 0;
+ }
+ }
+ }
+ if ( !got_picture )
+ mlt_frame_get_image( frame, buffer, format, width, height, writable );
+ }
+
+ // Very untidy - for rawvideo, the packet contains the frame, hence the free packet
+ // above will break the pause behaviour - so we wipe the frame now
+ if ( !strcmp( codec_context->codec->name, "rawvideo" ) )
+ mlt_properties_set_data( properties, "av_frame", NULL, 0, NULL, NULL );
+
+ // Set the field order property for this frame
+ mlt_properties_set_int( frame_properties, "top_field_first", mlt_properties_get_int( properties, "top_field_first" ) );
+
+ // Regardless of speed, we expect to get the next frame (cos we ain't too bright)
+ mlt_properties_set_position( properties, "_video_expected", position + 1 );
+
+ return 0;
+}
+
+/** Process properties as AVOptions and apply to AV context obj
+*/
+
+static void apply_properties( void *obj, mlt_properties properties, int flags )
+{
+ int i;
+ int count = mlt_properties_count( properties );
+ for ( i = 0; i < count; i++ )
+ {
+ const char *opt_name = mlt_properties_get_name( properties, i );
+ const AVOption *opt = av_find_opt( obj, opt_name, NULL, flags, flags );
+ if ( opt != NULL )
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(7<<8)+0)
+ av_set_string3( obj, opt_name, mlt_properties_get( properties, opt_name), 0, NULL );
+#elif LIBAVCODEC_VERSION_INT >= ((51<<16)+(59<<8)+0)
+ av_set_string2( obj, opt_name, mlt_properties_get( properties, opt_name), 0 );
+#else
+ av_set_string( obj, opt_name, mlt_properties_get( properties, opt_name) );
+#endif
+ }
+}
+
+/** Set up video handling.
+*/
+
+static void producer_set_up_video( mlt_producer this, mlt_frame frame )
+{
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Fetch the video_context
+ AVFormatContext *context = mlt_properties_get_data( properties, "video_context", NULL );
+
+ // Get the video_index
+ int index = mlt_properties_get_int( properties, "video_index" );
+
+ // Reopen the file if necessary
+ if ( !context && index > -1 )
+ {
+ mlt_events_block( properties, this );
+ producer_open( this, mlt_service_profile( MLT_PRODUCER_SERVICE(this) ),
+ mlt_properties_get( properties, "resource" ) );
+ context = mlt_properties_get_data( properties, "video_context", NULL );
+ mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL );
+ mlt_events_unblock( properties, this );
+
+ // Process properties as AVOptions
+ apply_properties( context, properties, AV_OPT_FLAG_DECODING_PARAM );
+ }
+
+ // Exception handling for video_index
+ if ( context && index >= (int) context->nb_streams )
+ {
+ // Get the last video stream
+ for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_VIDEO; --index );
+ mlt_properties_set_int( properties, "video_index", index );
+ }
+ if ( context && index > -1 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_VIDEO )
+ {
+ // Invalidate the video stream
+ index = -1;
+ mlt_properties_set_int( properties, "video_index", index );
+ }
+
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ if ( context && index > -1 )
+ {
+ // Get the video stream
+ AVStream *stream = context->streams[ index ];
+
+ // Get codec context
+ AVCodecContext *codec_context = stream->codec;
+
+ // Get the codec
+ AVCodec *codec = mlt_properties_get_data( properties, "video_codec", NULL );
+
+ // Update the video properties if the index changed
+ if ( index != mlt_properties_get_int( properties, "_video_index" ) )
+ {
+ // Reset the video properties if the index changed
+ mlt_properties_set_int( properties, "_video_index", index );
+ mlt_properties_set_data( properties, "video_codec", NULL, 0, NULL, NULL );
+ mlt_properties_set_int( properties, "width", codec_context->width );
+ mlt_properties_set_int( properties, "height", codec_context->height );
+ // TODO: get the first usable AVPacket and reset the stream position
+ mlt_properties_set_double( properties, "aspect_ratio",
+ get_aspect_ratio( context->streams[ index ], codec_context, NULL ) );
+ codec = NULL;
+ }
+
+ // Initialise the codec if necessary
+ if ( codec == NULL )
+ {
+ // Initialise multi-threading
+ int thread_count = mlt_properties_get_int( properties, "threads" );
+ if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) )
+ thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) );
+ if ( thread_count > 1 )
+ {
+ avcodec_thread_init( codec_context, thread_count );
+ codec_context->thread_count = thread_count;
+ }
+
+ // Find the codec
+ codec = avcodec_find_decoder( codec_context->codec_id );
+
+ // If we don't have a codec and we can't initialise it, we can't do much more...
+ avformat_lock( );
+ if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 )
+ {
+ // Now store the codec with its destructor
+ mlt_properties_set_data( properties, "video_codec", codec_context, 0, producer_codec_close, NULL );
+ }
+ else
+ {
+ // Remember that we can't use this later
+ mlt_properties_set_int( properties, "video_index", -1 );
+ index = -1;
+ }
+ avformat_unlock( );
+
+ // Process properties as AVOptions
+ apply_properties( codec_context, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM );
+ }
+
+ // No codec, no show...
+ if ( codec && index > -1 )
+ {
+ double source_fps = 0;
+ double force_aspect_ratio = mlt_properties_get_double( properties, "force_aspect_ratio" );
+ double aspect_ratio = ( force_aspect_ratio > 0.0 ) ?
+ force_aspect_ratio : mlt_properties_get_double( properties, "aspect_ratio" );
+
+ // Determine the fps
+ source_fps = ( double )codec_context->time_base.den / ( codec_context->time_base.num == 0 ? 1 : codec_context->time_base.num );
+
+ // We'll use fps if it's available
+ if ( source_fps > 0 )
+ mlt_properties_set_double( properties, "source_fps", source_fps );
+ else
+ mlt_properties_set_double( properties, "source_fps", mlt_producer_get_fps( this ) );
+ mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio );
+
+ // Set the width and height
+ mlt_properties_set_int( frame_properties, "width", codec_context->width );
+ mlt_properties_set_int( frame_properties, "height", codec_context->height );
+ mlt_properties_set_int( frame_properties, "real_width", codec_context->width );
+ mlt_properties_set_int( frame_properties, "real_height", codec_context->height );
+ mlt_properties_set_double( frame_properties, "aspect_ratio", aspect_ratio );
+
+ mlt_frame_push_get_image( frame, producer_get_image );
+ mlt_properties_set_data( frame_properties, "avformat_producer", this, 0, NULL, NULL );
+ }
+ else
+ {
+ mlt_properties_set_int( frame_properties, "test_image", 1 );
+ }
+ }
+ else
+ {
+ mlt_properties_set_int( frame_properties, "test_image", 1 );
+ }
+}
+
+/** Get the audio from a frame.
+*/
+
+static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties from the frame
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the frame number of this frame
+ mlt_position position = mlt_properties_get_position( frame_properties, "avformat_position" );
+
+ // Get the producer
+ mlt_producer this = mlt_properties_get_data( frame_properties, "avformat_producer", NULL );
+
+ // Get the producer properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Fetch the audio_context
+ AVFormatContext *context = mlt_properties_get_data( properties, "audio_context", NULL );
+
+ // Get the audio_index
+ int index = mlt_properties_get_int( properties, "audio_index" );
+
+ // Get the seekable status
+ int seekable = mlt_properties_get_int( properties, "seekable" );
+
+ // Obtain the expected frame numer
+ mlt_position expected = mlt_properties_get_position( properties, "_audio_expected" );
+
+ // Obtain the resample context if it exists (not always needed)
+ ReSampleContext *resample = mlt_properties_get_data( properties, "audio_resample", NULL );
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+ // Get the format converter context if it exists
+ AVAudioConvert *convert = mlt_properties_get_data( properties, "audio_convert", NULL );
+#endif
+
+ // Obtain the audio buffers
+ int16_t *audio_buffer = mlt_properties_get_data( properties, "audio_buffer", NULL );
+ int16_t *decode_buffer = mlt_properties_get_data( properties, "decode_buffer", NULL );
+ int16_t *convert_buffer = mlt_properties_get_data( properties, "convert_buffer", NULL );
+
+ // Get amount of audio used
+ int audio_used = mlt_properties_get_int( properties, "_audio_used" );
+
+ // Calculate the real time code
+ double real_timecode = producer_time_of_frame( this, position );
+
+ // Get the audio stream
+ AVStream *stream = context->streams[ index ];
+
+ // Get codec context
+ AVCodecContext *codec_context = stream->codec;
+
+ // Packet
+ AVPacket pkt;
+
+ // Number of frames to ignore (for ffwd)
+ int ignore = 0;
+
+ // Flag for paused (silence)
+ int paused = 0;
+
+ // Check for resample and create if necessary
+ if ( resample == NULL && codec_context->channels <= 2 )
+ {
+ // Create the resampler
+ resample = audio_resample_init( *channels, codec_context->channels, *frequency, codec_context->sample_rate );
+
+ // And store it on properties
+ mlt_properties_set_data( properties, "audio_resample", resample, 0, ( mlt_destructor )audio_resample_close, NULL );
+ }
+ else if ( resample == NULL )
+ {
+ *channels = codec_context->channels;
+ *frequency = codec_context->sample_rate;
+ }
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+ // Check for audio format converter and create if necessary
+ // TODO: support higher resolutions than 16-bit.
+ if ( convert == NULL && codec_context->sample_fmt != SAMPLE_FMT_S16 )
+ {
+ // Create single channel converter for interleaved with no mixing matrix
+ convert = av_audio_convert_alloc( SAMPLE_FMT_S16, 1, codec_context->sample_fmt, 1, NULL, 0 );
+ mlt_properties_set_data( properties, "audio_convert", convert, 0, ( mlt_destructor )av_audio_convert_free, NULL );
+ }
+#endif
+
+ // Check for audio buffer and create if necessary
+ if ( audio_buffer == NULL )
+ {
+ // Allocate the audio buffer
+ audio_buffer = mlt_pool_alloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
+
+ // And store it on properties for reuse
+ mlt_properties_set_data( properties, "audio_buffer", audio_buffer, 0, ( mlt_destructor )mlt_pool_release, NULL );
+ }
+
+ // Check for decoder buffer and create if necessary
+ if ( decode_buffer == NULL )
+ {
+ // Allocate the audio buffer
+ decode_buffer = av_malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
+
+ // And store it on properties for reuse
+ mlt_properties_set_data( properties, "decode_buffer", decode_buffer, 0, ( mlt_destructor )av_free, NULL );
+ }
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+ // Check for format converter buffer and create if necessary
+ if ( resample && convert && convert_buffer == NULL )
+ {
+ // Allocate the audio buffer
+ convert_buffer = mlt_pool_alloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
+
+ // And store it on properties for reuse
+ mlt_properties_set_data( properties, "convert_buffer", convert_buffer, 0, ( mlt_destructor )mlt_pool_release, NULL );
+ }
+#endif
+
+ // Seek if necessary
+ if ( position != expected )
+ {
+ if ( position + 1 == expected )
+ {
+ // We're paused - silence required
+ paused = 1;
+ }
+ else if ( !seekable && position > expected && ( position - expected ) < 250 )
+ {
+ // Fast forward - seeking is inefficient for small distances - just ignore following frames
+ ignore = position - expected;
+ }
+ else if ( position < expected || position - expected >= 12 )
+ {
+ int64_t timestamp = ( int64_t )( real_timecode * AV_TIME_BASE + 0.5 );
+ if ( context->start_time != AV_NOPTS_VALUE )
+ timestamp += context->start_time;
+ if ( timestamp < 0 )
+ timestamp = 0;
+
+ // Set to the real timecode
+ if ( av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD ) != 0 )
+ paused = 1;
+
+ // Clear the usage in the audio buffer
+ audio_used = 0;
+ }
+ }
+
+ // Get the audio if required
+ if ( !paused )
+ {
+ int ret = 0;
+ int got_audio = 0;
+
+ av_init_packet( &pkt );
+
+ while( ret >= 0 && !got_audio )
+ {
+ // Check if the buffer already contains the samples required
+ if ( audio_used >= *samples && ignore == 0 )
+ {
+ got_audio = 1;
+ break;
+ }
+
+ // Read a packet
+ ret = av_read_frame( context, &pkt );
+
+ int len = pkt.size;
+ uint8_t *ptr = pkt.data;
+
+ // We only deal with audio from the selected audio_index
+ while ( ptr != NULL && ret >= 0 && pkt.stream_index == index && len > 0 )
+ {
+ int data_size = sizeof( int16_t ) * AVCODEC_MAX_AUDIO_FRAME_SIZE;
+
+ // Decode the audio
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(29<<8)+0))
+ ret = avcodec_decode_audio2( codec_context, decode_buffer, &data_size, ptr, len );
+#else
+ ret = avcodec_decode_audio( codec_context, decode_buffer, &data_size, ptr, len );
+#endif
+ if ( ret < 0 )
+ {
+ ret = 0;
+ break;
+ }
+
+ len -= ret;
+ ptr += ret;
+
+ if ( data_size > 0 )
+ {
+ int src_stride[6]= { av_get_bits_per_sample_format( codec_context->sample_fmt ) / 8 };
+ int dst_stride[6]= { av_get_bits_per_sample_format( SAMPLE_FMT_S16 ) / 8 };
+
+ if ( resample )
+ {
+ int16_t *source = decode_buffer;
+ int16_t *dest = &audio_buffer[ audio_used * *channels ];
+ int convert_samples = data_size / src_stride[0];
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+ if ( convert )
+ {
+ const void *src_buf[6] = { decode_buffer };
+ void *dst_buf[6] = { convert_buffer };
+ av_audio_convert( convert, dst_buf, dst_stride, src_buf, src_stride, convert_samples );
+ source = convert_buffer;
+ }
+#endif
+ audio_used += audio_resample( resample, dest, source, convert_samples / codec_context->channels );
+ }
+ else
+ {
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+ if ( convert )
+ {
+ const void *src_buf[6] = { decode_buffer };
+ void *dst_buf[6] = { &audio_buffer[ audio_used * *channels ] };
+ av_audio_convert( convert, dst_buf, dst_stride, src_buf, src_stride, data_size / src_stride[0] );
+ }
+ else
+#endif
+ {
+ memcpy( &audio_buffer[ audio_used * *channels ], decode_buffer, data_size );
+ }
+ audio_used += data_size / *channels / src_stride[0];
+ }
+
+ // Handle ignore
+ while ( ignore && audio_used > *samples )
+ {
+ ignore --;
+ audio_used -= *samples;
+ memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * sizeof( int16_t ) );
+ }
+ }
+
+ // If we're behind, ignore this packet
+ if ( pkt.pts >= 0 )
+ {
+ double current_pts = av_q2d( stream->time_base ) * pkt.pts;
+ double source_fps = mlt_properties_get_double( properties, "source_fps" );
+ int req_position = ( int )( real_timecode * source_fps + 0.5 );
+ int int_position = ( int )( current_pts * source_fps + 0.5 );
+
+ if ( context->start_time != AV_NOPTS_VALUE )
+ int_position -= ( int )( context->start_time * source_fps / AV_TIME_BASE + 0.5 );
+ if ( seekable && !ignore && int_position < req_position )
+ ignore = 1;
+ }
+ }
+
+ // We're finished with this packet regardless
+ av_free_packet( &pkt );
+ }
+
+ *buffer = mlt_pool_alloc( *samples * *channels * sizeof( int16_t ) );
+ mlt_properties_set_data( frame_properties, "audio", *buffer, 0, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Now handle the audio if we have enough
+ if ( audio_used >= *samples )
+ {
+ memcpy( *buffer, audio_buffer, *samples * *channels * sizeof( int16_t ) );
+ audio_used -= *samples;
+ memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * *channels * sizeof( int16_t ) );
+ }
+ else
+ {
+ memset( *buffer, 0, *samples * *channels * sizeof( int16_t ) );
+ }
+
+ // Store the number of audio samples still available
+ mlt_properties_set_int( properties, "_audio_used", audio_used );
+ }
+ else
+ {
+ // Get silence and don't touch the context
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+ }
+
+ // Regardless of speed (other than paused), we expect to get the next frame
+ if ( !paused )
+ mlt_properties_set_position( properties, "_audio_expected", position + 1 );
+
+ return 0;
+}
+
+/** Set up audio handling.
+*/
+
+static void producer_set_up_audio( mlt_producer this, mlt_frame frame )
+{
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Fetch the audio_context
+ AVFormatContext *context = mlt_properties_get_data( properties, "audio_context", NULL );
+
+ // Get the audio_index
+ int index = mlt_properties_get_int( properties, "audio_index" );
+
+ // Reopen the file if necessary
+ if ( !context && index > -1 )
+ {
+ mlt_events_block( properties, this );
+ producer_open( this, mlt_service_profile( MLT_PRODUCER_SERVICE(this) ),
+ mlt_properties_get( properties, "resource" ) );
+ context = mlt_properties_get_data( properties, "audio_context", NULL );
+ mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL );
+ mlt_events_unblock( properties, this );
+ }
+
+ // Exception handling for audio_index
+ if ( context && index >= (int) context->nb_streams )
+ {
+ for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_AUDIO; --index );
+ mlt_properties_set_int( properties, "audio_index", index );
+ }
+ if ( context && index > -1 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_AUDIO )
+ {
+ index = -1;
+ mlt_properties_set_int( properties, "audio_index", index );
+ }
+
+ // Update the audio properties if the index changed
+ if ( index > -1 && index != mlt_properties_get_int( properties, "_audio_index" ) )
+ {
+ mlt_properties_set_int( properties, "_audio_index", index );
+ mlt_properties_set_data( properties, "audio_codec", NULL, 0, NULL, NULL );
+ }
+
+ // Deal with audio context
+ if ( context != NULL && index > -1 )
+ {
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get the audio stream
+ AVStream *stream = context->streams[ index ];
+
+ // Get codec context
+ AVCodecContext *codec_context = stream->codec;
+
+ // Get the codec
+ AVCodec *codec = mlt_properties_get_data( properties, "audio_codec", NULL );
+
+ // Initialise the codec if necessary
+ if ( codec == NULL )
+ {
+ // Find the codec
+ codec = avcodec_find_decoder( codec_context->codec_id );
+
+ // If we don't have a codec and we can't initialise it, we can't do much more...
+ avformat_lock( );
+ if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 )
+ {
+ // Now store the codec with its destructor
+ mlt_properties_set_data( properties, "audio_codec", codec_context, 0, producer_codec_close, NULL );
+
+ }
+ else
+ {
+ // Remember that we can't use this later
+ mlt_properties_set_int( properties, "audio_index", -1 );
+ index = -1;
+ }
+ avformat_unlock( );
+
+ // Process properties as AVOptions
+ apply_properties( codec_context, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM );
+ }
+
+ // No codec, no show...
+ if ( codec && index > -1 )
+ {
+ mlt_frame_push_audio( frame, producer_get_audio );
+ mlt_properties_set_data( frame_properties, "avformat_producer", this, 0, NULL, NULL );
+ mlt_properties_set_int( frame_properties, "frequency", codec_context->sample_rate );
+ mlt_properties_set_int( frame_properties, "channels", codec_context->channels );
+ }
+ }
+}
+
+/** Our get frame implementation.
+*/
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ // Create an empty frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+ // Set the position of this producer
+ mlt_properties_set_position( MLT_FRAME_PROPERTIES( *frame ), "avformat_position", mlt_producer_frame( this ) );
+
+ // Set up the video
+ producer_set_up_video( this, *frame );
+
+ // Set up the audio
+ producer_set_up_audio( this, *frame );
+
+ // Set the aspect_ratio
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "aspect_ratio", mlt_properties_get_double( MLT_PRODUCER_PROPERTIES( this ), "aspect_ratio" ) );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( this );
+
+ return 0;
+}
--- /dev/null
+schema_version: 0.1
+type: producer # consumer, filter, producer, or transition
+identifier: avformat
+title: FFmpeg Reader
+version: 0.2.5
+copyright: Copyright (C) 2003-2008 Ushodaya Enterprises Limited
+license: LGPL
+language: en
+url: http://www.ffmpeg.org/
+creator: Charles Yates
+contributor:
+ - Dan Dennedy
+tags:
+ - Audio # this may produce audio
+ - Video # this may produce video
+description: Read an audio and/or video file using FFmpeg
+icon:
+ filename: avformat/producer.png # relative to $MLT_DATA/modules/
+ content-type: image/png
+ content-encoding: base64 # could also be hex or none if inline SVG
+ content: |
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAPCAYAAAD+pA/bAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI
+ WXMAAAsSAAALEgHS3X78AAAAB3RJTUUH1gsBEgMLZIL+swAAAB10RVh0Q29tbWVudABDcmVhdGVk
+ IHdpdGggVGhlIEdJTVDvZCVuAAAEgUlEQVQ4y2VUSWwbZRT+5p/xP4v3eIntceLEThOylKgkB070
+ UAUOCNFDFYkIVeqBijMHQKiCAwLEDbVIPVQCUalFQggph6pUoWKRWoS6RCGJ04biOG7sOI638TJe
+ xjM/FwwRfKf3vve99+kd3uNubNxgVb0Km2W7Tbt06fQLpwv4G8u3k6/88nvJcXYhnJ+dGfsRR/Dl
+ 8tqpfKkZJ736d++cf6nU53Nra0utTOarVrMpdCj9XFhJr2C1sQq1pZ6ac8zNAljpi7ezzc+e5Lvx
+ je29H8rlyk8DA14GAOl05tT1n6tf39uqBM69GFgAsNjvaezsvKVfuyZkKxXIi4tvknn/PCQi4cA4
+ QMQf+aIv3Nvbm0sVTGe9zfDg8eGxUrk83689eFQcyhStQLluYG+/NNfny6VSsLW5Ge3pOjSHAy2e
+ /4TwjP84KAfBKRzW99f59G7aAQCVav20SZQAtdlQbfGxteTuNACkd/dsguR8W9NNiKKEVFbjisUi
+ BQAtnz8rFAq+LgAWDjN/IpEkk97JzYQjAdkhI2fkwtVG9X0AWN+pwTA5iJSH1qEoaMZC8tFjfv3J
+ IckcdiYNk0GUZVRbXKipt5YAoP7wYZDXNKHOGKR4fCs+NbVGBpwDt3zw3VEUBbqo437qvjuXy7kr
+ HbtqWQzUJgC8Ar2Dpe1UXpgZC763W+RAOAaR2tAxRTmV3kvsJJPjSrf7umEYqAoC1NnZVCAY3CLD
+ 6nBpfnC+4hJc4B08DGqc1xr1l5126RzAIHAWqCjjj1wb8eHQq9uZ6nS9a4PAmaACD8bLWN16itrG
+ hoJ8Ptw2TVjRKAZjsWUAIABACHk3RmOQ7BIyegbpg/1oqWGBoAc0U5BFAt20o1CqXSk26YLZM8Aa
+ u5AEAwKV4XQNLHpGRq5zpRJ0QuCdmUGpUPgGAAQAoKBPJ5wTyDfzKLMyGMGntRZAmIHxMA/TTnDA
+ e3FvM+siXhfMdgXjIQbmUpCvMjTa7fFG5imUdhtNSYIcCCxHx8Y6/2zgUTwNq2Nd8FIvYAHFch1d
+ RmHjuhhVfdnjI1LPYVeQaw3gsMZAmYYTk+oT1SdBkiSU6z3U6k30AHR9PrhU9WYoHP7XYHR41Jpw
+ TxSDQhAuzgXLtIMQG0RioFwuX3IrwiWPnUByDsKyGPxKB5S3Phj0UkgiRdMQ0FYcqBECFgj8GY7F
+ 7vZvg/QDl+S66mg5fg2IATDBC45jkPgOJhODLa8dN4d8BJQKYN06RgfF/eNTibRpWVcdsg2MV1An
+ FJrbDfXEiWxEVdf/ZxCPxVvTnmkjqgzB4p0gMGGnPUyMjUANupNusXNHpDxYt4wR1XtrKBq9a3W0
+ B047hSg7ofUIxMlJePz+K0d/FjmahLyh146rzyUNw0Jby8LGWvckSbwcjUayx1RpzWZWELR3jZln
+ Eh8CwDFVgcLr4HpNVDtMi588ecEbCHx7dKZwNBkODed+W318Zipk3Rx1iTG9GbwYiUQMAAj53Zef
+ H2u/Ua16L4ZCgykA8A94vn/W1DdH3dK0wIdXwkNDH+E/+Avfv/E5LPIz8wAAAABJRU5ErkJggg==
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - Audio sync discrepancy with some content.
+ - Not all libavformat supported formats are seekable.
+ - >
+ Seeking is not always accurate. Sometimes it doesn't seek to I-frames so you
+ may get junk for a few frames.
+ - >
+ Fails to play beyond first frame of video of sources with PTS not starting
+ at 0 (video4linux).
+
+parameters:
+ - identifier: argument # 'argument' is a reserved name for a value supplied to the factory
+ title: File # the title can be used as a label for the widget
+ type: string
+ description: |
+ A file name specification or URL in the form:
+ [{protocol}|{format}]:{resource}[?{format-parameter}[&{format-parameter}...]]
+ For example, video4linux:/dev/video1?width:320&height:240
+ Note: on the bash command line, & must be escaped as '\&'.
+ Also, note the use of ':' instead of '=' for parameters.
+ Use 'f-list' to see a list of supported file formats.
+ Use 'vcodec-list' to see a list of supported video decoders.
+ Use 'acodec-list' to see a list of supported audio decoders.
+ readonly: no
+ required: yes
+ mutable: no
+ widget: fileopen # could provide a button to use a file-open dialog
+
+ - identifier: audio_index # the name is the mlt_properties name
+ title: Audio Index
+ type: integer
+ # the description can be used in a tool tip
+ description: Choose the index of audio stream to use (-1 is off)
+ readonly: no
+ mutable: no
+ minimum: -1
+ # when maximum not specified, the scalar limit is used
+ default: 0
+ widget: spinner
+
+ - identifier: video_index
+ title: Video Index
+ type: integer
+ description: Choose the index of video stream to use (-1 is off)
+ readonly: no
+ mutable: no
+ minimum: -1
+ default: 0
+ widget: spinner
+
+ - identifier: in
+ title: In Point
+ type: time # time is not implemented, but it will correspond to the mlt_position replacement
+ description: Set the start time offset to use within the clip
+ readonly: no
+ mutable: no
+ minimum: 0
+ default: 0
+ widget: timecode # this is a special form of time value/code entry (e.g. see Kino)
+
+ - identifier: out
+ title: Out Point
+ type: time
+ description: Set the ending time offset to use within the clip
+ readonly: no
+ minimum: 0
+ mutable: no
+ widget: timecode # as opposed to time, which could be confused for a wallclock-style time widget
+
+ - identifier: threads
+ title: Decoding Threads
+ type: integer
+ description: Choose the number of threads to use in the decoder(s)
+ readonly: no
+ mutable: no
+ minimum: 0
+ maximum: 4
+ default: 1
+ widget: spinner
+ unit: threads # the unit is a label that appears after the widget
+
+ - identifier: force_aspect_ratio
+ title: Sample Aspect Ratio
+ type: float
+ description: Optionally override a (mis)detected aspect ratio
+ readonly: no
+ mutable: yes
+ minimum: 0.001 # just a UI suggestion
+ maximum: 9.999 # just a suggestion
+ # no default property means it should be blank in the UI and not applied unless provided
+
+ - identifier: resource
+ title: File
+ type: string
+ description: file or protocol specification
+ readonly: yes
+
+ - identifier: source_fps
+ title: Frame Rate
+ type: float
+ scale: 2 # scale is the number of digits to display after the decimal point
+ description: the framerate of the resource
+ readonly: yes
+ unit: frames/second
+
+ - identifier: aspect_ratio
+ title: Sample Aspect Ratio
+ type: float
+ description: >
+ The sample aspect ratio of the resource.
+ This is determined on every frame read.
+ readonly: yes
+
+ - identifier: length
+ title: Duration
+ type: time
+ description: duration
+ readonly: yes
+ widget: timecode
+
+ - identifier: seekable
+ title: Supports Seek
+ type: integer
+ description: if the resource can seek
+ readonly: yes
--- /dev/null
+#!/bin/sh
+
+# Clean up disables if not in help mode
+[ "$help" != "1" ] && rm -f disable-* producers.dat filters.dat transitions.dat consumers.dat
+
+# Create the make.inc file
+echo SUBDIRS = `find . -maxdepth 1 -type d | grep -v .svn | grep -v "^.$" | sed 's/\.\///'` > make.inc
+
+# Iterate through arguments
+for i in "$@"
+do
+ case $i in
+ --disable-* ) touch disable-${i#--disable-} ;;
+ esac
+done
+
+# Iterate through each of the components
+for i in *
+do
+ if [ -d $i -a \( "$help" = "1" -o ! -f disable-$i \) ]
+ then
+ if [ "$gpl" = "true" -o ! -f $i/gpl ]
+ then
+ [ -f $i/Makefile -a "$help" = "0" ] && echo "Configuring modules/$i:"
+ if [ -x $i/configure ]
+ then
+ olddir2=`pwd`
+ cd $i
+ ./configure "$@"
+ [ $? != 0 ] && exit 1
+ cd $olddir2
+ elif [ -f $i/configure ]
+ then
+ echo " configure script is not set executable!"
+ fi
+ elif [ "$help" = "0" ]
+ then
+ touch disable-$i
+ fi
+ fi
+done
+
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltcore$(LIBSUF)
+
+OBJS = factory.o \
+ producer_colour.o \
+ producer_consumer.o \
+ producer_noise.o \
+ producer_ppm.o \
+ filter_brightness.o \
+ filter_channelcopy.o \
+ filter_crop.o \
+ filter_data_feed.o \
+ filter_data_show.o \
+ filter_gamma.o \
+ filter_greyscale.o \
+ filter_luma.o \
+ filter_mirror.o \
+ filter_mono.o \
+ filter_obscure.o \
+ filter_region.o \
+ filter_rescale.o \
+ filter_resize.o \
+ filter_transition.o \
+ filter_watermark.o \
+ transition_composite.o \
+ transition_luma.o \
+ transition_mix.o \
+ transition_region.o \
+ consumer_null.o
+
+ASM_OBJS =
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS) $(ASM_OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(ASM_OBJS) $(LDFLAGS)
+
+composite_line_yuv_mmx.o: composite_line_yuv_mmx.S
+ $(CC) -o $@ -c composite_line_yuv_mmx.S
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(ASM_OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+ install -m 644 ../data_fx.properties "$(DESTDIR)$(prefix)/share/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+ .file "composite_line_yuv_mmx"
+ .version "01.01"
+
+gcc2_compiled.:
+.data
+
+.text
+ .align 16
+
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)
+.globl composite_line_yuv_mmx
+ .type composite_line_yuv_mmx,@function
+composite_line_yuv_mmx:
+#else
+.globl _composite_line_yuv_mmx
+_composite_line_yuv_mmx:
+#endif
+
+/*
+ * Arguments
+ *
+ * dest: 8(%ebp) %esi
+ * src: 12(%ebp)
+ * width_src: 16(%ebp)
+ * alpha: 20(%ebp)
+ * weight: 24(%ebp)
+ * luma: 28(%ebp)
+ * softness: 32(%ebp)
+ */
+
+/*
+ * Function call entry
+ */
+ pushl %ebp
+ movl %esp,%ebp
+ subl $28,%esp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+
+/* Initialise */
+ movl 8(%ebp), %esi # get dest
+ movl $0, %edx # j = 0
+
+.loop:
+
+ movl $0xffff, %ecx # a = 255
+ cmpl $0, 20(%ebp) # if alpha == NULL
+ je .noalpha
+ movl 20(%ebp), %edi # a = alpha[ j ]
+ movb (%edi,%edx), %cl
+.noalpha:
+ movl %ecx, -24(%ebp) # save ecx
+
+ movl 24(%ebp), %eax # mix = weight
+ cmpl $0, 28(%ebp) # if luma == NULL
+ je .noluma
+ movl 28(%ebp), %edi # mix = ...
+ movl %edx, %ebx
+ sall $1, %ebx
+ movw (%edi,%ebx), %bx # luma[ j*2 ]
+ cmpl %ebx, %eax
+ jl .luma0
+ movl %ebx, %ecx
+ addl 32(%ebp), %ecx # + softness
+ cmpl %ecx, %eax
+ jge .luma1
+ /* TODO: linear interpolate between edges */
+ subw %bx, %ax
+ sall $8, %eax
+ subw %bx, %cx
+ movl %edx, %ebx
+ divw %cx
+ movl %ebx, %edx
+ jmp .noluma
+.luma0:
+ movl $0, %eax
+ jmp .noluma
+.luma1:
+ movl $0xffff, %eax
+.noluma:
+ shrl $8, %eax
+
+ movl %edx, %ebx # edx will be destroyed by mulw
+ movl -24(%ebp), %ecx # restore ecx
+ mull %ecx # mix = mix * a...
+ movl %ebx, %edx # restore edx
+ shrl $8, %eax # >>8
+ andl $0xff, %eax
+
+/* put alpha and (1-alpha) into mm0 */
+/* 0 aa 0 1-a 0 aa 0 1-a */
+
+ /* duplicate word */
+ movl %eax, %ecx
+ shll $16, %ecx
+ orl %eax, %ecx
+
+ movd %ecx, %mm1
+
+ /* (1 << 16) - mix */
+ movl $0x000000ff, %ecx
+ subl %eax, %ecx
+ andl $0xff, %ecx
+
+ /* duplicate word */
+ movl %ecx, %eax
+ shll $16, %eax
+ orl %eax, %ecx
+
+ movd %ecx, %mm0
+
+ /* unpack words into double words */
+ punpcklwd %mm1, %mm0
+
+/* put src yuv and dest yuv into mm1 */
+/* 0 UVs 0 UVd 0 Ys 0 Yd */
+
+ movl 12(%ebp), %edi # get src
+ movb (%edi), %cl
+ shll $8, %ecx
+ movb 1(%edi), %al
+ shll $24, %eax
+ orl %eax, %ecx
+
+ movb (%esi), %al # get dest
+ orl %eax, %ecx
+ movb 1(%esi), %al
+ shll $16, %eax
+ orl %eax, %ecx
+
+ movd %ecx, %mm1
+ punpcklbw %mm4, %mm1
+
+/* alpha composite */
+ pmaddwd %mm1, %mm0
+ psrld $8, %mm0
+
+/* store result */
+ movd %mm0, %eax
+ movb %al, (%esi)
+ pextrw $2, %mm0, %eax
+ movl $128, %eax
+ movb %al, 1(%esi)
+
+/* for..next */
+ addl $1, %edx # j++
+ cmpl 16(%ebp), %edx # if ( j == width_src )
+ jge .out
+
+ addl $2, %esi
+ addl $2, 12(%ebp)
+
+ jmp .loop
+
+.out:
+ emms
+ leal -40(%ebp),%esp
+ popl %ebx
+ popl %esi
+ popl %edi
+ movl %ebp,%esp
+ popl %ebp
+ ret
+
+
+/********************************************/
+
+.align 8
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)
+.globl composite_have_mmx
+ .type composite_have_mmx,@function
+composite_have_mmx:
+#else
+.globl _composite_have_mmx
+_composite_have_mmx:
+#endif
+
+ push %ebx
+
+# Check if bit 21 in flags word is writeable
+
+ pushfl
+ popl %eax
+ movl %eax,%ebx
+ xorl $0x00200000, %eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+
+ cmpl %eax, %ebx
+
+ je .notfound
+
+# OK, we have CPUID
+
+ movl $1, %eax
+ cpuid
+
+ test $0x00800000, %edx
+ jz .notfound
+
+ movl $1, %eax
+ jmp .out2
+
+.notfound:
+ movl $0, %eax
+.out2:
+ popl %ebx
+ ret
--- /dev/null
+/*
+ * consumer_null.c -- a null consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// mlt Header files
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+
+// System header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+// Forward references.
+static int consumer_start( mlt_consumer this );
+static int consumer_stop( mlt_consumer this );
+static int consumer_is_stopped( mlt_consumer this );
+static void *consumer_thread( void *arg );
+static void consumer_close( mlt_consumer this );
+
+/** Initialise the dv consumer.
+*/
+
+mlt_consumer consumer_null_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Allocate the consumer
+ mlt_consumer this = mlt_consumer_new( profile );
+
+ // If memory allocated and initialises without error
+ if ( this != NULL )
+ {
+ // Assign close callback
+ this->close = consumer_close;
+
+ // Set up start/stop/terminated callbacks
+ this->start = consumer_start;
+ this->stop = consumer_stop;
+ this->is_stopped = consumer_is_stopped;
+ }
+
+ // Return this
+ return this;
+}
+
+/** Start the consumer.
+*/
+
+static int consumer_start( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Check that we're not already running
+ if ( !mlt_properties_get_int( properties, "running" ) )
+ {
+ // Allocate a thread
+ pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
+
+ // Assign the thread to properties
+ mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
+
+ // Set the running state
+ mlt_properties_set_int( properties, "running", 1 );
+ mlt_properties_set_int( properties, "joined", 0 );
+
+ // Create the thread
+ pthread_create( thread, NULL, consumer_thread, this );
+ }
+ return 0;
+}
+
+/** Stop the consumer.
+*/
+
+static int consumer_stop( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Check that we're running
+ if ( !mlt_properties_get_int( properties, "joined" ) )
+ {
+ // Get the thread
+ pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
+
+ // Stop the thread
+ mlt_properties_set_int( properties, "running", 0 );
+ mlt_properties_set_int( properties, "joined", 1 );
+
+ // Wait for termination
+ pthread_join( *thread, NULL );
+ }
+
+ return 0;
+}
+
+/** Determine if the consumer is stopped.
+*/
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ return !mlt_properties_get_int( properties, "running" );
+}
+
+/** The main thread - the argument is simply the consumer.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Map the argument to the object
+ mlt_consumer this = arg;
+
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Convenience functionality
+ int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
+ int terminated = 0;
+
+ // Frame and size
+ mlt_frame frame = NULL;
+
+ // Loop while running
+ while( !terminated && mlt_properties_get_int( properties, "running" ) )
+ {
+ // Get the frame
+ frame = mlt_consumer_rt_frame( this );
+
+ // Check for termination
+ if ( terminate_on_pause && frame != NULL )
+ terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
+
+ // Check that we have a frame to work with
+ if ( frame != NULL )
+ {
+ // Close the frame
+ mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+ mlt_frame_close( frame );
+ }
+ }
+
+ // Indicate that the consumer is stopped
+ mlt_properties_set_int( properties, "running", 0 );
+ mlt_consumer_stopped( this );
+
+ return NULL;
+}
+
+/** Close the consumer.
+*/
+
+static void consumer_close( mlt_consumer this )
+{
+ // Stop the consumer
+ mlt_consumer_stop( this );
+
+ // Close the parent
+ mlt_consumer_close( this );
+
+ // Free the memory
+ free( this );
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+#include <string.h>
+
+extern mlt_consumer consumer_null_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_brightness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_channelcopy_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_data_feed_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_data_show_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_gamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_greyscale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_obscure_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_rescale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_transition_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_watermark_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_colour_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+#include "transition_composite.h"
+extern mlt_transition transition_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+#include "transition_region.h"
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( consumer_type, "null", consumer_null_init );
+ MLT_REGISTER( filter_type, "brightness", filter_brightness_init );
+ MLT_REGISTER( filter_type, "channelcopy", filter_channelcopy_init );
+ MLT_REGISTER( filter_type, "crop", filter_crop_init );
+ MLT_REGISTER( filter_type, "data_feed", filter_data_feed_init );
+ MLT_REGISTER( filter_type, "data_show", filter_data_show_init );
+ MLT_REGISTER( filter_type, "gamma", filter_gamma_init );
+ MLT_REGISTER( filter_type, "greyscale", filter_greyscale_init );
+ MLT_REGISTER( filter_type, "grayscale", filter_greyscale_init );
+ MLT_REGISTER( filter_type, "luma", filter_luma_init );
+ MLT_REGISTER( filter_type, "mirror", filter_mirror_init );
+ MLT_REGISTER( filter_type, "mono", filter_mono_init );
+ MLT_REGISTER( filter_type, "obscure", filter_obscure_init );
+ MLT_REGISTER( filter_type, "region", filter_region_init );
+ MLT_REGISTER( filter_type, "rescale", filter_rescale_init );
+ MLT_REGISTER( filter_type, "resize", filter_resize_init );
+ MLT_REGISTER( filter_type, "transition", filter_transition_init );
+ MLT_REGISTER( filter_type, "watermark", filter_watermark_init );
+ MLT_REGISTER( producer_type, "color", producer_colour_init );
+ MLT_REGISTER( producer_type, "colour", producer_colour_init );
+ MLT_REGISTER( producer_type, "consumer", producer_consumer_init );
+ MLT_REGISTER( producer_type, "noise", producer_noise_init );
+ MLT_REGISTER( producer_type, "ppm", producer_ppm_init );
+ MLT_REGISTER( transition_type, "composite", transition_composite_init );
+ MLT_REGISTER( transition_type, "luma", transition_luma_init );
+ MLT_REGISTER( transition_type, "mix", transition_mix_init );
+ MLT_REGISTER( transition_type, "region", transition_region_init );
+}
--- /dev/null
+/*
+ * filter_brightness.c -- gamma filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define CLAMP( x, min, max ) (x) < (min) ? (min) : (x) > (max) ? (max) : (x)
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ // Get the brightness level
+ double level = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "brightness" );
+
+ // Only process if level is something other than 1
+ if ( level != 1.0 )
+ {
+ int i = *width * *height + 1;
+ uint8_t *p = *image;
+ int32_t m = level * ( 1 << 16 );
+ int32_t n = 128 * ( ( 1 << 16 ) - m );
+
+ while ( --i )
+ {
+ p[0] = CLAMP( (p[0] * m) >> 16, 16, 235 );
+ p[1] = CLAMP( (p[1] * m + n) >> 16, 16, 240 );
+ p += 2;
+ }
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the starting brightness level
+ double level = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "start" ) );
+
+ // If there is an end adjust gain to the range
+ if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL )
+ {
+ // Determine the time position of this frame in the transition duration
+ mlt_position in = mlt_filter_get_in( this );
+ mlt_position out = mlt_filter_get_out( this );
+ mlt_position time = mlt_frame_get_position( frame );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "end" ) );
+ level += ( end - level ) * position;
+ }
+
+ // Push the frame filter
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "brightness", level );
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_brightness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "1" : arg );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_channelcopy.c -- copy one audio channel to another
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#define __USE_ISOC99 1
+#include <math.h>
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties of the a frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+ int i, j;
+ int from = mlt_properties_get_int( properties, "channelcopy.from" );
+ int to = mlt_properties_get_int( properties, "channelcopy.to" );
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+
+ // Duplicate channels as necessary
+ {
+ int size = *channels * *samples * 2;
+ int16_t *new_buffer = mlt_pool_alloc( size );
+
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Duplicate the existing channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * *channels ) + ( j == to ? from : j ) ];
+ }
+ }
+ *buffer = new_buffer;
+ }
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
+
+ // Propogate the parameters
+ mlt_properties_set_int( frame_props, "channelcopy.to", mlt_properties_get_int( properties, "to" ) );
+ mlt_properties_set_int( frame_props, "channelcopy.from", mlt_properties_get_int( properties, "from" ) );
+
+ // Override the get_audio method
+ mlt_frame_push_audio( frame, filter_get_audio );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_channelcopy_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ if ( arg != NULL )
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "to", atoi( arg ) );
+ else
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "to", 1 );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_crop.c -- cropping filter
+ * Copyright (C) 2009 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+static void crop( uint8_t *src, uint8_t *dest, int bpp, int width, int height, int left, int right, int top, int bottom )
+{
+ int stride = ( width - left - right ) * bpp + 1;
+ int y = height - top - bottom + 1;
+ uint8_t *s = &src[ ( ( top * width ) + left ) * bpp ];
+
+ while ( --y )
+ {
+ int x = stride;
+ while ( --x )
+ *dest ++ = *s ++;
+ s += ( right + left ) * bpp;
+ }
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = 0;
+
+ // Get the properties from the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Correct Width/height if necessary
+ if ( *width == 0 || *height == 0 )
+ {
+ *width = mlt_properties_get_int( properties, "normalised_width" );
+ *height = mlt_properties_get_int( properties, "normalised_height" );
+ }
+
+ // Now get the image
+ error = mlt_frame_get_image( this, image, format, width, height, writable );
+
+ int left = mlt_properties_get_int( properties, "crop.left" );
+ int right = mlt_properties_get_int( properties, "crop.right" );
+ int top = mlt_properties_get_int( properties, "crop.top" );
+ int bottom = mlt_properties_get_int( properties, "crop.bottom" );
+ int owidth = *width - left - right;
+ int oheight = *height - top - bottom;
+
+ // We only know how to process yuv422 at the moment
+ if ( ( owidth != *width || oheight != *height ) &&
+ error == 0 && *format == mlt_image_yuv422 && *image != NULL && owidth > 0 && oheight > 0 )
+ {
+ // Provides a manual override for misreported field order
+ if ( mlt_properties_get( properties, "meta.top_field_first" ) )
+ {
+ mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( properties, "meta.top_field_first" ) );
+ mlt_properties_set_int( properties, "meta.top_field_first", 0 );
+ }
+
+ if ( top % 2 )
+ mlt_properties_set_int( properties, "top_field_first", !mlt_properties_get_int( properties, "top_field_first" ) );
+
+ left -= left % 2;
+ owidth = *width - left - right;
+
+ // Create the output image
+ uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+ if ( output )
+ {
+ // Call the generic resize
+ crop( *image, output, 2, *width, *height, left, right, top, bottom );
+
+ // Now update the frame
+ *image = output;
+ mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+ }
+
+ // We should resize the alpha too
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+ if ( alpha != NULL )
+ {
+ uint8_t *newalpha = mlt_pool_alloc( owidth * oheight );
+ if ( newalpha )
+ {
+ crop( alpha, newalpha, 1, *width, *height, left, right, top, bottom );
+ mlt_properties_set_data( properties, "alpha", newalpha, owidth * oheight, ( mlt_destructor )mlt_pool_release, NULL );
+ this->get_alpha_mask = NULL;
+ }
+ }
+ *width = owidth;
+ *height = oheight;
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "active" ) )
+ {
+ // Push the get_image method on to the stack
+ mlt_frame_push_get_image( frame, filter_get_image );
+ }
+ else
+ {
+ mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
+ mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
+ int left = mlt_properties_get_int( filter_props, "left" );
+ int right = mlt_properties_get_int( filter_props, "right" );
+ int top = mlt_properties_get_int( filter_props, "top" );
+ int bottom = mlt_properties_get_int( filter_props, "bottom" );
+ int width = mlt_properties_get_int( frame_props, "real_width" );
+ int height = mlt_properties_get_int( frame_props, "real_height" );
+
+ mlt_properties_set_int( frame_props, "crop.left", left );
+ mlt_properties_set_int( frame_props, "crop.right", right );
+ mlt_properties_set_int( frame_props, "crop.top", top );
+ mlt_properties_set_int( frame_props, "crop.bottom", bottom );
+ mlt_properties_set_int( frame_props, "real_width", width - left - right );
+ mlt_properties_set_int( frame_props, "real_height", height - top - bottom );
+ }
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
+ if ( mlt_filter_init( this, this ) == 0 )
+ {
+ this->process = filter_process;
+ if ( arg )
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "active", atoi( arg ) );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_data_feed.c -- data feed filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <framework/mlt.h>
+
+/** This filter should be used in conjuction with the data_show filter.
+ The concept of the data_feed is that it can be used to pass titles
+ or images to render on the frame, but doesn't actually do it
+ itself. data_feed imposes few rules on what's passed on and the
+ validity is confirmed in data_show before use.
+*/
+
+/** Data queue destructor.
+*/
+
+static void destroy_data_queue( void *arg )
+{
+ if ( arg != NULL )
+ {
+ // Assign the correct type
+ mlt_deque queue = arg;
+
+ // Iterate through each item and destroy them
+ while ( mlt_deque_peek_front( queue ) != NULL )
+ mlt_properties_close( mlt_deque_pop_back( queue ) );
+
+ // Close the deque
+ mlt_deque_close( queue );
+ }
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( this );
+
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get the data queue
+ mlt_deque data_queue = mlt_properties_get_data( frame_properties, "data_queue", NULL );
+
+ // Get the type of the data feed
+ char *type = mlt_properties_get( filter_properties, "type" );
+
+ // Get the in and out points of this filter
+ int in = mlt_filter_get_in( this );
+ int out = mlt_filter_get_out( this );
+
+ // Create the data queue if it doesn't exist
+ if ( data_queue == NULL )
+ {
+ // Create the queue
+ data_queue = mlt_deque_init( );
+
+ // Assign it to the frame with the destructor
+ mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, destroy_data_queue, NULL );
+ }
+
+ // Now create the data feed
+ if ( data_queue != NULL && type != NULL && !strcmp( type, "attr_check" ) )
+ {
+ int i = 0;
+ int count = mlt_properties_count( frame_properties );
+
+ for ( i = 0; i < count; i ++ )
+ {
+ char *name = mlt_properties_get_name( frame_properties, i );
+
+ // Only deal with meta.attr.name values here - these should have a value of 1 to be considered
+ // Additional properties of the form are meta.attr.name.property are passed down on the feed
+ if ( !strncmp( name, "meta.attr.", 10 ) && strchr( name + 10, '.' ) == NULL && mlt_properties_get_int( frame_properties, name ) == 1 )
+ {
+ // Temp var to hold name + '.' for pass method
+ char temp[ 132 ];
+
+ // Create a new data feed
+ mlt_properties feed = mlt_properties_new( );
+
+ // Assign it the base properties
+ mlt_properties_set( feed, "id", mlt_properties_get( filter_properties, "_unique_id" ) );
+ mlt_properties_set( feed, "type", strrchr( name, '.' ) + 1 );
+ mlt_properties_set_position( feed, "position", mlt_frame_get_position( frame ) );
+
+ // Assign in/out of service we're connected to
+ mlt_properties_set_position( feed, "in", mlt_properties_get_position( frame_properties, "in" ) );
+ mlt_properties_set_position( feed, "out", mlt_properties_get_position( frame_properties, "out" ) );
+
+ // Pass all meta properties
+ sprintf( temp, "%s.", name );
+ mlt_properties_pass( feed, frame_properties, temp );
+
+ // Push it on to the queue
+ mlt_deque_push_back( data_queue, feed );
+
+ // Make sure this attribute only gets processed once
+ mlt_properties_set_int( frame_properties, name, 0 );
+ }
+ }
+ }
+ else if ( data_queue != NULL )
+ {
+ // Create a new data feed
+ mlt_properties feed = mlt_properties_new( );
+
+ // Assign it the base properties
+ mlt_properties_set( feed, "id", mlt_properties_get( filter_properties, "_unique_id" ) );
+ mlt_properties_set( feed, "type", type );
+ mlt_properties_set_position( feed, "position", mlt_frame_get_position( frame ) );
+
+ // Assign in/out of service we're connected to
+ mlt_properties_set_position( feed, "in", mlt_properties_get_position( frame_properties, "in" ) );
+ mlt_properties_set_position( feed, "out", mlt_properties_get_position( frame_properties, "out" ) );
+
+ // Correct in/out to the filter if specified
+ if ( in != 0 )
+ mlt_properties_set_position( feed, "in", in );
+ if ( out != 0 )
+ mlt_properties_set_position( feed, "out", out );
+
+ // Pass the properties which start with a "feed." prefix
+ // Note that 'feed.text' in the filter properties becomes 'text' on the feed
+ mlt_properties_pass( feed, filter_properties, "feed." );
+
+ // Push it on to the queue
+ mlt_deque_push_back( data_queue, feed );
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_data_feed_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create the filter
+ mlt_filter this = mlt_filter_new( );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Assign the argument (default to titles)
+ mlt_properties_set( properties, "type", arg == NULL ? "titles" : arg );
+
+ // Specify the processing method
+ this->process = filter_process;
+ }
+
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_data_show.c -- data feed filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle the profile.
+*/
+
+static mlt_filter obtain_filter( mlt_filter filter, char *type )
+{
+ // Result to return
+ mlt_filter result = NULL;
+
+ // Miscelaneous variable
+ int i = 0;
+ int type_len = strlen( type );
+
+ // Get the properties of the data show filter
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the profile properties
+ mlt_properties profile_properties = mlt_properties_get_data( filter_properties, "profile_properties", NULL );
+
+ // Obtain the profile_properties if we haven't already
+ if ( profile_properties == NULL )
+ {
+ char temp[ 512 ];
+
+ // Get the profile requested
+ char *profile = mlt_properties_get( filter_properties, "resource" );
+
+ // If none is specified, pick up the default for this normalisation
+ if ( profile == NULL )
+ sprintf( temp, "%s/feeds/%s/data_fx.properties", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ) );
+ else if ( strchr( profile, '%' ) )
+ sprintf( temp, "%s/feeds/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( profile, '%' ) + 1 );
+ else
+ strcpy( temp, profile );
+
+ // Load the specified profile or use the default
+ profile_properties = mlt_properties_load( temp );
+
+ // Store for later retrieval
+ mlt_properties_set_data( filter_properties, "profile_properties", profile_properties, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ }
+
+ if ( profile_properties != NULL )
+ {
+ for ( i = 0; i < mlt_properties_count( profile_properties ); i ++ )
+ {
+ char *name = mlt_properties_get_name( profile_properties, i );
+ char *value = mlt_properties_get_value( profile_properties, i );
+
+ if ( result == NULL && !strcmp( name, type ) && result == NULL )
+ result = mlt_factory_filter( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ), value, NULL );
+ else if ( result != NULL && !strncmp( name, type, type_len ) && name[ type_len ] == '.' )
+ mlt_properties_set( MLT_FILTER_PROPERTIES( result ), name + type_len + 1, value );
+ else if ( result != NULL )
+ break;
+ }
+ }
+
+ return result;
+}
+
+/** Retrieve medatata value
+*/
+
+char* metadata_value(mlt_properties properties, char* name)
+{
+ if (name == NULL) return NULL;
+ char *meta = malloc( strlen(name) + 18 );
+ sprintf( meta, "meta.attr.%s.markup", name);
+ char *result = mlt_properties_get( properties, meta);
+ free(meta);
+ return result;
+}
+
+/** Convert frames to Timecode
+*/
+
+char* frame_to_timecode( int frames , int fps)
+{
+ if (fps == 0) return strdup("-");
+ char *res = malloc(12);
+ int seconds = frames / (int) fps;
+ frames = frames % ((int) fps);
+ int minutes = seconds / 60;
+ seconds = seconds % 60;
+ int hours = minutes / 60;
+ minutes = minutes % 60;
+ sprintf(res, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames);
+ return res;
+}
+
+/** Process the frame for the requested type
+*/
+
+static int process_feed( mlt_properties feed, mlt_filter filter, mlt_frame frame )
+{
+ // Error return
+ int error = 1;
+
+ // Get the properties of the data show filter
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the type requested by the feeding filter
+ char *type = mlt_properties_get( feed, "type" );
+
+ // Fetch the filter associated to this type
+ mlt_filter requested = mlt_properties_get_data( filter_properties, type, NULL );
+
+ // If it doesn't exist, then create it now
+ if ( requested == NULL )
+ {
+ // Source filter from profile
+ requested = obtain_filter( filter, type );
+
+ // Store it on the properties for subsequent retrieval/destruction
+ mlt_properties_set_data( filter_properties, type, requested, 0, ( mlt_destructor )mlt_filter_close, NULL );
+ }
+
+ // If we have one, then process it now...
+ if ( requested != NULL )
+ {
+ int i = 0;
+ mlt_properties properties = MLT_FILTER_PROPERTIES( requested );
+ static const char *prefix = "properties.";
+ int len = strlen( prefix );
+
+ // Determine if this is an absolute or relative feed
+ int absolute = mlt_properties_get_int( feed, "absolute" );
+
+ // Make do with what we have
+ int length = !absolute ?
+ mlt_properties_get_int( feed, "out" ) - mlt_properties_get_int( feed, "in" ) + 1 :
+ mlt_properties_get_int( feed, "out" ) + 1;
+
+ // Repeat period
+ int period = mlt_properties_get_int( properties, "period" );
+ period = period == 0 ? 1 : period;
+
+ // Pass properties from feed into requested
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ {
+ char *name = mlt_properties_get_name( properties, i );
+ char *key = mlt_properties_get_value( properties, i );
+ if ( !strncmp( name, prefix, len ) )
+ {
+ if ( !strncmp( name + len, "length[", 7 ) )
+ {
+ mlt_properties_set_position( properties, key, ( length - period ) / period );
+ }
+ else
+ {
+ char *value = mlt_properties_get( feed, name + len );
+ if ( value != NULL )
+ {
+ // check for metadata keywords in metadata markup if user requested so
+ if ( mlt_properties_get_int( filter_properties, "dynamic" ) == 1 && !strcmp( name + strlen( name ) - 6, "markup") )
+ {
+ // Find keywords which should be surrounded by '#', like: #title#
+ char* keywords = strtok( value, "#" );
+ char result[512] = ""; // XXX: how much is enough?
+ int ct = 0;
+ int fromStart = ( value[0] == '#' ) ? 1 : 0;
+
+ while ( keywords != NULL )
+ {
+ if ( ct % 2 == fromStart )
+ {
+ // backslash in front of # suppresses substitution
+ if ( keywords[ strlen( keywords ) -1 ] == '\\' )
+ {
+ // keep characters except backslash
+ strncat( result, keywords, strlen( keywords ) -1 );
+ strcat( result, "#" );
+ ct++;
+ }
+ else
+ {
+ strcat( result, keywords );
+ }
+ }
+ else if ( !strcmp( keywords, "timecode" ) )
+ {
+ // special case: replace #timecode# with current frame timecode
+ int pos = mlt_properties_get_int( feed, "position" );
+ char *tc = frame_to_timecode( pos, mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ) );
+ strcat( result, tc );
+ free( tc );
+ }
+ else
+ {
+ // replace keyword with metadata value
+ char *metavalue = metadata_value( MLT_FRAME_PROPERTIES( frame ), keywords );
+ strcat( result, metavalue ? metavalue : "-" );
+ }
+ keywords = strtok( NULL, "#" );
+ ct++;
+ }
+ mlt_properties_set( properties, key, (char*) result );
+ }
+ else mlt_properties_set( properties, key, value );
+ }
+ }
+ }
+ }
+
+ // Set the original position on the frame
+ if ( absolute == 0 )
+ mlt_frame_set_position( frame, mlt_properties_get_int( feed, "position" ) - mlt_properties_get_int( feed, "in" ) );
+ else
+ mlt_frame_set_position( frame, mlt_properties_get_int( feed, "position" ) );
+
+ // Process the filter
+ mlt_filter_process( requested, frame );
+
+ // Should be ok...
+ error = 0;
+ }
+
+ return error;
+}
+
+void process_queue( mlt_deque data_queue, mlt_frame frame, mlt_filter filter )
+{
+ if ( data_queue != NULL )
+ {
+ // Create a new queue for those that we can't handle
+ mlt_deque temp_queue = mlt_deque_init( );
+
+ // Iterate through each entry on the queue
+ while ( mlt_deque_peek_front( data_queue ) != NULL )
+ {
+ // Get the data feed
+ mlt_properties feed = mlt_deque_pop_front( data_queue );
+
+ if ( mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ) != NULL )
+ mlt_properties_debug( feed, mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ), stderr );
+
+ // Process the data feed...
+ if ( process_feed( feed, filter, frame ) == 0 )
+ mlt_properties_close( feed );
+ else
+ mlt_deque_push_back( temp_queue, feed );
+ }
+
+ // Now put the unprocessed feeds back on the stack
+ while ( mlt_deque_peek_front( temp_queue ) )
+ {
+ // Get the data feed
+ mlt_properties feed = mlt_deque_pop_front( temp_queue );
+
+ // Put it back on the data queue
+ mlt_deque_push_back( data_queue, feed );
+ }
+
+ // Close the temporary queue
+ mlt_deque_close( temp_queue );
+ }
+}
+
+/** Get the image.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Pop the service
+ mlt_filter filter = mlt_frame_pop_service( frame );
+
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Track specific
+ process_queue( mlt_properties_get_data( frame_properties, "data_queue", NULL ), frame, filter );
+
+ // Global
+ process_queue( mlt_properties_get_data( frame_properties, "global_queue", NULL ), frame, filter );
+
+ // Need to get the image
+ return mlt_frame_get_image( frame, image, format, width, height, 1 );
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the filter
+ mlt_frame_push_service( frame, this );
+
+ // Register the get image method
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ // Return the frame
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_data_show_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
+{
+ // Create the filter
+ mlt_filter this = mlt_filter_new( );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Assign the argument (default to titles)
+ mlt_properties_set( properties, "resource", arg == NULL ? NULL : arg );
+
+ // Specify the processing method
+ this->process = filter_process;
+ }
+
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_gamma.c -- gamma filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ // Get the gamma value
+ double gamma = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "gamma" );
+
+ if ( gamma != 1.0 )
+ {
+ uint8_t *p = *image;
+ uint8_t *q = *image + *width * *height * 2;
+
+ // Calculate the look up table
+ double exp = 1 / gamma;
+ uint8_t lookup[ 256 ];
+ int i;
+
+ for( i = 0; i < 256; i ++ )
+ lookup[ i ] = ( uint8_t )( pow( ( double )i / 255.0, exp ) * 255 );
+
+ while ( p != q )
+ {
+ *p = lookup[ *p ];
+ p += 2;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ double gamma = mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "gamma" );
+ gamma = gamma <= 0 ? 1 : gamma;
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "gamma", gamma );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_gamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "gamma", arg == NULL ? "1" : arg );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_greyscale.c -- greyscale filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ uint8_t *p = *image;
+ uint8_t *q = *image + *width * *height * 2;
+ while ( p ++ != q )
+ *p ++ = 128;
+ }
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_greyscale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ this->process = filter_process;
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_luma.c -- luma filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_transition.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = 0;
+ mlt_filter filter = mlt_frame_pop_service( this );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ mlt_transition luma = mlt_properties_get_data( properties, "luma", NULL );
+ mlt_frame b_frame = mlt_properties_get_data( properties, "frame", NULL );
+ mlt_properties b_frame_props = b_frame ? MLT_FRAME_PROPERTIES( b_frame ) : NULL;
+ int out = mlt_properties_get_int( properties, "period" );
+
+ if ( out == 0 )
+ out = 24;
+
+ if ( b_frame == NULL || mlt_properties_get_int( b_frame_props, "width" ) != *width || mlt_properties_get_int( b_frame_props, "height" ) != *height )
+ {
+ b_frame = mlt_frame_init( MLT_FILTER_SERVICE( filter ) );
+ mlt_properties_set_data( properties, "frame", b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ }
+
+ if ( luma == NULL )
+ {
+ char *resource = mlt_properties_get( properties, "resource" );
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+ luma = mlt_factory_transition( profile, "luma", resource );
+ if ( luma != NULL )
+ {
+ mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES( luma );
+ mlt_properties_set_int( luma_properties, "in", 0 );
+ mlt_properties_set_int( luma_properties, "out", out );
+ mlt_properties_set_int( luma_properties, "reverse", 1 );
+ mlt_properties_set_data( properties, "luma", luma, 0, ( mlt_destructor )mlt_transition_close, NULL );
+ }
+
+ // Prime the filter with the first image to prevent a transition from the white
+ // of a test card.
+ error = mlt_frame_get_image( this, image, format, width, height, 1 );
+ if ( error == 0 )
+ {
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( this );
+ int size = 0;
+ uint8_t *src = mlt_properties_get_data( a_props, "image", &size );
+ uint8_t *dst = mlt_pool_alloc( size );
+
+ if ( dst != NULL )
+ {
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+ memcpy( dst, src, size );
+ mlt_properties_set_data( b_props, "image", dst, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( b_props, "width", *width );
+ mlt_properties_set_int( b_props, "height", *height );
+ mlt_properties_set_int( b_props, "format", *format );
+ }
+ }
+ }
+
+ if ( luma != NULL &&
+ ( mlt_properties_get( properties, "blur" ) != NULL ||
+ (int)mlt_frame_get_position( this ) % ( out + 1 ) != out ) )
+ {
+ mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES( luma );
+ mlt_properties_pass( luma_properties, properties, "luma." );
+ mlt_transition_process( luma, this, b_frame );
+ }
+
+ error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 )
+ {
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( this );
+ int size = 0;
+ uint8_t *src = mlt_properties_get_data( a_props, "image", &size );
+ uint8_t *dst = mlt_pool_alloc( size );
+
+ if ( dst != NULL )
+ {
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+ memcpy( dst, src, size );
+ mlt_properties_set_data( b_props, "image", dst, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( b_props, "width", *width );
+ mlt_properties_set_int( b_props, "height", *height );
+ mlt_properties_set_int( b_props, "format", *format );
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the filter on to the stack
+ mlt_frame_push_service( frame, this );
+
+ // Push the get_image on to the stack
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ this->process = filter_process;
+ if ( arg != NULL )
+ mlt_properties_set( properties, "resource", arg );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_mirror.c -- mirror filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Pop the mirror filter from the stack
+ mlt_filter this = mlt_frame_pop_service( frame );
+
+ // Get the mirror type
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Get the properties
+ char *mirror = mlt_properties_get( properties, "mirror" );
+
+ // Determine if reverse is required
+ int reverse = mlt_properties_get_int( properties, "reverse" );
+
+ // Get the image
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ // Get the alpha
+ uint8_t *alpha = mlt_frame_get_alpha_mask( frame );
+
+ // If we have an image of the right colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ // We'll KISS here
+ int hh = *height / 2;
+
+ if ( !strcmp( mirror, "horizontal" ) )
+ {
+ uint8_t *p = NULL;
+ uint8_t *q = NULL;
+ uint8_t *a = NULL;
+ uint8_t *b = NULL;
+ int i;
+ int uneven_w = ( *width % 2 ) * 2;
+ for ( i = 0; i < *height; i ++ )
+ {
+ p = ( uint8_t * )*image + i * *width * 2;
+ q = p + *width * 2;
+ a = alpha + i * *width;
+ b = a + *width - 1;
+ if ( !reverse )
+ {
+ while ( p < q )
+ {
+ *p ++ = *( q - 2 );
+ *p ++ = *( q - 3 - uneven_w );
+ *p ++ = *( q - 4 );
+ *p ++ = *( q - 1 - uneven_w );
+ q -= 4;
+ *a ++ = *b --;
+ *a ++ = *b --;
+ }
+ }
+ else
+ {
+ while ( p < q )
+ {
+ *( q - 2 ) = *p ++;
+ *( q - 3 - uneven_w ) = *p ++;
+ *( q - 4 ) = *p ++;
+ *( q - 1 - uneven_w ) = *p ++;
+ q -= 4;
+ *b -- = *a ++;
+ *b -- = *a ++;
+ }
+ }
+ }
+ }
+ else if ( !strcmp( mirror, "vertical" ) )
+ {
+ uint16_t *end = ( uint16_t *)*image + *width * *height;
+ uint16_t *p = NULL;
+ uint16_t *q = NULL;
+ uint8_t *a = NULL;
+ uint8_t *b = NULL;
+ int i;
+ int j;
+ for ( i = 0; i < hh; i ++ )
+ {
+ p = ( uint16_t * )*image + i * *width;
+ q = end - ( i + 1 ) * *width;
+ j = *width;
+ a = alpha + i * *width;
+ b = alpha + ( *height - i - 1 ) * *width;
+ if ( !reverse )
+ {
+ while ( j -- )
+ {
+ *p ++ = *q ++;
+ *a ++ = *b ++;
+ }
+ }
+ else
+ {
+ while ( j -- )
+ {
+ *q ++ = *p ++;
+ *b ++ = *a ++;
+ }
+ }
+ }
+ }
+ else if ( !strcmp( mirror, "diagonal" ) )
+ {
+ uint8_t *end = ( uint8_t *)*image + *width * *height * 2;
+ uint8_t *p = NULL;
+ uint8_t *q = NULL;
+ uint8_t *a = NULL;
+ uint8_t *b = NULL;
+ int i;
+ int j;
+ int uneven_w = ( *width % 2 ) * 2;
+ for ( i = 0; i < *height; i ++ )
+ {
+ p = ( uint8_t * )*image + i * *width * 2;
+ q = end - i * *width * 2;
+ j = ( ( *width * ( *height - i ) ) / *height ) / 2;
+ a = alpha + i * *width;
+ b = alpha + ( *height - i - 1 ) * *width;
+ if ( !reverse )
+ {
+ while ( j -- )
+ {
+ *p ++ = *( q - 2 );
+ *p ++ = *( q - 3 - uneven_w );
+ *p ++ = *( q - 4 );
+ *p ++ = *( q - 1 - uneven_w );
+ q -= 4;
+ *a ++ = *b --;
+ *a ++ = *b --;
+ }
+ }
+ else
+ {
+ while ( j -- )
+ {
+ *( q - 2 ) = *p ++;
+ *( q - 3 - uneven_w ) = *p ++;
+ *( q - 4 ) = *p ++;
+ *( q - 1 - uneven_w ) = *p ++;
+ q -= 4;
+ *b -- = *a ++;
+ *b -- = *a ++;
+ }
+ }
+ }
+ }
+ else if ( !strcmp( mirror, "xdiagonal" ) )
+ {
+ uint8_t *end = ( uint8_t *)*image + *width * *height * 2;
+ uint8_t *p = NULL;
+ uint8_t *q = NULL;
+ int i;
+ int j;
+ uint8_t *a = NULL;
+ uint8_t *b = NULL;
+ int uneven_w = ( *width % 2 ) * 2;
+ for ( i = 0; i < *height; i ++ )
+ {
+ p = ( uint8_t * )*image + ( i + 1 ) * *width * 2;
+ q = end - ( i + 1 ) * *width * 2;
+ j = ( ( *width * ( *height - i ) ) / *height ) / 2;
+ a = alpha + ( i + 1 ) * *width - 1;
+ b = alpha + ( *height - i - 1 ) * *width;
+ if ( !reverse )
+ {
+ while ( j -- )
+ {
+ *q ++ = *( p - 2 );
+ *q ++ = *( p - 3 - uneven_w );
+ *q ++ = *( p - 4 );
+ *q ++ = *( p - 1 - uneven_w );
+ p -= 4;
+ *b ++ = *a --;
+ *b ++ = *a --;
+ }
+ }
+ else
+ {
+ while ( j -- )
+ {
+ *( p - 2 ) = *q ++;
+ *( p - 3 - uneven_w ) = *q ++;
+ *( p - 4 ) = *q ++;
+ *( p - 1 - uneven_w ) = *q ++;
+ p -= 4;
+ *a -- = *b ++;
+ *a -- = *b ++;
+ }
+ }
+ }
+ }
+ else if ( !strcmp( mirror, "flip" ) )
+ {
+ uint8_t t[ 4 ];
+ uint8_t *p = NULL;
+ uint8_t *q = NULL;
+ int i;
+ uint8_t *a = NULL;
+ uint8_t *b = NULL;
+ uint8_t c;
+ int uneven_w = ( *width % 2 ) * 2;
+ for ( i = 0; i < *height; i ++ )
+ {
+ p = ( uint8_t * )*image + i * *width * 2;
+ q = p + *width * 2;
+ a = alpha + i * *width;
+ b = a + *width - 1;
+ while ( p < q )
+ {
+ t[ 0 ] = p[ 0 ];
+ t[ 1 ] = p[ 1 + uneven_w ];
+ t[ 2 ] = p[ 2 ];
+ t[ 3 ] = p[ 3 + uneven_w ];
+ *p ++ = *( q - 2 );
+ *p ++ = *( q - 3 - uneven_w );
+ *p ++ = *( q - 4 );
+ *p ++ = *( q - 1 - uneven_w );
+ *( -- q ) = t[ 3 ];
+ *( -- q ) = t[ 0 ];
+ *( -- q ) = t[ 1 ];
+ *( -- q ) = t[ 2 ];
+ c = *a;
+ *a ++ = *b;
+ *b -- = c;
+ c = *a;
+ *a ++ = *b;
+ *b -- = c;
+ }
+ }
+ }
+ else if ( !strcmp( mirror, "flop" ) )
+ {
+ uint16_t *end = ( uint16_t *)*image + *width * *height;
+ uint16_t *p = NULL;
+ uint16_t *q = NULL;
+ uint16_t t;
+ uint8_t *a = NULL;
+ uint8_t *b = NULL;
+ uint8_t c;
+ int i;
+ int j;
+ for ( i = 0; i < hh; i ++ )
+ {
+ p = ( uint16_t * )*image + i * *width;
+ q = end - ( i + 1 ) * *width;
+ a = alpha + i * *width;
+ b = alpha + ( *height - i - 1 ) * *width;
+ j = *width;
+ while ( j -- )
+ {
+ t = *p;
+ *p ++ = *q;
+ *q ++ = t;
+ c = *a;
+ *a ++ = *b;
+ *b ++ = c;
+ }
+ }
+ }
+ }
+
+ // Return the error
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the service on to the stack
+ mlt_frame_push_service( frame, this );
+
+ // Push the filter method on to the stack
+ mlt_frame_push_service( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Construct a new filter
+ mlt_filter this = mlt_filter_new( );
+
+ // If we have a filter, initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Set the default mirror type
+ mlt_properties_set_or_default( properties, "mirror", arg, "horizontal" );
+
+ // Assign the process method
+ this->process = filter_process;
+ }
+
+ // Return the filter
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_mono.c -- mix all channels to a mono signal across n channels
+ * Copyright (C) 2003-2006 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties of the a frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+ int channels_out = mlt_properties_get_int( properties, "mono.channels" );
+ int i, j, size;
+ int16_t *new_buffer;
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+
+ size = *samples * channels_out * sizeof( int16_t );
+ new_buffer = mlt_pool_alloc( size );
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Mix
+ for ( i = 0; i < *samples; i++ )
+ {
+ int16_t mixdown = 0;
+ for ( j = 0; j < *channels; j++ )
+ mixdown += (*buffer)[ ( i * *channels ) + j ] / *channels;
+ for ( j = 0; j < channels_out; j++ )
+ new_buffer[ ( i * channels_out ) + j ] = mixdown;
+ }
+
+ // Apply results
+ *buffer = new_buffer;
+ *channels = channels_out;
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
+
+ // Propogate the parameters
+ mlt_properties_set_int( frame_props, "mono.channels", mlt_properties_get_int( properties, "channels" ) );
+
+ // Override the get_audio method
+ mlt_frame_push_audio( frame, filter_get_audio );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ if ( arg != NULL )
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", atoi( arg ) );
+ else
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", 2 );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_obscure.c -- obscure filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Geometry struct.
+*/
+
+struct geometry_s
+{
+ int nw;
+ int nh;
+ float x;
+ float y;
+ float w;
+ float h;
+ int mask_w;
+ int mask_h;
+};
+
+/** Parse a value from a geometry string.
+*/
+
+static inline float parse_value( char **ptr, int normalisation, char delim, float defaults )
+{
+ float value = defaults;
+
+ if ( *ptr != NULL && **ptr != '\0' )
+ {
+ char *end = NULL;
+ value = strtod( *ptr, &end );
+ if ( end != NULL )
+ {
+ if ( *end == '%' )
+ value = ( value / 100.0 ) * normalisation;
+ while ( *end == delim || *end == '%' )
+ end ++;
+ }
+ *ptr = end;
+ }
+
+ return value;
+}
+
+/** Parse a geometry property string.
+*/
+
+static void geometry_parse( struct geometry_s *geometry, struct geometry_s *defaults, char *property, int nw, int nh )
+{
+ // Assign normalised width and height
+ geometry->nw = nw;
+ geometry->nh = nh;
+
+ // Assign from defaults if available
+ if ( defaults != NULL )
+ {
+ geometry->x = defaults->x;
+ geometry->y = defaults->y;
+ geometry->w = defaults->w;
+ geometry->h = defaults->h;
+ geometry->mask_w = defaults->mask_w;
+ geometry->mask_h = defaults->mask_h;
+ }
+ else
+ {
+ geometry->x = 0;
+ geometry->y = 0;
+ geometry->w = nw;
+ geometry->h = nh;
+ geometry->mask_w = 20;
+ geometry->mask_h = 20;
+ }
+
+ // Parse the geomtry string
+ if ( property != NULL )
+ {
+ char *ptr = property;
+ geometry->x = parse_value( &ptr, nw, ',', geometry->x );
+ geometry->y = parse_value( &ptr, nh, ':', geometry->y );
+ geometry->w = parse_value( &ptr, nw, 'x', geometry->w );
+ geometry->h = parse_value( &ptr, nh, ':', geometry->h );
+ geometry->mask_w = parse_value( &ptr, nw, 'x', geometry->mask_w );
+ geometry->mask_h = parse_value( &ptr, nh, ' ', geometry->mask_h );
+ }
+}
+
+/** A Timism but not as clean ;-).
+*/
+
+static float lerp( float value, float lower, float upper )
+{
+ if ( value < lower )
+ return lower;
+ else if ( value > upper )
+ return upper;
+ return value;
+}
+
+/** Calculate real geometry.
+*/
+
+static void geometry_calculate( struct geometry_s *output, struct geometry_s *in, struct geometry_s *out, float position, int ow, int oh )
+{
+ // Calculate this frames geometry
+ output->x = lerp( ( in->x + ( out->x - in->x ) * position ) / ( float )out->nw * ow, 0, ow );
+ output->y = lerp( ( in->y + ( out->y - in->y ) * position ) / ( float )out->nh * oh, 0, oh );
+ output->w = lerp( ( in->w + ( out->w - in->w ) * position ) / ( float )out->nw * ow, 0, ow - output->x );
+ output->h = lerp( ( in->h + ( out->h - in->h ) * position ) / ( float )out->nh * oh, 0, oh - output->y );
+ output->mask_w = in->mask_w + ( out->mask_w - in->mask_w ) * position;
+ output->mask_h = in->mask_h + ( out->mask_h - in->mask_h ) * position;
+}
+
+/** Calculate the position for this frame.
+*/
+
+static float position_calculate( mlt_filter this, mlt_frame frame )
+{
+ // Get the in and out position
+ mlt_position in = mlt_filter_get_in( this );
+ mlt_position out = mlt_filter_get_out( this );
+
+ // Get the position of the frame
+ mlt_position position = mlt_frame_get_position( frame );
+
+ // Now do the calcs
+ return ( float )( position - in ) / ( float )( out - in + 1 );
+}
+
+/** The averaging function...
+*/
+
+static inline void obscure_average( uint8_t *start, int width, int height, int stride )
+{
+ register int y;
+ register int x;
+ register int Y = ( *start + *( start + 2 ) ) / 2;
+ register int U = *( start + 1 );
+ register int V = *( start + 3 );
+ register uint8_t *p;
+ register int components = width >> 1;
+
+ y = height;
+ while( y -- )
+ {
+ p = start;
+ x = components;
+ while( x -- )
+ {
+ Y = ( Y + *p ++ ) >> 1;
+ U = ( U + *p ++ ) >> 1;
+ Y = ( Y + *p ++ ) >> 1;
+ V = ( V + *p ++ ) >> 1;
+ }
+ start += stride;
+ }
+
+ start -= height * stride;
+ y = height;
+ while( y -- )
+ {
+ p = start;
+ x = components;
+ while( x -- )
+ {
+ *p ++ = Y;
+ *p ++ = U;
+ *p ++ = Y;
+ *p ++ = V;
+ }
+ start += stride;
+ }
+}
+
+
+/** The obscurer rendering function...
+*/
+
+static void obscure_render( uint8_t *image, int width, int height, struct geometry_s result )
+{
+ int area_x = result.x;
+ int area_y = result.y;
+ int area_w = result.w;
+ int area_h = result.h;
+
+ int mw = result.mask_w;
+ int mh = result.mask_h;
+ int w;
+ int h;
+ int aw;
+ int ah;
+
+ uint8_t *p = image + area_y * width * 2 + area_x * 2;
+
+ for ( w = 0; w < area_w; w += mw )
+ {
+ for ( h = 0; h < area_h; h += mh )
+ {
+ aw = w + mw > area_w ? mw - ( w + mw - area_w ) : mw;
+ ah = h + mh > area_h ? mh - ( h + mh - area_h ) : mh;
+ if ( aw > 1 && ah > 1 )
+ obscure_average( p + h * ( width << 1 ) + ( w << 1 ), aw, ah, width << 1 );
+ }
+ }
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Pop the top of stack now
+ mlt_filter this = mlt_frame_pop_service( frame );
+
+ // Get the image from the frame
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ // Get the image from the frame
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ if ( this != NULL )
+ {
+ // Get the filter properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Obtain the normalised width and height from the frame
+ int normalised_width = mlt_properties_get_int( frame_properties, "normalised_width" );
+ int normalised_height = mlt_properties_get_int( frame_properties, "normalised_height" );
+
+ // Structures for geometry
+ struct geometry_s result;
+ struct geometry_s start;
+ struct geometry_s end;
+
+ // Retrieve the position
+ float position = mlt_properties_get_double(frame_properties, "filter_position");
+
+ // Now parse the geometries
+ geometry_parse( &start, NULL, mlt_properties_get( properties, "start" ), normalised_width, normalised_height );
+ geometry_parse( &end, &start, mlt_properties_get( properties, "end" ), normalised_width, normalised_height );
+
+ // Do the calculation
+ geometry_calculate( &result, &start, &end, position, *width, *height );
+
+ // Now actually render it
+ obscure_render( *image, *width, *height, result );
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push this on to the service stack
+ mlt_frame_push_service( frame, this );
+
+ // Calculate the position for the filter effect
+ float position = position_calculate( this, frame );
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "filter_position", position );
+
+ // Push the get image call
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_obscure_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ this->process = filter_process;
+ mlt_properties_set( properties, "start", arg != NULL ? arg : "0%,0%:100%x100%" );
+ mlt_properties_set( properties, "end", "" );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_region.c -- region filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "transition_region.h"
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the properties of the filter
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Get the region transition
+ mlt_transition transition = mlt_properties_get_data( properties, "_transition", NULL );
+
+ // Create the transition if not available
+ if ( transition == NULL )
+ {
+ // Create the transition
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ transition = mlt_factory_transition( profile, "region", NULL );
+
+ // Register with the filter
+ mlt_properties_set_data( properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
+
+ // Pass a reference to this filter down
+ mlt_properties_set_data( MLT_TRANSITION_PROPERTIES( transition ), "_region_filter", this, 0, NULL, NULL );
+ }
+
+ // Pass all properties down
+ mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "" );
+
+ // Process the frame
+ return mlt_transition_process( transition, frame, NULL );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create a new filter
+ mlt_filter this = mlt_filter_new( );
+
+ // Further initialisation
+ if ( this != NULL )
+ {
+ // Get the properties from the filter
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Assign the filter process method
+ this->process = filter_process;
+
+ // Resource defines the shape of the region
+ mlt_properties_set( properties, "resource", arg == NULL ? "rectangle" : arg );
+
+ // Ensure that attached filters are handled privately
+ mlt_properties_set_int( properties, "_filter_private", 1 );
+ }
+
+ // Return the filter
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_rescale.c -- scale the producer video frame size to match the consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+typedef int ( *image_scaler )( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight );
+
+static void scale_alpha( mlt_frame this, int iwidth, int iheight, int owidth, int oheight );
+
+static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight )
+{
+ // Get the properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the rescaling interpolsation
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+
+ // Carry out the rescaling
+ if ( iformat == mlt_image_yuv422 && oformat == mlt_image_yuv422 )
+ {
+ // Scale the frame
+ mlt_frame_rescale_yuv422( this, owidth, oheight );
+
+ // Return the output
+ *image = mlt_properties_get_data( properties, "image", NULL );
+
+ // Scale the alpha channel only if exists and not correct size
+ int alpha_size = 0;
+ mlt_properties_get_data( properties, "alpha", &alpha_size );
+ if ( alpha_size > 0 && alpha_size != ( owidth * oheight ) )
+ scale_alpha( this, iwidth, iheight, owidth, oheight );
+ }
+ else if ( iformat == mlt_image_rgb24 || iformat == mlt_image_rgb24a )
+ {
+ int bpp = (iformat == mlt_image_rgb24a ? 4 : 3 );
+
+ // Create the yuv image
+ uint8_t *output = mlt_pool_alloc( iwidth * ( iheight + 1 ) * 2 );
+
+ if ( strcmp( interps, "none" ) && ( iwidth != owidth || iheight != oheight ) )
+ {
+ // Extract YUV422 and alpha
+ if ( bpp == 4 )
+ {
+ // Allocate the alpha mask
+ uint8_t *alpha = mlt_pool_alloc( iwidth * ( iheight + 1 ) );
+
+ // Convert the image and extract alpha
+ mlt_convert_rgb24a_to_yuv422( *image, iwidth, iheight, iwidth * 4, output, alpha );
+
+ mlt_properties_set_data( properties, "alpha", alpha, iwidth * ( iheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+
+ scale_alpha( this, iwidth, iheight, owidth, oheight );
+ }
+ else
+ {
+ // No alpha to extract
+ mlt_convert_rgb24_to_yuv422( *image, iwidth, iheight, iwidth * 3, output );
+ }
+
+ mlt_properties_set_data( properties, "image", output, iwidth * ( iheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Scale the frame
+ output = mlt_frame_rescale_yuv422( this, owidth, oheight );
+
+ }
+ else
+ {
+ // Extract YUV422 and alpha
+ if ( bpp == 4 )
+ {
+ // Allocate the alpha mask
+ uint8_t *alpha = mlt_pool_alloc( owidth * ( oheight + 1 ) );
+
+ // Convert the image and extract alpha
+ mlt_convert_rgb24a_to_yuv422( *image, owidth, oheight, owidth * 4, output, alpha );
+
+ mlt_properties_set_data( properties, "alpha", alpha, owidth * ( oheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+
+ scale_alpha( this, iwidth, iheight, owidth, oheight );
+ }
+ else
+ {
+ // No alpha to extract
+ mlt_convert_rgb24_to_yuv422( *image, owidth, oheight, owidth * 3, output );
+ }
+ }
+
+ // Now update the frame
+ mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+
+ *image = output;
+ }
+
+ return 0;
+}
+
+static void scale_alpha( mlt_frame this, int iwidth, int iheight, int owidth, int oheight )
+{
+ // Scale the alpha
+ uint8_t *output = NULL;
+ uint8_t *input = mlt_frame_get_alpha_mask( this );
+
+ if ( input != NULL )
+ {
+ uint8_t *out_line;
+ int x, y;
+ int ox = ( iwidth << 10 ) / owidth;
+ int oy = ( iheight << 10 ) / oheight;
+
+ output = mlt_pool_alloc( owidth * oheight );
+ out_line = output;
+
+ // Loop for the entirety of our output height.
+ for ( y = 0; y < oheight; y ++ )
+ for ( x = 0; x < owidth; x ++ )
+ *out_line ++ = *( input + ( ( 512 + ( y * oy * iwidth ) + x * ox ) >> 10 ) );
+
+ // Set it back on the frame
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", output, owidth * oheight, mlt_pool_release, NULL );
+ }
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the frame properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the filter from the stack
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the image scaler method
+ image_scaler scaler_method = mlt_properties_get_data( filter_properties, "method", NULL );
+
+ // Correct Width/height if necessary
+ if ( *width == 0 || *height == 0 )
+ {
+ *width = mlt_properties_get_int( properties, "normalised_width" );
+ *height = mlt_properties_get_int( properties, "normalised_height" );
+ }
+
+ // There can be problems with small images - avoid them (by hacking - gah)
+ if ( *width >= 6 && *height >= 6 )
+ {
+ int iwidth = *width;
+ int iheight = *height;
+ int owidth = *width;
+ int oheight = *height;
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+ int wanted_format = *format;
+
+ // Default from the scaler if not specifed on the frame
+ if ( interps == NULL )
+ {
+ interps = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "interpolation" );
+ mlt_properties_set( properties, "rescale.interp", interps );
+ }
+
+ // If real_width/height exist, we want that as minimum information
+ if ( mlt_properties_get_int( properties, "real_width" ) )
+ {
+ iwidth = mlt_properties_get_int( properties, "real_width" );
+ iheight = mlt_properties_get_int( properties, "real_height" );
+ }
+
+ // Let the producer know what we are actually requested to obtain
+ if ( *format == mlt_image_yuv422 && strcmp( interps, "none" ) )
+ {
+ mlt_properties_set_int( properties, "rescale_width", *width );
+ mlt_properties_set_int( properties, "rescale_height", *height );
+ }
+ else
+ {
+ // When no scaling is requested, revert the requested dimensions if possible
+ mlt_properties_set_int( properties, "rescale_width", iwidth );
+ mlt_properties_set_int( properties, "rescale_height", iheight );
+ }
+
+ // Deinterlace if height is changing to prevent fields mixing on interpolation
+ // One exception: non-interpolated, integral scaling
+ if ( iheight != oheight && ( strcmp( interps, "nearest" ) || ( iheight % oheight != 0 ) ) )
+ mlt_properties_set_int( properties, "consumer_deinterlace", 1 );
+
+ // Get the image as requested
+ mlt_frame_get_image( this, image, format, &iwidth, &iheight, writable );
+
+ // Get rescale interpretation again, in case the producer wishes to override scaling
+ interps = mlt_properties_get( properties, "rescale.interp" );
+
+ if ( *image != NULL && ( *format != mlt_image_yuv422 || ( iwidth != owidth || iheight != oheight ) ) )
+ {
+ // If the colour space is correct and scaling is off, do nothing
+ if ( *format == mlt_image_yuv422 && !strcmp( interps, "none" ) )
+ {
+ *width = iwidth;
+ *height = iheight;
+ }
+ else if ( *format == mlt_image_yuv422 )
+ {
+ // Call the local scaler
+ scaler_method( this, image, *format, mlt_image_yuv422, iwidth, iheight, owidth, oheight );
+ *width = owidth;
+ *height = oheight;
+ }
+ else if ( *format == mlt_image_rgb24 && wanted_format == mlt_image_rgb24 )
+ {
+ // Call the local scaler
+ scaler_method( this, image, *format, mlt_image_rgb24, iwidth, iheight, owidth, oheight );
+
+ // Return the output
+ *width = owidth;
+ *height = oheight;
+ }
+ else if ( *format == mlt_image_rgb24 || *format == mlt_image_rgb24a )
+ {
+ // Call the local scaler
+ scaler_method( this, image, *format, mlt_image_yuv422, iwidth, iheight, owidth, oheight );
+
+ // Return the output
+ *format = mlt_image_yuv422;
+ *width = owidth;
+ *height = oheight;
+ }
+ else
+ {
+ *width = iwidth;
+ *height = iheight;
+ }
+ }
+ else
+ {
+ *width = iwidth;
+ *height = iheight;
+ }
+ }
+ else
+ {
+ // Store the requested width/height
+ int iwidth = *width;
+ int iheight = *height;
+
+ // Get the image as requested
+ mlt_frame_get_image( this, image, format, &iwidth, &iheight, writable );
+
+ // Too small - for now just assign as though we got what we wanted
+ *width = iwidth;
+ *height = iheight;
+ }
+
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the filter
+ mlt_frame_push_service( frame, this );
+
+ // Push the get image method
+ mlt_frame_push_service( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_rescale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create a new scaler
+ mlt_filter this = mlt_filter_new( );
+
+ // If successful, then initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Set the process method
+ this->process = filter_process;
+
+ // Set the inerpolation
+ mlt_properties_set( properties, "interpolation", arg == NULL ? "bilinear" : arg );
+
+ // Set the method
+ mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL );
+ }
+
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_resize.c -- resizing filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Swapbytes inline.
+*/
+
+static inline void swap_bytes( uint8_t *upper, uint8_t *lower )
+{
+ uint8_t t = *lower;
+ *lower = *upper;
+ *upper = t;
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = 0;
+
+ // Get the properties from the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Pop the top of stack now
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Retrieve the aspect ratio
+ double aspect_ratio = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( this ) );
+
+ // Correct Width/height if necessary
+ if ( *width == 0 || *height == 0 )
+ {
+ *width = mlt_properties_get_int( properties, "normalised_width" );
+ *height = mlt_properties_get_int( properties, "normalised_height" );
+ }
+
+ // Assign requested width/height from our subordinate
+ int owidth = *width;
+ int oheight = *height;
+
+ // Check for the special case - no aspect ratio means no problem :-)
+ if ( aspect_ratio == 0.0 )
+ aspect_ratio = mlt_properties_get_double( properties, "consumer_aspect_ratio" );
+
+ // Reset the aspect ratio
+ mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio );
+
+ // Hmmm...
+ char *rescale = mlt_properties_get( properties, "rescale.interp" );
+ if ( rescale != NULL && !strcmp( rescale, "none" ) )
+ return mlt_frame_get_image( this, image, format, width, height, writable );
+
+ if ( mlt_properties_get_int( properties, "distort" ) == 0 )
+ {
+ // Normalise the input and out display aspect
+ int normalised_width = mlt_properties_get_int( properties, "normalised_width" );
+ int normalised_height = mlt_properties_get_int( properties, "normalised_height" );
+ int real_width = mlt_properties_get_int( properties, "real_width" );
+ int real_height = mlt_properties_get_int( properties, "real_height" );
+ if ( real_width == 0 )
+ real_width = mlt_properties_get_int( properties, "width" );
+ if ( real_height == 0 )
+ real_height = mlt_properties_get_int( properties, "height" );
+ double input_ar = aspect_ratio * real_width / real_height;
+ double output_ar = mlt_properties_get_double( properties, "consumer_aspect_ratio" ) * owidth / oheight;
+
+// fprintf( stderr, "real %dx%d normalised %dx%d output %dx%d sar %f in-dar %f out-dar %f\n",
+// real_width, real_height, normalised_width, normalised_height, owidth, oheight, aspect_ratio, input_ar, output_ar);
+
+ // Optimised for the input_ar > output_ar case (e.g. widescreen on standard)
+ int scaled_width = rint( ( input_ar * normalised_width ) / output_ar );
+ int scaled_height = normalised_height;
+
+ // Now ensure that our images fit in the output frame
+ if ( scaled_width > normalised_width )
+ {
+ scaled_width = normalised_width;
+ scaled_height = rint( ( output_ar * normalised_height ) / input_ar );
+ }
+
+ // Now calculate the actual image size that we want
+ owidth = rint( scaled_width * owidth / normalised_width );
+ oheight = rint( scaled_height * oheight / normalised_height );
+
+ // Tell frame we have conformed the aspect to the consumer
+ mlt_frame_set_aspect_ratio( this, mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
+ }
+
+ mlt_properties_set_int( properties, "distort", 0 );
+
+ // Now pass on the calculations down the line
+ mlt_properties_set_int( properties, "resize_width", *width );
+ mlt_properties_set_int( properties, "resize_height", *height );
+
+ // Now get the image
+ error = mlt_frame_get_image( this, image, format, &owidth, &oheight, writable );
+
+ // We only know how to process yuv422 at the moment
+ if ( error == 0 && *format == mlt_image_yuv422 && *image != NULL )
+ {
+ // Get the requested scale operation
+ char *op = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "scale" );
+
+ // Provides a manual override for misreported field order
+ if ( mlt_properties_get( properties, "meta.top_field_first" ) )
+ mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( properties, "meta.top_field_first" ) );
+
+ // Correct field order if needed
+ if ( mlt_properties_get_int( properties, "top_field_first" ) == 1 )
+ {
+ // Get the input image, width and height
+ int size;
+ uint8_t *image = mlt_properties_get_data( properties, "image", &size );
+ uint8_t *ptr = image + owidth * 2;
+ memmove( ptr, image, size - owidth * 2 );
+
+ // Set the normalised field order
+ mlt_properties_set_int( properties, "top_field_first", 0 );
+ mlt_properties_set_int( properties, "meta.top_field_first", 0 );
+ }
+
+ if ( !strcmp( op, "affine" ) )
+ {
+ *image = mlt_frame_rescale_yuv422( this, *width, *height );
+ }
+ else if ( strcmp( op, "none" ) != 0 )
+ {
+ *image = mlt_frame_resize_yuv422( this, *width, *height );
+ }
+ else
+ {
+ *width = owidth;
+ *height = oheight;
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Store the aspect ratio reported by the source
+ mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( frame ), mlt_frame_get_aspect_ratio( frame ) );
+
+ // Push this on to the service stack
+ mlt_frame_push_service( frame, this );
+
+ // Push the get_image method on to the stack
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
+ if ( mlt_filter_init( this, this ) == 0 )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "scale", arg == NULL ? "off" : arg );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_transition.c -- Convert any transition into a filter
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_transition.h>
+
+/** Get the image via the transition.
+ NB: Not all transitions will accept a and b frames being the same...
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_transition transition = mlt_frame_pop_service( this );
+ if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "image_count" ) >= 1 )
+ mlt_transition_process( transition, this, this );
+ return mlt_frame_get_image( this, image, format, width, height, writable );
+}
+
+/** Get the audio via the transition.
+ NB: Not all transitions will accept a and b frames being the same...
+*/
+
+static int filter_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Obtain the transition instance
+ mlt_transition transition = mlt_frame_pop_audio( this );
+ mlt_transition_process( transition, this, this );
+ return mlt_frame_get_audio( this, buffer, format, frequency, channels, samples );
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Obtain the transition instance
+ mlt_transition transition = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "instance", NULL );
+
+ // If we haven't created the instance, do it now
+ if ( transition == NULL )
+ {
+ char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "transition" );
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ transition = mlt_factory_transition( profile, name, NULL );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "instance", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
+ }
+
+ // We may still not have a transition...
+ if ( transition != NULL )
+ {
+ // Get the transition type
+ int type = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type" );
+
+ // Set the basic info
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "in", mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "in" ) );
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "out" ) );
+
+ // Refresh with current user values
+ mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), MLT_FILTER_PROPERTIES( this ), "transition." );
+
+ if ( type & 1 && !mlt_frame_is_test_card( frame ) && !( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "hide" ) & 1 ) )
+ {
+ mlt_frame_push_service( frame, transition );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ }
+ if ( type & 2 && !mlt_frame_is_test_audio( frame ) && !( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "hide" ) & 2 ) )
+ {
+ mlt_frame_push_audio( frame, transition );
+ mlt_frame_push_audio( frame, filter_get_audio );
+ }
+
+ if ( type == 0 )
+ mlt_properties_debug( MLT_TRANSITION_PROPERTIES( transition ), "unknown transition type", stderr );
+ }
+ else
+ {
+ mlt_properties_debug( MLT_FILTER_PROPERTIES( this ), "no transition", stderr );
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_transition_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "transition", arg );
+ this->process = filter_process;
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_watermark.c -- watermark filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_transition.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Error we will return
+ int error = 0;
+
+ // Get the watermark filter object
+ mlt_filter this = mlt_frame_pop_service( frame );
+
+ // Get the properties of the filter
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Get the producer from the filter
+ mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+ // Get the composite from the filter
+ mlt_transition composite = mlt_properties_get_data( properties, "composite", NULL );
+
+ // Get the resource to use
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ // Get the old resource
+ char *old_resource = mlt_properties_get( properties, "_old_resource" );
+
+ // Create a composite if we don't have one
+ if ( composite == NULL )
+ {
+ // Create composite via the factory
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ composite = mlt_factory_transition( profile, "composite", NULL );
+
+ // Register the composite for reuse/destruction
+ if ( composite != NULL )
+ mlt_properties_set_data( properties, "composite", composite, 0, ( mlt_destructor )mlt_transition_close, NULL );
+ }
+
+ // If we have one
+ if ( composite != NULL )
+ {
+ // Get the properties
+ mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
+
+ // Pass all the composite. properties on the filter down
+ mlt_properties_pass( composite_properties, properties, "composite." );
+
+ if ( mlt_properties_get( properties, "composite.out" ) == NULL )
+ mlt_properties_set_int( composite_properties, "out", mlt_properties_get_int( properties, "_out" ) );
+
+ // Force a refresh
+ mlt_properties_set_int( composite_properties, "refresh", 1 );
+ }
+
+ // Create a producer if don't have one
+ if ( producer == NULL || ( old_resource != NULL && strcmp( resource, old_resource ) ) )
+ {
+ // Get the factory producer service
+ char *factory = mlt_properties_get( properties, "factory" );
+
+ // Create the producer
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ producer = mlt_factory_producer( profile, factory, resource );
+
+ // If we have one
+ if ( producer != NULL )
+ {
+ // Register the producer for reuse/destruction
+ mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+
+ // Ensure that we loop
+ mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
+
+ // Set the old resource
+ mlt_properties_set( properties, "_old_resource", resource );
+ }
+ }
+
+ if ( producer != NULL )
+ {
+ // Get the producer properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Now pass all producer. properties on the filter down
+ mlt_properties_pass( producer_properties, properties, "producer." );
+ }
+
+ // Only continue if we have both producer and composite
+ if ( composite != NULL && producer != NULL )
+ {
+ // Get the service of the producer
+ mlt_service service = MLT_PRODUCER_SERVICE( producer );
+
+ // We will get the 'b frame' from the producer
+ mlt_frame b_frame = NULL;
+
+ // Get the unique id of the filter (used to reacquire the producer position)
+ char *name = mlt_properties_get( properties, "_unique_id" );
+
+ // Get the original producer position
+ mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
+
+ // Make sure the producer is in the correct position
+ mlt_producer_seek( producer, position );
+
+ // Resetting position to appease the composite transition
+ mlt_frame_set_position( frame, position );
+
+ // Get the b frame and process with composite if successful
+ if ( mlt_service_get_frame( service, &b_frame, 0 ) == 0 )
+ {
+ // Get the a and b frame properties
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( frame );
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ // Set the b frame to be in the same position and have same consumer requirements
+ mlt_frame_set_position( b_frame, position );
+ mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+ mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "deinterlace" ) );
+ mlt_properties_set_double( b_props, "output_ratio", mlt_properties_get_double( a_props, "output_ratio" ) );
+
+ // Check for the special case - no aspect ratio means no problem :-)
+ if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 )
+ mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+ if ( mlt_frame_get_aspect_ratio( frame ) == 0 )
+ mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+ mlt_properties_set_int( b_props, "normalised_width", mlt_properties_get_int( a_props, "normalised_width" ) );
+ mlt_properties_set_int( b_props, "normalised_height", mlt_properties_get_int( a_props, "normalised_height" ) );
+
+ if ( mlt_properties_get_int( properties, "distort" ) )
+ {
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( composite ), "distort", 1 );
+ mlt_properties_set_int( a_props, "distort", 1 );
+ mlt_properties_set_int( b_props, "distort", 1 );
+ }
+
+ if ( mlt_properties_get_int( properties, "reverse" ) == 0 )
+ {
+ // Apply all filters that are attached to this filter to the b frame
+ mlt_service_apply_filters( MLT_FILTER_SERVICE( this ), b_frame, 0 );
+
+ // Process the frame
+ mlt_transition_process( composite, frame, b_frame );
+
+ // Get the image
+ error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+ }
+ else
+ {
+ char temp[ 132 ];
+ int count = 0;
+ uint8_t *alpha = NULL;
+ const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
+ if ( rescale == NULL || !strcmp( rescale, "none" ) )
+ rescale = "hyper";
+ mlt_transition_process( composite, b_frame, frame );
+ mlt_properties_set_int( a_props, "consumer_deinterlace", 1 );
+ mlt_properties_set_int( b_props, "consumer_deinterlace", 1 );
+ mlt_properties_set( a_props, "rescale.interp", rescale );
+ mlt_properties_set( b_props, "rescale.interp", rescale );
+ mlt_service_apply_filters( MLT_FILTER_SERVICE( this ), b_frame, 0 );
+ error = mlt_frame_get_image( b_frame, image, format, width, height, 1 );
+ alpha = mlt_frame_get_alpha_mask( b_frame );
+ mlt_properties_set_data( a_props, "image", *image, *width * *height * 2, NULL, NULL );
+ mlt_properties_set_data( a_props, "alpha", alpha, *width * *height, NULL, NULL );
+ mlt_properties_set_int( a_props, "width", *width );
+ mlt_properties_set_int( a_props, "height", *height );
+ mlt_properties_set_int( a_props, "progressive", 1 );
+ mlt_properties_inc_ref( b_props );
+ strcpy( temp, "_b_frame" );
+ while( mlt_properties_get_data( a_props, temp, NULL ) != NULL )
+ sprintf( temp, "_b_frame%d", count ++ );
+ mlt_properties_set_data( a_props, temp, b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ }
+ }
+
+ // Close the b frame
+ mlt_frame_close( b_frame );
+ }
+ else
+ {
+ // Get the image from the frame without running fx
+ error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get a unique name to store the frame position
+ char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" );
+
+ // Assign the frame out point to the filter (just in case we need it later)
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_out", mlt_properties_get_int( properties, "out" ) );
+
+ // Assign the current position to the name
+ mlt_properties_set_position( properties, name, mlt_frame_get_position( frame ) - mlt_filter_get_in( this ) );
+
+ // Push the filter on to the stack
+ mlt_frame_push_service( frame, this );
+
+ // Push the get_image on to the stack
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_watermark_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ this->process = filter_process;
+ mlt_properties_set( properties, "factory", "fezzik" );
+ if ( arg != NULL )
+ mlt_properties_set( properties, "resource", arg );
+ // Ensure that attached filters are handled privately
+ mlt_properties_set_int( properties, "_filter_private", 1 );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * producer_colour.c -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_pool.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+typedef struct
+{
+ uint8_t r, g, b, a;
+} rgba_color;
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_colour_init( mlt_profile profile, mlt_service_type type, const char *id, char *colour )
+{
+ mlt_producer producer = calloc( 1, sizeof( struct mlt_producer_s ) );
+ if ( producer != NULL && mlt_producer_init( producer, NULL ) == 0 )
+ {
+ // Get the properties interface
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Callback registration
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Set the default properties
+ mlt_properties_set( properties, "resource", colour == NULL ? "0x000000ff" : colour );
+ mlt_properties_set( properties, "_resource", "" );
+ mlt_properties_set_double( properties, "aspect_ratio", 0 );
+
+ return producer;
+ }
+ free( producer );
+ return NULL;
+}
+
+rgba_color parse_color( char *color, unsigned int color_int )
+{
+ rgba_color result = { 0xff, 0xff, 0xff, 0xff };
+
+ if ( !strcmp( color, "red" ) )
+ {
+ result.r = 0xff;
+ result.g = 0x00;
+ result.b = 0x00;
+ }
+ else if ( !strcmp( color, "green" ) )
+ {
+ result.r = 0x00;
+ result.g = 0xff;
+ result.b = 0x00;
+ }
+ else if ( !strcmp( color, "blue" ) )
+ {
+ result.r = 0x00;
+ result.g = 0x00;
+ result.b = 0xff;
+ }
+ else if ( strcmp( color, "white" ) )
+ {
+ result.r = ( color_int >> 24 ) & 0xff;
+ result.g = ( color_int >> 16 ) & 0xff;
+ result.b = ( color_int >> 8 ) & 0xff;
+ result.a = ( color_int ) & 0xff;
+ }
+
+ return result;
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // May need to know the size of the image to clone it
+ int size = 0;
+
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer for this frame
+ mlt_producer producer = mlt_properties_get_data( properties, "producer_colour", NULL );
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Get the current and previous colour strings
+ char *now = mlt_properties_get( producer_props, "resource" );
+ char *then = mlt_properties_get( producer_props, "_resource" );
+
+ // Get the current image and dimensions cached in the producer
+ uint8_t *image = mlt_properties_get_data( producer_props, "image", &size );
+ int current_width = mlt_properties_get_int( producer_props, "_width" );
+ int current_height = mlt_properties_get_int( producer_props, "_height" );
+
+ // Parse the colour
+ if ( now && strchr( now, '/' ) )
+ {
+ now = strrchr( now, '/' ) + 1;
+ mlt_properties_set( producer_props, "resource", now );
+ }
+ rgba_color color = parse_color( now, mlt_properties_get_int( producer_props, "resource" ) );
+
+ // See if we need to regenerate
+ if ( strcmp( now, then ) || *width != current_width || *height != current_height )
+ {
+ // Color the image
+ uint8_t y, u, v;
+ int i = *height;
+ int j = 0;
+ int uneven = *width % 2;
+ int count = ( *width - uneven ) / 2;
+ uint8_t *p = NULL;
+
+ // Allocate the image
+ size = *width * *height * 2;
+ image = mlt_pool_alloc( size );
+
+ // Update the producer
+ mlt_properties_set_data( producer_props, "image", image, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( producer_props, "_width", *width );
+ mlt_properties_set_int( producer_props, "_height", *height );
+ mlt_properties_set( producer_props, "_resource", now );
+
+ RGB2YUV( color.r, color.g, color.b, y, u, v );
+
+ p = image;
+
+ while ( i -- )
+ {
+ j = count;
+ while ( j -- )
+ {
+ *p ++ = y;
+ *p ++ = u;
+ *p ++ = y;
+ *p ++ = v;
+ }
+ if ( uneven )
+ {
+ *p ++ = y;
+ *p ++ = u;
+ }
+ }
+ }
+
+ // Update the frame
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+
+ // Clone if necessary (deemed always necessary)
+ if ( 1 )
+ {
+ // Create the alpha channel
+ uint8_t *alpha = mlt_pool_alloc( size >> 1 );
+
+ // Clone our image
+ uint8_t *copy = mlt_pool_alloc( size );
+ memcpy( copy, image, size );
+
+ // We're going to pass the copy on
+ image = copy;
+
+ // Initialise the alpha
+ if ( alpha )
+ memset( alpha, color.a, size >> 1 );
+
+ // Now update properties so we free the copy after
+ mlt_properties_set_data( properties, "image", copy, size, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha, size >> 1, mlt_pool_release, NULL );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+ }
+
+ // Pass on the image
+ *buffer = image;
+ *format = mlt_image_yuv422;
+
+ return 0;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ if ( *frame != NULL )
+ {
+ // Obtain properties of frame and producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Set the producer on the frame properties
+ mlt_properties_set_data( properties, "producer_colour", producer, 0, NULL, NULL );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", 1 );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+
+ // colour is an alias for resource
+ if ( mlt_properties_get( producer_props, "colour" ) != NULL )
+ mlt_properties_set( producer_props, "resource", mlt_properties_get( producer_props, "colour" ) );
+
+ // Push the get_image method
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer producer )
+{
+ producer->close = NULL;
+ mlt_producer_close( producer );
+ free( producer );
+}
--- /dev/null
+/*
+ * producer_consumer.c -- produce as a consumer of an encapsulated producer
+ * Copyright (C) 2008 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+struct context_s {
+ mlt_producer this;
+ mlt_producer producer;
+ mlt_consumer consumer;
+ mlt_profile profile;
+ int is_close_profile;
+};
+typedef struct context_s *context;
+
+
+static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ context cx = mlt_frame_pop_service( frame );
+ mlt_frame nested_frame = mlt_frame_pop_service( frame );
+
+ *width = cx->profile->width;
+ *height = cx->profile->height;
+
+ int result = mlt_frame_get_image( nested_frame, image, format, width, height, writable );
+
+ // Allocate the image
+ int size = *width * *height * ( *format == mlt_image_yuv422 ? 2 : *format == mlt_image_rgb24 ? 3 : *format == mlt_image_rgb24a ? 4 : ( 3 / 2 ) );
+ uint8_t *new_image = mlt_pool_alloc( size );
+
+ // Update the frame
+ mlt_properties properties = mlt_frame_properties( frame );
+ mlt_properties_set_data( properties, "image", new_image, size, mlt_pool_release, NULL );
+ memcpy( new_image, *image, size );
+ mlt_frame_close( nested_frame );
+ *image = new_image;
+
+// mlt_properties_debug( properties, "frame", stderr );
+// mlt_properties_debug( mlt_frame_properties( nested_frame ), "nested_frame", stderr );
+
+ return result;
+}
+
+static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ mlt_frame nested_frame = mlt_frame_pop_audio( frame );
+ int result = mlt_frame_get_audio( nested_frame, buffer, format, frequency, channels, samples );
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "audio", new_buffer, size, mlt_pool_release, NULL );
+ memcpy( new_buffer, *buffer, size );
+ *buffer = new_buffer;
+ mlt_frame_close( nested_frame );
+ return result;
+}
+
+static int get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES(this);
+ context cx = mlt_properties_get_data( properties, "context", NULL );
+
+ if ( !cx )
+ {
+ // Allocate and initialize our context
+ cx = mlt_pool_alloc( sizeof( struct context_s ) );
+ mlt_properties_set_data( properties, "context", cx, 0, mlt_pool_release, NULL );
+ cx->this = this;
+ char *profile_name = mlt_properties_get( properties, "profile" );
+ mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) );
+
+ if ( profile_name )
+ {
+ cx->profile = mlt_profile_init( profile_name );
+ cx->is_close_profile = 1;
+ }
+ else
+ {
+ cx->profile = profile;
+ cx->is_close_profile = 0;
+ }
+
+ // For now, we must conform the nested network's frame rate to the parent network's
+ // framerate.
+ cx->profile->frame_rate_num = profile->frame_rate_num;
+ cx->profile->frame_rate_den = profile->frame_rate_den;
+
+ // We will encapsulate a consumer
+ cx->consumer = mlt_consumer_new( cx->profile );
+ // Do not use _pass_list on real_time so that it defaults to 0 in the absence of
+ // an explicit real_time property.
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( cx->consumer ), "real_time",
+ mlt_properties_get_int( properties, "real_time" ) );
+ mlt_properties_pass_list( MLT_CONSUMER_PROPERTIES( cx->consumer ), properties,
+ "buffer, prefill" );
+
+ // Encapsulate a real producer for the resource
+ cx->producer = mlt_factory_producer( cx->profile, mlt_environment( "MLT_PRODUCER" ),
+ mlt_properties_get( properties, "resource" ) );
+ mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( cx->producer ),
+ "out, length" );
+
+ // Since we control the seeking, prevent it from seeking on its own
+ mlt_producer_set_speed( cx->producer, 0 );
+
+ // Connect it all together
+ mlt_consumer_connect( cx->consumer, MLT_PRODUCER_SERVICE( cx->producer ) );
+ mlt_consumer_start( cx->consumer );
+ }
+
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+ if ( frame )
+ {
+ // Our "in" needs to be the same, keep it so
+ mlt_properties_pass_list( MLT_PRODUCER_PROPERTIES( cx->producer ), properties, "in, out" );
+
+ // Seek the producer to the correct place
+ // Calculate our positions
+ double actual_position = mlt_producer_get_speed( this ) * (double)mlt_producer_position( this );
+ mlt_position need_first = floor( actual_position );
+ mlt_producer_seek( cx->producer, need_first );
+
+ // Get the nested frame
+ mlt_frame nested_frame = mlt_consumer_rt_frame( cx->consumer );
+
+ // Stack the producer and our methods on the nested frame
+ mlt_frame_push_service( *frame, nested_frame );
+ mlt_frame_push_service( *frame, cx );
+ mlt_frame_push_get_image( *frame, get_image );
+ mlt_frame_push_audio( *frame, nested_frame );
+ mlt_frame_push_audio( *frame, get_audio );
+
+ // Give the returned frame temporal identity
+ mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+ // Put additional references on the frame so both get_image and get_audio
+ // methods can close it.
+ mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( nested_frame ) );
+
+ // Inform the normalizers about our video properties
+ mlt_properties frame_props = MLT_FRAME_PROPERTIES( *frame );
+ mlt_properties_set_double( frame_props, "aspect_ratio", mlt_profile_sar( cx->profile ) );
+ mlt_properties_set_int( frame_props, "width", cx->profile->width );
+ mlt_properties_set_int( frame_props, "height", cx->profile->height );
+ mlt_properties_set_int( frame_props, "real_width", cx->profile->width );
+ mlt_properties_set_int( frame_props, "real_height", cx->profile->height );
+ mlt_properties_set_int( frame_props, "progressive", cx->profile->progressive );
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( this );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer this )
+{
+ context cx = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( this ), "context", NULL );
+
+ // Shut down all the encapsulated services
+ if ( cx )
+ {
+ mlt_consumer_stop( cx->consumer );
+ mlt_consumer_close( cx->consumer );
+ mlt_producer_close( cx->producer );
+ if ( cx->is_close_profile )
+ mlt_profile_close( cx->profile );
+ }
+
+ this->close = NULL;
+ mlt_producer_close( this );
+ free( this );
+}
+
+mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_producer this = mlt_producer_new( );
+
+ // Encapsulate the real producer
+ mlt_producer real_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), arg );
+
+ if ( this && real_producer )
+ {
+ // Override some producer methods
+ this->close = ( mlt_destructor )producer_close;
+ this->get_frame = get_frame;
+
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+ mlt_properties_set( properties, "resource", arg );
+ mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "out, length" );
+
+ // Done with the producer - will re-open later when we have the profile property
+ mlt_producer_close( real_producer );
+ }
+ else
+ {
+ if ( this )
+ mlt_producer_close( this );
+ if ( real_producer )
+ mlt_producer_close( real_producer );
+
+ this = NULL;
+ }
+ return this;
+}
--- /dev/null
+/*
+ * producer_noise.c -- noise generating producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_pool.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Random number generator
+*/
+
+static unsigned int seed_x = 521288629;
+static unsigned int seed_y = 362436069;
+
+static inline unsigned int fast_rand( )
+{
+ static unsigned int a = 18000, b = 30903;
+ seed_x = a * ( seed_x & 65535 ) + ( seed_x >> 16 );
+ seed_y = b * ( seed_y & 65535 ) + ( seed_y >> 16 );
+ return ( ( seed_x << 16 ) + ( seed_y & 65535 ) );
+}
+
+// Foward declarations
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer this );
+
+/** Initialise.
+*/
+
+mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create a new producer object
+ mlt_producer this = mlt_producer_new( );
+
+ // Initialise the producer
+ if ( this != NULL )
+ {
+ // Callback registration
+ this->get_frame = producer_get_frame;
+ this->close = ( mlt_destructor )producer_close;
+ }
+
+ return this;
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Calculate the size of the image
+ int size = *width * *height * 2;
+
+ // Set the format being returned
+ *format = mlt_image_yuv422;
+
+ // Allocate the image
+ *buffer = mlt_pool_alloc( size );
+
+ // Update the frame
+ mlt_properties_set_data( properties, "image", *buffer, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+
+ // Before we write to the image, make sure we have one
+ if ( *buffer != NULL )
+ {
+ // Calculate the end of the buffer
+ uint8_t *p = *buffer + *width * *height * 2;
+
+ // Value to hold a random number
+ uint32_t value;
+
+ // Generate random noise
+ while ( p != *buffer )
+ {
+ value = fast_rand( ) & 0xff;
+ *( -- p ) = 128;
+ *( -- p ) = value < 16 ? 16 : value > 240 ? 240 : value;
+ }
+ }
+
+ return 0;
+}
+
+static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the frame properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ int size = 0;
+
+ // Correct the returns if necessary
+ *samples = *samples <= 0 ? 1920 : *samples;
+ *channels = *channels <= 0 ? 2 : *channels;
+ *frequency = *frequency <= 0 ? 48000 : *frequency;
+
+ // Calculate the size of the buffer
+ size = *samples * *channels * sizeof( int16_t );
+
+ // Allocate the buffer
+ *buffer = mlt_pool_alloc( size );
+
+ // Make sure we got one and fill it
+ if ( *buffer != NULL )
+ {
+ int16_t *p = *buffer + size / 2;
+ while ( p != *buffer )
+ *( -- p ) = fast_rand( ) & 0x0f00;
+ }
+
+ // Set the buffer for destruction
+ mlt_properties_set_data( properties, "audio", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ return 0;
+}
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+ // Check that we created a frame and initialise it
+ if ( *frame != NULL )
+ {
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Aspect ratio is whatever it needs to be
+ mlt_properties_set_double( properties, "aspect_ratio", 0 );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", 1 );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+ // Push the get_image method
+ mlt_frame_push_get_image( *frame, producer_get_image );
+
+ // Specify the audio
+ mlt_frame_push_audio( *frame, producer_get_audio );
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( this );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer this )
+{
+ this->close = NULL;
+ mlt_producer_close( this );
+ free( this );
+}
+
--- /dev/null
+/*
+ * producer_ppm.c -- simple ppm test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct producer_ppm_s *producer_ppm;
+
+struct producer_ppm_s
+{
+ struct mlt_producer_s parent;
+ char *command;
+ FILE *video;
+ FILE *audio;
+ uint64_t expected;
+};
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *command )
+{
+ producer_ppm this = calloc( sizeof( struct producer_ppm_s ), 1 );
+ if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+ {
+ mlt_producer producer = &this->parent;
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )producer_close;
+
+ if ( command != NULL )
+ {
+ mlt_properties_set( properties, "resource", command );
+ this->command = strdup( command );
+ }
+ else
+ {
+ mlt_properties_set( properties, "resource", "ppm test" );
+ }
+
+ return producer;
+ }
+ free( this );
+ return NULL;
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ if ( mlt_properties_get_int( properties, "has_image" ) )
+ {
+ // Get the RGB image
+ uint8_t *rgb = mlt_properties_get_data( properties, "image", NULL );
+
+ // Get width and height
+ *width = mlt_properties_get_int( properties, "width" );
+ *height = mlt_properties_get_int( properties, "height" );
+
+ // Convert to requested format
+ if ( *format == mlt_image_yuv422 )
+ {
+ uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 2 );
+ mlt_convert_rgb24_to_yuv422( rgb, *width, *height, *width * 3, image );
+ mlt_properties_set_data( properties, "image", image, *width * ( *height + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ *buffer = image;
+ }
+ else if ( *format == mlt_image_rgb24 )
+ {
+ *buffer = rgb;
+ }
+ }
+ else
+ {
+ mlt_frame_get_image( this, buffer, format, width, height, writable );
+ }
+
+ return 0;
+}
+
+FILE *producer_ppm_run_video( producer_ppm this )
+{
+ if ( this->video == NULL )
+ {
+ if ( this->command == NULL )
+ {
+ this->video = popen( "image2raw -k -r 25 -ppm /usr/share/pixmaps/*.png", "r" );
+ }
+ else
+ {
+ char command[ 1024 ];
+ float fps = mlt_producer_get_fps( &this->parent );
+ float position = mlt_producer_position( &this->parent );
+ sprintf( command, "ffmpeg -i \"%s\" -ss %f -f imagepipe -r %f -img ppm - 2>/dev/null", this->command, position, fps );
+ this->video = popen( command, "r" );
+ }
+ }
+ return this->video;
+}
+
+FILE *producer_ppm_run_audio( producer_ppm this )
+{
+ if ( this->audio == NULL )
+ {
+ if ( this->command != NULL )
+ {
+ char command[ 1024 ];
+ float position = mlt_producer_position( &this->parent );
+ sprintf( command, "ffmpeg -i \"%s\" -ss %f -f s16le -ar 48000 -ac 2 - 2>/dev/null", this->command, position );
+ this->audio = popen( command, "r" );
+ }
+ }
+ return this->audio;
+}
+
+static void producer_ppm_position( producer_ppm this, uint64_t requested )
+{
+ if ( requested != this->expected )
+ {
+ if ( this->video != NULL )
+ pclose( this->video );
+ this->video = NULL;
+ if ( this->audio != NULL )
+ pclose( this->audio );
+ this->audio = NULL;
+ }
+
+ // This is the next frame we expect
+ this->expected = mlt_producer_frame( &this->parent ) + 1;
+
+ // Open the pipe
+ this->video = producer_ppm_run_video( this );
+
+ // Open the audio pipe
+ this->audio = producer_ppm_run_audio( this );
+
+}
+
+static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ FILE *pipe = mlt_properties_get_data( properties, "audio.pipe", NULL );
+
+ *frequency = 48000;
+ *channels = 2;
+ *samples = 1920;
+
+ // Size
+ int size = *samples * *channels * 2;
+
+ // Allocate an image
+ *buffer = malloc( size );
+
+ // Read it
+ if ( pipe != NULL )
+ fread( *buffer, size, 1, pipe );
+ else
+ memset( *buffer, 0, size );
+
+ // Pass the data on the frame properties
+ mlt_properties_set_data( properties, "audio", *buffer, size, free, NULL );
+
+ return 0;
+}
+
+static int read_ppm_header( FILE *video, int *width, int *height )
+{
+ int count = 0;
+ {
+ char temp[ 132 ];
+ fgets( temp, 132, video );
+ fgets( temp, 132, video );
+ count += sscanf( temp, "%d %d", width, height );
+ fgets( temp, 132, video );
+ }
+ return count;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ producer_ppm this = producer->child;
+ int width;
+ int height;
+
+ // Construct a test frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ // Are we at the position expected?
+ producer_ppm_position( this, mlt_producer_frame( producer ) );
+
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ FILE *video = this->video;
+ FILE *audio = this->audio;
+
+ // Read the video
+ if ( video != NULL && read_ppm_header( video, &width, &height ) == 2 )
+ {
+ // Allocate an image
+ uint8_t *image = mlt_pool_alloc( width * ( height + 1 ) * 3 );
+
+ // Read it
+ fread( image, width * height * 3, 1, video );
+
+ // Pass the data on the frame properties
+ mlt_properties_set_data( properties, "image", image, width * ( height + 1 ) * 3, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", width );
+ mlt_properties_set_int( properties, "height", height );
+ mlt_properties_set_int( properties, "has_image", 1 );
+ mlt_properties_set_int( properties, "progressive", 1 );
+ mlt_properties_set_double( properties, "aspect_ratio", 1 );
+
+ // Push the image callback
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+ else
+ {
+ // Push the image callback
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+
+ // Set the audio pipe
+ mlt_properties_set_data( properties, "audio.pipe", audio, 0, NULL, NULL );
+
+ // Hmm - register audio callback
+ mlt_frame_push_audio( *frame, producer_get_audio );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ producer_ppm this = parent->child;
+ if ( this->video )
+ pclose( this->video );
+ if ( this->audio )
+ pclose( this->audio );
+ free( this->command );
+ parent->close = NULL;
+ mlt_producer_close( parent );
+ free( this );
+}
--- /dev/null
+/*
+ * transition_composite.c -- compose one image over another using alpha channel
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "transition_composite.h"
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+typedef void ( *composite_line_fn )( uint8_t *dest, uint8_t *src, int width_src, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int softness );
+
+/** Geometry struct.
+*/
+
+struct geometry_s
+{
+ struct mlt_geometry_item_s item;
+ int nw; // normalised width
+ int nh; // normalised height
+ int sw; // scaled width, not including consumer scale based upon w/nw
+ int sh; // scaled height, not including consumer scale based upon h/nh
+ int halign; // horizontal alignment: 0=left, 1=center, 2=right
+ int valign; // vertical alignment: 0=top, 1=middle, 2=bottom
+ int x_src;
+ int y_src;
+};
+
+/** Parse the alignment properties into the geometry.
+*/
+
+static int alignment_parse( char* align )
+{
+ int ret = 0;
+
+ if ( align == NULL );
+ else if ( isdigit( align[ 0 ] ) )
+ ret = atoi( align );
+ else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
+ ret = 1;
+ else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' )
+ ret = 2;
+
+ return ret;
+}
+
+/** Calculate real geometry.
+*/
+
+static void geometry_calculate( mlt_transition this, struct geometry_s *output, double position )
+{
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ mlt_geometry geometry = mlt_properties_get_data( properties, "geometries", NULL );
+ int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
+ int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
+ int length = mlt_geometry_get_length( geometry );
+
+ // Allow wrapping
+ if ( !repeat_off && position >= length && length != 0 )
+ {
+ int section = position / length;
+ position -= section * length;
+ if ( !mirror_off && section % 2 == 1 )
+ position = length - position;
+ }
+
+ // Fetch the key for the position
+ mlt_geometry_fetch( geometry, &output->item, position );
+}
+
+static mlt_geometry transition_parse_keys( mlt_transition this, int normalised_width, int normalised_height )
+{
+ // Loop variable for property interrogation
+ int i = 0;
+
+ // Get the properties of the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // Create an empty geometries object
+ mlt_geometry geometry = mlt_geometry_init( );
+
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+ int length = out - in + 1;
+ double cycle = mlt_properties_get_double( properties, "cycle" );
+
+ // Get the new style geometry string
+ char *property = mlt_properties_get( properties, "geometry" );
+
+ // Allow a geometry repeat cycle
+ if ( cycle >= 1 )
+ length = cycle;
+ else if ( cycle > 0 )
+ length *= cycle;
+
+ // Parse the geometry if we have one
+ mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
+
+ // Check if we're using the old style geometry
+ if ( property == NULL )
+ {
+ // DEPRECATED: Multiple keys for geometry information is inefficient and too rigid for
+ // practical use - while deprecated, it has been slightly extended too - keys can now
+ // be specified out of order, and can be blanked or NULL to simulate removal
+
+ // Structure to use for parsing and inserting
+ struct mlt_geometry_item_s item;
+
+ // Parse the start property
+ item.frame = 0;
+ if ( mlt_geometry_parse_item( geometry, &item, mlt_properties_get( properties, "start" ) ) == 0 )
+ mlt_geometry_insert( geometry, &item );
+
+ // Parse the keys in between
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ {
+ // Get the name of the property
+ char *name = mlt_properties_get_name( properties, i );
+
+ // Check that it's valid
+ if ( !strncmp( name, "key[", 4 ) )
+ {
+ // Get the value of the property
+ char *value = mlt_properties_get_value( properties, i );
+
+ // Determine the frame number
+ item.frame = atoi( name + 4 );
+
+ // Parse and add to the list
+ if ( mlt_geometry_parse_item( geometry, &item, value ) == 0 )
+ mlt_geometry_insert( geometry, &item );
+ else
+ fprintf( stderr, "Invalid Key - skipping %s = %s\n", name, value );
+ }
+ }
+
+ // Parse the end
+ item.frame = -1;
+ if ( mlt_geometry_parse_item( geometry, &item, mlt_properties_get( properties, "end" ) ) == 0 )
+ mlt_geometry_insert( geometry, &item );
+ }
+
+ return geometry;
+}
+
+/** Adjust position according to scaled size and alignment properties.
+*/
+
+static void alignment_calculate( struct geometry_s *geometry )
+{
+ geometry->item.x += ( geometry->item.w - geometry->sw ) * geometry->halign / 2;
+ geometry->item.y += ( geometry->item.h - geometry->sh ) * geometry->valign / 2;
+}
+
+/** Calculate the position for this frame.
+*/
+
+static int position_calculate( mlt_transition this, mlt_position position )
+{
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+
+ // Now do the calcs
+ return position - in;
+}
+
+/** Calculate the field delta for this frame - position between two frames.
+*/
+
+static inline double delta_calculate( mlt_transition this, mlt_frame frame, mlt_position position )
+{
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+ double length = out - in + 1;
+
+ // Now do the calcs
+ double x = ( double )( position - in ) / length;
+ double y = ( double )( position + 1 - in ) / length;
+
+ return length * ( y - x ) / 2.0;
+}
+
+static int get_value( mlt_properties properties, const char *preferred, const char *fallback )
+{
+ int value = mlt_properties_get_int( properties, preferred );
+ if ( value == 0 )
+ value = mlt_properties_get_int( properties, fallback );
+ return value;
+}
+
+/** A linear threshold determination function.
+*/
+
+static inline int32_t linearstep( int32_t edge1, int32_t edge2, int32_t a )
+{
+ if ( a < edge1 )
+ return 0;
+
+ if ( a >= edge2 )
+ return 0x10000;
+
+ return ( ( a - edge1 ) << 16 ) / ( edge2 - edge1 );
+}
+
+/** A smoother, non-linear threshold determination function.
+*/
+
+static inline int32_t smoothstep( int32_t edge1, int32_t edge2, uint32_t a )
+{
+ if ( a < edge1 )
+ return 0;
+
+ if ( a >= edge2 )
+ return 0x10000;
+
+ a = ( ( a - edge1 ) << 16 ) / ( edge2 - edge1 );
+
+ return ( ( ( a * a ) >> 16 ) * ( ( 3 << 16 ) - ( 2 * a ) ) ) >> 16;
+}
+
+/** Load the luma map from PGM stream.
+*/
+
+static void luma_read_pgm( FILE *f, uint16_t **map, int *width, int *height )
+{
+ uint8_t *data = NULL;
+ while (1)
+ {
+ char line[128];
+ char comment[128];
+ int i = 2;
+ int maxval;
+ int bpp;
+ uint16_t *p;
+
+ line[127] = '\0';
+
+ // get the magic code
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ if ( line[0] != 'P' || line[1] != '5' )
+ break;
+
+ // skip white space and see if a new line must be fetched
+ for ( i = 2; i < 127 && line[i] != '\0' && isspace( line[i] ); i++ );
+ if ( ( line[i] == '\0' || line[i] == '#' ) && fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // get the dimensions
+ if ( line[0] == 'P' )
+ i = sscanf( line, "P5 %d %d %d", width, height, &maxval );
+ else
+ i = sscanf( line, "%d %d %d", width, height, &maxval );
+
+ // get the height value, if not yet
+ if ( i < 2 )
+ {
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ i = sscanf( line, "%d", height );
+ if ( i == 0 )
+ break;
+ else
+ i = 2;
+ }
+
+ // get the maximum gray value, if not yet
+ if ( i < 3 )
+ {
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ i = sscanf( line, "%d", &maxval );
+ if ( i == 0 )
+ break;
+ }
+
+ // determine if this is one or two bytes per pixel
+ bpp = maxval > 255 ? 2 : 1;
+
+ // allocate temporary storage for the raw data
+ data = mlt_pool_alloc( *width * *height * bpp );
+ if ( data == NULL )
+ break;
+
+ // read the raw data
+ if ( fread( data, *width * *height * bpp, 1, f ) != 1 )
+ break;
+
+ // allocate the luma bitmap
+ *map = p = (uint16_t*)mlt_pool_alloc( *width * *height * sizeof( uint16_t ) );
+ if ( *map == NULL )
+ break;
+
+ // proces the raw data into the luma bitmap
+ for ( i = 0; i < *width * *height * bpp; i += bpp )
+ {
+ if ( bpp == 1 )
+ *p++ = data[ i ] << 8;
+ else
+ *p++ = ( data[ i ] << 8 ) + data[ i + 1 ];
+ }
+
+ break;
+ }
+
+ if ( data != NULL )
+ mlt_pool_release( data );
+}
+
+/** Generate a luma map from any YUV image.
+*/
+
+static void luma_read_yuv422( uint8_t *image, uint16_t **map, int width, int height )
+{
+ int i;
+
+ // allocate the luma bitmap
+ uint16_t *p = *map = ( uint16_t* )mlt_pool_alloc( width * height * sizeof( uint16_t ) );
+ if ( *map == NULL )
+ return;
+
+ // proces the image data into the luma bitmap
+ for ( i = 0; i < width * height * 2; i += 2 )
+ *p++ = ( image[ i ] - 16 ) * 299; // 299 = 65535 / 219
+}
+
+static inline int calculate_mix( uint16_t *luma, int j, int soft, int weight, int alpha )
+{
+ return ( ( ( luma == NULL ) ? weight : smoothstep( luma[ j ], luma[ j ] + soft, weight + soft ) ) * alpha ) >> 8;
+}
+
+static inline uint8_t sample_mix( uint8_t dest, uint8_t src, int mix )
+{
+ return ( src * mix + dest * ( ( 1 << 16 ) - mix ) ) >> 16;
+}
+
+/** Composite a source line over a destination line
+*/
+
+static void composite_line_yuv( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+ register int j;
+ register int mix;
+
+ for ( j = 0; j < width; j ++ )
+ {
+ mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ );
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *alpha_a = ( mix >> 8 ) | *alpha_a;
+ alpha_a ++;
+ }
+}
+
+static void composite_line_yuv_or( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+ register int j;
+ register int mix;
+
+ for ( j = 0; j < width; j ++ )
+ {
+ mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ | *alpha_a );
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *alpha_a ++ = mix >> 8;
+ }
+}
+
+static void composite_line_yuv_and( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+ register int j;
+ register int mix;
+
+ for ( j = 0; j < width; j ++ )
+ {
+ mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ & *alpha_a );
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *alpha_a ++ = mix >> 8;
+ }
+}
+
+static void composite_line_yuv_xor( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+ register int j;
+ register int mix;
+
+ for ( j = 0; j < width; j ++ )
+ {
+ mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ ^ *alpha_a );
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *dest = sample_mix( *dest, *src++, mix );
+ dest++;
+ *alpha_a ++ = mix >> 8;
+ }
+}
+
+/** Composite function.
+*/
+
+static int composite_yuv( uint8_t *p_dest, int width_dest, int height_dest, uint8_t *p_src, int width_src, int height_src, uint8_t *alpha_b, uint8_t *alpha_a, struct geometry_s geometry, int field, uint16_t *p_luma, int32_t softness, composite_line_fn line_fn )
+{
+ int ret = 0;
+ int i;
+ int x_src = -geometry.x_src, y_src = -geometry.y_src;
+ int uneven_x_src = ( x_src % 2 );
+ int32_t weight = ( ( 1 << 16 ) - 1 ) * ( geometry.item.mix / 100 );
+ int step = ( field > -1 ) ? 2 : 1;
+ int bpp = 2;
+ int stride_src = geometry.sw * bpp;
+ int stride_dest = width_dest * bpp;
+
+ // Adjust to consumer scale
+ int x = rint( geometry.item.x * width_dest / geometry.nw );
+ int y = rint( geometry.item.y * height_dest / geometry.nh );
+ int uneven_x = ( x % 2 );
+
+ // optimization points - no work to do
+ if ( width_src <= 0 || height_src <= 0 || y_src >= height_src || x_src >= width_src )
+ return ret;
+
+ if ( ( x < 0 && -x >= width_src ) || ( y < 0 && -y >= height_src ) )
+ return ret;
+
+ // cropping affects the source width
+ if ( x_src > 0 )
+ {
+ width_src -= x_src;
+ // and it implies cropping
+ if ( width_src > geometry.item.w )
+ width_src = geometry.item.w;
+ }
+
+ // cropping affects the source height
+ if ( y_src > 0 )
+ {
+ height_src -= y_src;
+ // and it implies cropping
+ if ( height_src > geometry.item.h )
+ height_src = geometry.item.h;
+ }
+
+ // crop overlay off the left edge of frame
+ if ( x < 0 )
+ {
+ x_src = -x;
+ width_src -= x_src;
+ x = 0;
+ }
+
+ // crop overlay beyond right edge of frame
+ if ( x + width_src > width_dest )
+ width_src = width_dest - x;
+
+ // crop overlay off the top edge of the frame
+ if ( y < 0 )
+ {
+ y_src = -y;
+ height_src -= y_src;
+ y = 0;
+ }
+
+ // crop overlay below bottom edge of frame
+ if ( y + height_src > height_dest )
+ height_src = height_dest - y;
+
+ // offset pointer into overlay buffer based on cropping
+ p_src += x_src * bpp + y_src * stride_src;
+
+ // offset pointer into frame buffer based upon positive coordinates only!
+ p_dest += ( x < 0 ? 0 : x ) * bpp + ( y < 0 ? 0 : y ) * stride_dest;
+
+ // offset pointer into alpha channel based upon cropping
+ alpha_b += x_src + y_src * stride_src / bpp;
+ alpha_a += x + y * stride_dest / bpp;
+
+ // offset pointer into luma channel based upon cropping
+ if ( p_luma )
+ p_luma += x_src + y_src * stride_src / bpp;
+
+ // Assuming lower field first
+ // Special care is taken to make sure the b_frame is aligned to the correct field.
+ // field 0 = lower field and y should be odd (y is 0-based).
+ // field 1 = upper field and y should be even.
+ if ( ( field > -1 ) && ( y % 2 == field ) )
+ {
+ if ( ( field == 1 && y < height_dest - 1 ) || ( field == 0 && y == 0 ) )
+ p_dest += stride_dest;
+ else
+ p_dest -= stride_dest;
+ }
+
+ // On the second field, use the other lines from b_frame
+ if ( field == 1 )
+ {
+ p_src += stride_src;
+ alpha_b += stride_src / bpp;
+ alpha_a += stride_dest / bpp;
+ height_src--;
+ }
+
+ stride_src *= step;
+ stride_dest *= step;
+ int alpha_b_stride = stride_src / bpp;
+ int alpha_a_stride = stride_dest / bpp;
+
+ // Align chroma of source and destination
+ if ( uneven_x != uneven_x_src )
+ {
+ p_src += 2;
+ width_src -= 2;
+ alpha_b += 1;
+ }
+
+ // now do the compositing only to cropped extents
+ for ( i = 0; i < height_src; i += step )
+ {
+ line_fn( p_dest, p_src, width_src, alpha_b, alpha_a, weight, p_luma, softness );
+
+ p_src += stride_src;
+ p_dest += stride_dest;
+ alpha_b += alpha_b_stride;
+ alpha_a += alpha_a_stride;
+ if ( p_luma )
+ p_luma += alpha_b_stride;
+ }
+
+ return ret;
+}
+
+
+/** Scale 16bit greyscale luma map using nearest neighbor.
+*/
+
+static inline void
+scale_luma ( uint16_t *dest_buf, int dest_width, int dest_height, const uint16_t *src_buf, int src_width, int src_height, int invert )
+{
+ register int i, j;
+ register int x_step = ( src_width << 16 ) / dest_width;
+ register int y_step = ( src_height << 16 ) / dest_height;
+ register int x, y = 0;
+
+ for ( i = 0; i < dest_height; i++ )
+ {
+ const uint16_t *src = src_buf + ( y >> 16 ) * src_width;
+ x = 0;
+
+ for ( j = 0; j < dest_width; j++ )
+ {
+ *dest_buf++ = src[ x >> 16 ] ^ invert;
+ x += x_step;
+ }
+ y += y_step;
+ }
+}
+
+static uint16_t* get_luma( mlt_transition this, mlt_properties properties, int width, int height )
+{
+ // The cached luma map information
+ int luma_width = mlt_properties_get_int( properties, "_luma.width" );
+ int luma_height = mlt_properties_get_int( properties, "_luma.height" );
+ uint16_t *luma_bitmap = mlt_properties_get_data( properties, "_luma.bitmap", NULL );
+ int invert = mlt_properties_get_int( properties, "luma_invert" );
+
+ // If the filename property changed, reload the map
+ char *resource = mlt_properties_get( properties, "luma" );
+
+ char temp[ 512 ];
+
+ if ( luma_width == 0 || luma_height == 0 )
+ {
+ luma_width = width;
+ luma_height = height;
+ }
+
+ if ( resource && resource[0] && strchr( resource, '%' ) )
+ {
+ // TODO: Clean up quick and dirty compressed/existence check
+ FILE *test;
+ sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 );
+ test = fopen( temp, "r" );
+ if ( test == NULL )
+ strcat( temp, ".png" );
+ else
+ fclose( test );
+ resource = temp;
+ }
+
+ if ( resource && resource[0] )
+ {
+ char *old_luma = mlt_properties_get( properties, "_luma" );
+ int old_invert = mlt_properties_get_int( properties, "_luma_invert" );
+
+ if ( invert != old_invert || ( old_luma && old_luma[0] && strcmp( resource, old_luma ) ) )
+ {
+ mlt_properties_set_data( properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL );
+ luma_bitmap = NULL;
+ }
+ }
+ else {
+ char *old_luma = mlt_properties_get( properties, "_luma" );
+ if ( old_luma && old_luma[0] )
+ {
+ mlt_properties_set_data( properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL );
+ luma_bitmap = NULL;
+ mlt_properties_set( properties, "_luma", NULL);
+ }
+ }
+
+ if ( resource && resource[0] && ( luma_bitmap == NULL || luma_width != width || luma_height != height ) )
+ {
+ uint16_t *orig_bitmap = mlt_properties_get_data( properties, "_luma.orig_bitmap", NULL );
+ luma_width = mlt_properties_get_int( properties, "_luma.orig_width" );
+ luma_height = mlt_properties_get_int( properties, "_luma.orig_height" );
+
+ // Load the original luma once
+ if ( orig_bitmap == NULL )
+ {
+ char *extension = strrchr( resource, '.' );
+
+ // See if it is a PGM
+ if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 )
+ {
+ // Open PGM
+ FILE *f = fopen( resource, "r" );
+ if ( f != NULL )
+ {
+ // Load from PGM
+ luma_read_pgm( f, &orig_bitmap, &luma_width, &luma_height );
+ fclose( f );
+
+ // Remember the original size for subsequent scaling
+ mlt_properties_set_data( properties, "_luma.orig_bitmap", orig_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "_luma.orig_width", luma_width );
+ mlt_properties_set_int( properties, "_luma.orig_height", luma_height );
+ }
+ }
+ else
+ {
+ // Get the factory producer service
+ char *factory = mlt_properties_get( properties, "factory" );
+
+ // Create the producer
+ mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+ mlt_producer producer = mlt_factory_producer( profile, factory, resource );
+
+ // If we have one
+ if ( producer != NULL )
+ {
+ // Get the producer properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Ensure that we loop
+ mlt_properties_set( producer_properties, "eof", "loop" );
+
+ // Now pass all producer. properties on the transition down
+ mlt_properties_pass( producer_properties, properties, "luma." );
+
+ // We will get the alpha frame from the producer
+ mlt_frame luma_frame = NULL;
+
+ // Get the luma frame
+ if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 )
+ {
+ uint8_t *luma_image;
+ mlt_image_format luma_format = mlt_image_yuv422;
+
+ // Get image from the luma producer
+ mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "none" );
+ mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 );
+
+ // Generate the luma map
+ if ( luma_image != NULL && luma_format == mlt_image_yuv422 )
+ luma_read_yuv422( luma_image, &orig_bitmap, luma_width, luma_height );
+
+ // Remember the original size for subsequent scaling
+ mlt_properties_set_data( properties, "_luma.orig_bitmap", orig_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "_luma.orig_width", luma_width );
+ mlt_properties_set_int( properties, "_luma.orig_height", luma_height );
+
+ // Cleanup the luma frame
+ mlt_frame_close( luma_frame );
+ }
+
+ // Cleanup the luma producer
+ mlt_producer_close( producer );
+ }
+ }
+ }
+ // Scale luma map
+ luma_bitmap = mlt_pool_alloc( width * height * sizeof( uint16_t ) );
+ scale_luma( luma_bitmap, width, height, orig_bitmap, luma_width, luma_height, invert * ( ( 1 << 16 ) - 1 ) );
+
+ // Remember the scaled luma size to prevent unnecessary scaling
+ mlt_properties_set_int( properties, "_luma.width", width );
+ mlt_properties_set_int( properties, "_luma.height", height );
+ mlt_properties_set_data( properties, "_luma.bitmap", luma_bitmap, width * height * 2, mlt_pool_release, NULL );
+ mlt_properties_set( properties, "_luma", resource );
+ mlt_properties_set_int( properties, "_luma_invert", invert );
+ }
+ return luma_bitmap;
+}
+
+/** Get the properly sized image from b_frame.
+*/
+
+static int get_b_frame_image( mlt_transition this, mlt_frame b_frame, uint8_t **image, int *width, int *height, struct geometry_s *geometry )
+{
+ int ret = 0;
+ mlt_image_format format = mlt_image_yuv422;
+
+ // Get the properties objects
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ uint8_t resize_alpha = mlt_properties_get_int( b_props, "resize_alpha" );
+
+ // Do not scale if we are cropping - the compositing rectangle can crop the b image
+ // TODO: Use the animatable w and h of the crop geometry to scale independently of crop rectangle
+ if ( mlt_properties_get( properties, "crop" ) )
+ {
+ int real_width = get_value( b_props, "real_width", "width" );
+ int real_height = get_value( b_props, "real_height", "height" );
+ double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" );
+ double consumer_ar = mlt_properties_get_double( b_props, "consumer_aspect_ratio" );
+ double background_ar = mlt_properties_get_double( b_props, "output_ratio" );
+ double output_ar = background_ar != 0.0 ? background_ar : consumer_ar;
+ int scaled_width = rint( ( input_ar == 0.0 ? output_ar : input_ar ) / output_ar * real_width );
+ int scaled_height = real_height;
+ geometry->sw = scaled_width;
+ geometry->sh = scaled_height;
+ }
+ // Normalise aspect ratios and scale preserving aspect ratio
+ else if ( mlt_properties_get_int( properties, "aligned" ) && mlt_properties_get_int( properties, "distort" ) == 0 && mlt_properties_get_int( b_props, "distort" ) == 0 && geometry->item.distort == 0 )
+ {
+ // Adjust b_frame pixel aspect
+ int normalised_width = geometry->item.w;
+ int normalised_height = geometry->item.h;
+ int real_width = get_value( b_props, "real_width", "width" );
+ int real_height = get_value( b_props, "real_height", "height" );
+ double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" );
+ double consumer_ar = mlt_properties_get_double( b_props, "consumer_aspect_ratio" );
+ double background_ar = mlt_properties_get_double( b_props, "output_ratio" );
+ double output_ar = background_ar != 0.0 ? background_ar : consumer_ar;
+ int scaled_width = rint( ( input_ar == 0.0 ? output_ar : input_ar ) / output_ar * real_width );
+ int scaled_height = real_height;
+// fprintf(stderr, "%s: scaled %dx%d norm %dx%d real %dx%d output_ar %f => %f\n", __FILE__,
+// scaled_width, scaled_height, normalised_width, normalised_height, real_width, real_height,
+// background_ar, output_ar);
+
+ // Now ensure that our images fit in the normalised frame
+ if ( scaled_width > normalised_width )
+ {
+ scaled_height = rint( scaled_height * normalised_width / scaled_width );
+ scaled_width = normalised_width;
+ }
+ if ( scaled_height > normalised_height )
+ {
+ scaled_width = rint( scaled_width * normalised_height / scaled_height );
+ scaled_height = normalised_height;
+ }
+
+ // Honour the fill request - this will scale the image to fill width or height while maintaining a/r
+ // ????: Shouln't this be the default behaviour?
+ if ( mlt_properties_get_int( properties, "fill" ) && scaled_width > 0 && scaled_height > 0 )
+ {
+ if ( scaled_height < normalised_height && scaled_width * normalised_height / scaled_height <= normalised_width )
+ {
+ scaled_width = rint( scaled_width * normalised_height / scaled_height );
+ scaled_height = normalised_height;
+ }
+ else if ( scaled_width < normalised_width && scaled_height * normalised_width / scaled_width < normalised_height )
+ {
+ scaled_height = rint( scaled_height * normalised_width / scaled_width );
+ scaled_width = normalised_width;
+ }
+ }
+
+ // Save the new scaled dimensions
+ geometry->sw = scaled_width;
+ geometry->sh = scaled_height;
+ }
+ else
+ {
+ geometry->sw = geometry->item.w;
+ geometry->sh = geometry->item.h;
+ }
+
+ // We want to ensure that we bypass resize now...
+ if ( resize_alpha == 0 )
+ mlt_properties_set_int( b_props, "distort", mlt_properties_get_int( properties, "distort" ) );
+
+ // If we're not aligned, we want a non-transparent background
+ if ( mlt_properties_get_int( properties, "aligned" ) == 0 )
+ mlt_properties_set_int( b_props, "resize_alpha", 255 );
+
+ // Take into consideration alignment for optimisation (titles are a special case)
+ if ( !mlt_properties_get_int( properties, "titles" ) &&
+ mlt_properties_get( properties, "crop" ) == NULL )
+ alignment_calculate( geometry );
+
+ // Adjust to consumer scale
+ *width = rint( geometry->sw * *width / geometry->nw );
+ *height = rint( geometry->sh * *height / geometry->nh );
+// fprintf(stderr, "%s: scaled %dx%d norm %dx%d resize %dx%d\n", __FILE__,
+// geometry->sw, geometry->sh, geometry->nw, geometry->nh, *width, *height);
+
+ ret = mlt_frame_get_image( b_frame, image, &format, width, height, 1 );
+
+ // Set the frame back
+ mlt_properties_set_int( b_props, "resize_alpha", resize_alpha );
+
+ return ret && image != NULL;
+}
+
+static void crop_calculate( mlt_transition this, mlt_properties properties, struct geometry_s *result, double position )
+{
+ // Initialize panning info
+ result->x_src = 0;
+ result->y_src = 0;
+ if ( mlt_properties_get( properties, "crop" ) )
+ {
+ mlt_geometry crop = mlt_properties_get_data( properties, "crop_geometry", NULL );
+ if ( !crop )
+ {
+ crop = mlt_geometry_init();
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+ int length = out - in + 1;
+ double cycle = mlt_properties_get_double( properties, "cycle" );
+
+ // Allow a geometry repeat cycle
+ if ( cycle >= 1 )
+ length = cycle;
+ else if ( cycle > 0 )
+ length *= cycle;
+ mlt_geometry_parse( crop, mlt_properties_get( properties, "crop" ), length, result->sw, result->sh );
+ mlt_properties_set_data( properties, "crop_geometry", crop, 0, (mlt_destructor)mlt_geometry_close, NULL );
+ }
+
+ // Repeat processing
+ int length = mlt_geometry_get_length( crop );
+ int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
+ int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
+ if ( !repeat_off && position >= length && length != 0 )
+ {
+ int section = position / length;
+ position -= section * length;
+ if ( !mirror_off && section % 2 == 1 )
+ position = length - position;
+ }
+
+ // Compute the pan
+ struct mlt_geometry_item_s crop_item;
+ mlt_geometry_fetch( crop, &crop_item, position );
+ result->x_src = rint( crop_item.x );
+ result->y_src = rint( crop_item.y );
+ }
+}
+
+static mlt_geometry composite_calculate( mlt_transition this, struct geometry_s *result, mlt_frame a_frame, double position )
+{
+ // Get the properties from the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // Get the properties from the frame
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+ // Structures for geometry
+ mlt_geometry start = mlt_properties_get_data( properties, "geometries", NULL );
+
+ // Obtain the normalised width and height from the a_frame
+ int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
+ int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
+
+ char *name = mlt_properties_get( properties, "_unique_id" );
+ char key[ 256 ];
+
+ sprintf( key, "%s.in", name );
+ if ( mlt_properties_get( a_props, key ) )
+ {
+ sscanf( mlt_properties_get( a_props, key ), "%f,%f,%f,%f,%f,%d,%d", &result->item.x, &result->item.y, &result->item.w, &result->item.h, &result->item.mix, &result->nw, &result->nh );
+ }
+ else
+ {
+ // Now parse the geometries
+ if ( start == NULL )
+ {
+ // Parse the transitions properties
+ start = transition_parse_keys( this, normalised_width, normalised_height );
+
+ // Assign to properties to ensure we get destroyed
+ mlt_properties_set_data( properties, "geometries", start, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+ }
+ else
+ {
+ int length = mlt_transition_get_out( this ) - mlt_transition_get_in( this ) + 1;
+ double cycle = mlt_properties_get_double( properties, "cycle" );
+ if ( cycle > 1 )
+ length = cycle;
+ else if ( cycle > 0 )
+ length *= cycle;
+ mlt_geometry_refresh( start, mlt_properties_get( properties, "geometry" ), length, normalised_width, normalised_height );
+ }
+
+ // Do the calculation
+ geometry_calculate( this, result, position );
+
+ // Assign normalised info
+ result->nw = normalised_width;
+ result->nh = normalised_height;
+ }
+
+ // Now parse the alignment
+ result->halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
+ result->valign = alignment_parse( mlt_properties_get( properties, "valign" ) );
+
+ crop_calculate( this, properties, result, position );
+
+ return start;
+}
+
+mlt_frame composite_copy_region( mlt_transition this, mlt_frame a_frame, mlt_position frame_position )
+{
+ // Create a frame to return
+ mlt_frame b_frame = mlt_frame_init( MLT_TRANSITION_SERVICE( this ) );
+
+ // Get the properties of the a frame
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+ // Get the properties of the b frame
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ // Get the position
+ int position = position_calculate( this, frame_position );
+
+ // Get the unique id of the transition
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
+ char key[ 256 ];
+
+ // Destination image
+ uint8_t *dest = NULL;
+
+ // Get the image and dimensions
+ uint8_t *image = mlt_properties_get_data( a_props, "image", NULL );
+ int width = mlt_properties_get_int( a_props, "width" );
+ int height = mlt_properties_get_int( a_props, "height" );
+ int format = mlt_properties_get_int( a_props, "format" );
+
+ // Pointers for copy operation
+ uint8_t *p;
+
+ // Coordinates
+ int w = 0;
+ int h = 0;
+ int x = 0;
+ int y = 0;
+
+ int ss = 0;
+ int ds = 0;
+
+ // Will need to know region to copy
+ struct geometry_s result;
+
+ // Calculate the region now
+ composite_calculate( this, &result, a_frame, position );
+
+ // Need to scale down to actual dimensions
+ x = rint( result.item.x * width / result.nw );
+ y = rint( result.item.y * height / result.nh );
+ w = rint( result.item.w * width / result.nw );
+ h = rint( result.item.h * height / result.nh );
+
+ if ( x % 2 )
+ {
+ x --;
+ w ++;
+ }
+
+ // Store the key
+ sprintf( key, "%s.in=%d,%d,%d,%d,%f,%d,%d", name, x, y, w, h, result.item.mix, width, height );
+ mlt_properties_parse( a_props, key );
+ sprintf( key, "%s.out=%d,%d,%d,%d,%f,%d,%d", name, x, y, w, h, result.item.mix, width, height );
+ mlt_properties_parse( a_props, key );
+
+ ds = w * 2;
+ ss = width * 2;
+
+ // Now we need to create a new destination image
+ dest = mlt_pool_alloc( w * h * 2 );
+
+ // Assign to the new frame
+ mlt_properties_set_data( b_props, "image", dest, w * h * 2, mlt_pool_release, NULL );
+ mlt_properties_set_int( b_props, "width", w );
+ mlt_properties_set_int( b_props, "height", h );
+ mlt_properties_set_int( b_props, "format", format );
+
+ if ( y < 0 )
+ {
+ dest += ( ds * -y );
+ h += y;
+ y = 0;
+ }
+
+ if ( y + h > height )
+ h -= ( y + h - height );
+
+ if ( x < 0 )
+ {
+ dest += -x * 2;
+ w += x;
+ x = 0;
+ }
+
+ if ( w > 0 && h > 0 )
+ {
+ // Copy the region of the image
+ p = image + y * ss + x * 2;
+
+ while ( h -- )
+ {
+ memcpy( dest, p, w * 2 );
+ dest += ds;
+ p += ss;
+ }
+ }
+
+ // Assign this position to the b frame
+ mlt_frame_set_position( b_frame, frame_position );
+ mlt_properties_set_int( b_props, "distort", 1 );
+
+ // Return the frame
+ return b_frame;
+}
+
+/** Get the image.
+*/
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the b frame from the stack
+ mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+
+ // Get the transition from the a frame
+ mlt_transition this = mlt_frame_pop_service( a_frame );
+
+ // Get in and out
+ double position = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( a_frame ) );
+ int out = mlt_frame_pop_service_int( a_frame );
+ int in = mlt_frame_pop_service_int( a_frame );
+
+ // Get the properties from the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // TODO: clean up always_active behaviour
+ if ( mlt_properties_get_int( properties, "always_active" ) )
+ {
+ mlt_events_block( properties, properties );
+ mlt_properties_set_int( properties, "in", in );
+ mlt_properties_set_int( properties, "out", out );
+ mlt_events_unblock( properties, properties );
+ }
+
+ // This compositer is yuv422 only
+ *format = mlt_image_yuv422;
+
+ if ( b_frame != NULL )
+ {
+ // Get the properties of the a frame
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+ // Get the properties of the b frame
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ // Structures for geometry
+ struct geometry_s result;
+
+ // Calculate the position
+ double delta = delta_calculate( this, a_frame, position );
+
+ // Get the image from the b frame
+ uint8_t *image_b = NULL;
+ int width_b = *width;
+ int height_b = *height;
+
+ // Vars for alphas
+ uint8_t *alpha_a = NULL;
+ uint8_t *alpha_b = NULL;
+
+ // Composites always need scaling... defaulting to lowest
+ const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
+ if ( rescale == NULL || !strcmp( rescale, "none" ) )
+ rescale = "nearest";
+ mlt_properties_set( a_props, "rescale.interp", rescale );
+ mlt_properties_set( b_props, "rescale.interp", rescale );
+
+ // Do the calculation
+ // NB: Locks needed here since the properties are being modified
+ mlt_service_lock( MLT_TRANSITION_SERVICE( this ) );
+ composite_calculate( this, &result, a_frame, position );
+ mlt_service_unlock( MLT_TRANSITION_SERVICE( this ) );
+
+ // Since we are the consumer of the b_frame, we must pass along these
+ // consumer properties from the a_frame
+ mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "deinterlace" ) );
+ mlt_properties_set( b_props, "consumer_deinterlace_method", mlt_properties_get( a_props, "consumer_deinterlace_method" ) );
+ mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+ // TODO: Dangerous/temporary optimisation - if nothing to do, then do nothing
+ if ( mlt_properties_get_int( properties, "no_alpha" ) &&
+ result.item.x == 0 && result.item.y == 0 && result.item.w == *width && result.item.h == *height && result.item.mix == 100 )
+ {
+ mlt_frame_get_image( b_frame, image, format, width, height, 1 );
+ if ( !mlt_frame_is_test_card( a_frame ) )
+ mlt_frame_replace_image( a_frame, *image, *format, *width, *height );
+ return 0;
+ }
+
+ if ( a_frame == b_frame )
+ {
+ double aspect_ratio = mlt_frame_get_aspect_ratio( b_frame );
+ get_b_frame_image( this, b_frame, &image_b, &width_b, &height_b, &result );
+ alpha_b = mlt_frame_get_alpha_mask( b_frame );
+ mlt_properties_set_double( a_props, "aspect_ratio", aspect_ratio );
+ }
+
+ // Get the image from the a frame
+ mlt_frame_get_image( a_frame, image, format, width, height, 1 );
+ alpha_a = mlt_frame_get_alpha_mask( a_frame );
+
+ // Optimisation - no compositing required
+ if ( result.item.mix == 0 || ( result.item.w == 0 && result.item.h == 0 ) )
+ return 0;
+
+ // Need to keep the width/height of the a_frame on the b_frame for titling
+ if ( mlt_properties_get( a_props, "dest_width" ) == NULL )
+ {
+ mlt_properties_set_int( a_props, "dest_width", *width );
+ mlt_properties_set_int( a_props, "dest_height", *height );
+ mlt_properties_set_int( b_props, "dest_width", *width );
+ mlt_properties_set_int( b_props, "dest_height", *height );
+ }
+ else
+ {
+ mlt_properties_set_int( b_props, "dest_width", mlt_properties_get_int( a_props, "dest_width" ) );
+ mlt_properties_set_int( b_props, "dest_height", mlt_properties_get_int( a_props, "dest_height" ) );
+ }
+
+ // Special case for titling...
+ if ( mlt_properties_get_int( properties, "titles" ) )
+ {
+ if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL )
+ mlt_properties_set( b_props, "rescale.interp", "hyper" );
+ width_b = mlt_properties_get_int( a_props, "dest_width" );
+ height_b = mlt_properties_get_int( a_props, "dest_height" );
+ }
+
+ if ( *image != image_b && ( image_b != NULL || get_b_frame_image( this, b_frame, &image_b, &width_b, &height_b, &result ) == 0 ) )
+ {
+ uint8_t *dest = *image;
+ uint8_t *src = image_b;
+ int progressive =
+ mlt_properties_get_int( a_props, "consumer_deinterlace" ) ||
+ mlt_properties_get_int( properties, "progressive" );
+ int field;
+
+ int32_t luma_softness = mlt_properties_get_double( properties, "softness" ) * ( 1 << 16 );
+ uint16_t *luma_bitmap = get_luma( this, properties, width_b, height_b );
+ char *operator = mlt_properties_get( properties, "operator" );
+
+ alpha_b = alpha_b == NULL ? mlt_frame_get_alpha_mask( b_frame ) : alpha_b;
+
+ composite_line_fn line_fn = composite_line_yuv;
+
+ // Replacement and override
+ if ( operator != NULL )
+ {
+ if ( !strcmp( operator, "or" ) )
+ line_fn = composite_line_yuv_or;
+ if ( !strcmp( operator, "and" ) )
+ line_fn = composite_line_yuv_and;
+ if ( !strcmp( operator, "xor" ) )
+ line_fn = composite_line_yuv_xor;
+ }
+
+ // Allow the user to completely obliterate the alpha channels from both frames
+ if ( mlt_properties_get( properties, "alpha_a" ) )
+ memset( alpha_a, mlt_properties_get_int( properties, "alpha_a" ), *width * *height );
+
+ if ( mlt_properties_get( properties, "alpha_b" ) )
+ memset( alpha_b, mlt_properties_get_int( properties, "alpha_b" ), width_b * height_b );
+
+ for ( field = 0; field < ( progressive ? 1 : 2 ); field++ )
+ {
+ // Assume lower field (0) first
+ double field_position = position + field * delta;
+
+ // Do the calculation if we need to
+ // NB: Locks needed here since the properties are being modified
+ mlt_service_lock( MLT_TRANSITION_SERVICE( this ) );
+ composite_calculate( this, &result, a_frame, field_position );
+ mlt_service_unlock( MLT_TRANSITION_SERVICE( this ) );
+
+ if ( mlt_properties_get_int( properties, "titles" ) )
+ {
+ result.item.w = rint( *width * ( result.item.w / result.nw ) );
+ result.nw = result.item.w;
+ result.item.h = rint( *height * ( result.item.h / result.nh ) );
+ result.nh = *height;
+ result.sw = width_b;
+ result.sh = height_b;
+ }
+
+ // Enforce cropping
+ if ( mlt_properties_get( properties, "crop" ) )
+ {
+ if ( result.x_src == 0 )
+ width_b = width_b > result.item.w ? result.item.w : width_b;
+ if ( result.y_src == 0 )
+ height_b = height_b > result.item.h ? result.item.h : height_b;
+ }
+ else
+ {
+ // Otherwise, align
+ alignment_calculate( &result );
+ }
+
+ // Composite the b_frame on the a_frame
+ composite_yuv( dest, *width, *height, src, width_b, height_b, alpha_b, alpha_a, result, progressive ? -1 : field, luma_bitmap, luma_softness, line_fn );
+ }
+ }
+ }
+ else
+ {
+ mlt_frame_get_image( a_frame, image, format, width, height, 1 );
+ }
+
+ return 0;
+}
+
+/** Composition transition processing.
+*/
+
+static mlt_frame composite_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+ // UGH - this is a TODO - find a more reliable means of obtaining in/out for the always_active case
+ if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "always_active" ) == 0 )
+ {
+ mlt_frame_push_service_int( a_frame, mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "in" ) );
+ mlt_frame_push_service_int( a_frame, mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "out" ) );
+ mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( a_frame ), position_calculate( this, mlt_frame_get_position( a_frame ) ) );
+ }
+ else
+ {
+ mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL );
+ mlt_frame_push_service_int( a_frame, mlt_properties_get_int( props, "in" ) );
+ mlt_frame_push_service_int( a_frame, mlt_properties_get_int( props, "out" ) );
+ mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( a_frame ), mlt_properties_get_int( props, "_frame" ) - mlt_properties_get_int( props, "in" ) );
+ }
+
+ mlt_frame_push_service( a_frame, this );
+ mlt_frame_push_frame( a_frame, b_frame );
+ mlt_frame_push_get_image( a_frame, transition_get_image );
+ return a_frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_transition transition_composite_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_transition this = calloc( sizeof( struct mlt_transition_s ), 1 );
+ if ( this != NULL && mlt_transition_init( this, NULL ) == 0 )
+ {
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ this->process = composite_process;
+
+ // Default starting motion and zoom
+ mlt_properties_set( properties, "start", arg != NULL ? arg : "0,0:100%x100%" );
+
+ // Default factory
+ mlt_properties_set( properties, "factory", "fezzik" );
+
+ // Use alignment (and hence alpha of b frame)
+ mlt_properties_set_int( properties, "aligned", 1 );
+
+ // Inform apps and framework that this is a video only transition
+ mlt_properties_set_int( properties, "_transition_type", 1 );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * transition_composite.h -- compose one image over another using alpha channel
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TRANSITION_COMPOSITE_H_
+#define _TRANSITION_COMPOSITE_H_
+
+#include <framework/mlt_transition.h>
+
+extern mlt_transition transition_composite_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+// Courtesy functionality - allows regionalised filtering
+extern mlt_frame composite_copy_region( mlt_transition, mlt_frame, mlt_position );
+
+#endif
--- /dev/null
+/*
+ * transition_luma.c -- a generic dissolve/wipe processor
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * Adapted from Kino Plugin Timfx, which is
+ * Copyright (C) 2002 Timothy M. Shead <tshead@k-3d.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+/** Calculate the position for this frame.
+*/
+
+static float position_calculate( mlt_transition this, mlt_frame frame )
+{
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+
+ // Get the position of the frame
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
+ mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
+
+ // Now do the calcs
+ return ( float )( position - in ) / ( float )( out - in + 1 );
+}
+
+/** Calculate the field delta for this frame - position between two frames.
+*/
+
+static float delta_calculate( mlt_transition this, mlt_frame frame )
+{
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+
+ // Get the position of the frame
+ mlt_position position = mlt_frame_get_position( frame );
+
+ // Now do the calcs
+ float x = ( float )( position - in ) / ( float )( out - in + 1 );
+ float y = ( float )( position + 1 - in ) / ( float )( out - in + 1 );
+
+ return ( y - x ) / 2.0;
+}
+
+static inline int dissolve_yuv( mlt_frame this, mlt_frame that, float weight, int width, int height )
+{
+ int ret = 0;
+ int width_src = width, height_src = height;
+ mlt_image_format format = mlt_image_yuv422;
+ uint8_t *p_src, *p_dest;
+ uint8_t *p, *q;
+ uint8_t *limit;
+ uint8_t *alpha_src;
+ uint8_t *alpha_dst;
+
+ int32_t weigh = weight * ( 1 << 16 );
+ int32_t weigh_complement = ( 1 - weight ) * ( 1 << 16 );
+
+ if ( mlt_properties_get( &this->parent, "distort" ) )
+ mlt_properties_set( &that->parent, "distort", mlt_properties_get( &this->parent, "distort" ) );
+ mlt_properties_set_int( &that->parent, "consumer_deinterlace", mlt_properties_get_int( &this->parent, "consumer_deinterlace" ) );
+ mlt_frame_get_image( this, &p_dest, &format, &width, &height, 1 );
+ alpha_dst = mlt_frame_get_alpha_mask( this );
+ mlt_frame_get_image( that, &p_src, &format, &width_src, &height_src, 0 );
+ alpha_src = mlt_frame_get_alpha_mask( that );
+
+ // Pick the lesser of two evils ;-)
+ width_src = width_src > width ? width : width_src;
+ height_src = height_src > height ? height : height_src;
+
+ p = p_dest;
+ q = alpha_dst;
+ limit = p_dest + height_src * width_src * 2;
+
+ while ( p < limit )
+ {
+ *p_dest++ = ( *p_src++ * weigh + *p++ * weigh_complement ) >> 16;
+ *p_dest++ = ( *p_src++ * weigh + *p++ * weigh_complement ) >> 16;
+ *alpha_dst++ = ( *alpha_src++ * weigh + *q++ * weigh_complement ) >> 16;
+ }
+
+ return ret;
+}
+
+// image processing functions
+
+static inline int32_t smoothstep( int32_t edge1, int32_t edge2, uint32_t a )
+{
+ if ( a < edge1 )
+ return 0;
+
+ if ( a >= edge2 )
+ return 0x10000;
+
+ a = ( ( a - edge1 ) << 16 ) / ( edge2 - edge1 );
+
+ return ( ( ( a * a ) >> 16 ) * ( ( 3 << 16 ) - ( 2 * a ) ) ) >> 16;
+}
+
+/** powerful stuff
+
+ \param field_order -1 = progressive, 0 = lower field first, 1 = top field first
+*/
+static void luma_composite( mlt_frame a_frame, mlt_frame b_frame, int luma_width, int luma_height,
+ uint16_t *luma_bitmap, float pos, float frame_delta, float softness, int field_order,
+ int *width, int *height )
+{
+ int width_src = *width, height_src = *height;
+ int width_dest = *width, height_dest = *height;
+ mlt_image_format format_src = mlt_image_yuv422, format_dest = mlt_image_yuv422;
+ uint8_t *p_src, *p_dest;
+ int i, j;
+ int stride_src;
+ int stride_dest;
+ uint16_t weight = 0;
+
+ format_src = mlt_image_yuv422;
+ format_dest = mlt_image_yuv422;
+
+ if ( mlt_properties_get( &a_frame->parent, "distort" ) )
+ mlt_properties_set( &b_frame->parent, "distort", mlt_properties_get( &a_frame->parent, "distort" ) );
+ mlt_properties_set_int( &b_frame->parent, "consumer_deinterlace", mlt_properties_get_int( &a_frame->parent, "consumer_deinterlace" ) );
+ mlt_frame_get_image( a_frame, &p_dest, &format_dest, &width_dest, &height_dest, 1 );
+ mlt_frame_get_image( b_frame, &p_src, &format_src, &width_src, &height_src, 0 );
+
+ if ( *width == 0 || *height == 0 )
+ return;
+
+ // Pick the lesser of two evils ;-)
+ width_src = width_src > width_dest ? width_dest : width_src;
+ height_src = height_src > height_dest ? height_dest : height_src;
+
+ stride_src = width_src * 2;
+ stride_dest = width_dest * 2;
+
+ // Offset the position based on which field we're looking at ...
+ int32_t field_pos[ 2 ];
+ field_pos[ 0 ] = ( pos + ( ( field_order == 0 ? 1 : 0 ) * frame_delta * 0.5 ) ) * ( 1 << 16 ) * ( 1.0 + softness );
+ field_pos[ 1 ] = ( pos + ( ( field_order == 0 ? 0 : 1 ) * frame_delta * 0.5 ) ) * ( 1 << 16 ) * ( 1.0 + softness );
+
+ register uint8_t *p;
+ register uint8_t *q;
+ register uint8_t *o;
+ uint16_t *l;
+
+ uint32_t value;
+
+ int32_t x_diff = ( luma_width << 16 ) / *width;
+ int32_t y_diff = ( luma_height << 16 ) / *height;
+ int32_t x_offset = 0;
+ int32_t y_offset = 0;
+ uint8_t *p_row;
+ uint8_t *q_row;
+
+ int32_t i_softness = softness * ( 1 << 16 );
+
+ int field_count = field_order < 0 ? 1 : 2;
+ int field_stride_src = field_count * stride_src;
+ int field_stride_dest = field_count * stride_dest;
+ int field = 0;
+
+ // composite using luma map
+ while ( field < field_count )
+ {
+ p_row = p_src + field * stride_src;
+ q_row = p_dest + field * stride_dest;
+ y_offset = field << 16;
+ i = field;
+
+ while ( i < height_src )
+ {
+ p = p_row;
+ q = q_row;
+ o = q;
+ l = luma_bitmap + ( y_offset >> 16 ) * ( luma_width * field_count );
+ x_offset = 0;
+ j = width_src;
+
+ while( j -- )
+ {
+ weight = l[ x_offset >> 16 ];
+ value = smoothstep( weight, i_softness + weight, field_pos[ field ] );
+ *o ++ = ( *p ++ * value + *q++ * ( ( 1 << 16 ) - value ) ) >> 16;
+ *o ++ = ( *p ++ * value + *q++ * ( ( 1 << 16 ) - value ) ) >> 16;
+ x_offset += x_diff;
+ }
+
+ y_offset += y_diff;
+ i += field_count;
+ p_row += field_stride_src;
+ q_row += field_stride_dest;
+ }
+
+ field ++;
+ }
+}
+
+/** Load the luma map from PGM stream.
+*/
+
+static void luma_read_pgm( FILE *f, uint16_t **map, int *width, int *height )
+{
+ uint8_t *data = NULL;
+ while (1)
+ {
+ char line[128];
+ char comment[128];
+ int i = 2;
+ int maxval;
+ int bpp;
+ uint16_t *p;
+
+ line[127] = '\0';
+
+ // get the magic code
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ if ( line[0] != 'P' || line[1] != '5' )
+ break;
+
+ // skip white space and see if a new line must be fetched
+ for ( i = 2; i < 127 && line[i] != '\0' && isspace( line[i] ); i++ );
+ if ( ( line[i] == '\0' || line[i] == '#' ) && fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // get the dimensions
+ if ( line[0] == 'P' )
+ i = sscanf( line, "P5 %d %d %d", width, height, &maxval );
+ else
+ i = sscanf( line, "%d %d %d", width, height, &maxval );
+
+ // get the height value, if not yet
+ if ( i < 2 )
+ {
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ i = sscanf( line, "%d", height );
+ if ( i == 0 )
+ break;
+ else
+ i = 2;
+ }
+
+ // get the maximum gray value, if not yet
+ if ( i < 3 )
+ {
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ // skip comments
+ while ( sscanf( line, " #%s", comment ) > 0 )
+ if ( fgets( line, 127, f ) == NULL )
+ break;
+
+ i = sscanf( line, "%d", &maxval );
+ if ( i == 0 )
+ break;
+ }
+
+ // determine if this is one or two bytes per pixel
+ bpp = maxval > 255 ? 2 : 1;
+
+ // allocate temporary storage for the raw data
+ data = mlt_pool_alloc( *width * *height * bpp );
+ if ( data == NULL )
+ break;
+
+ // read the raw data
+ if ( fread( data, *width * *height * bpp, 1, f ) != 1 )
+ break;
+
+ // allocate the luma bitmap
+ *map = p = (uint16_t*)mlt_pool_alloc( *width * *height * sizeof( uint16_t ) );
+ if ( *map == NULL )
+ break;
+
+ // proces the raw data into the luma bitmap
+ for ( i = 0; i < *width * *height * bpp; i += bpp )
+ {
+ if ( bpp == 1 )
+ *p++ = data[ i ] << 8;
+ else
+ *p++ = ( data[ i ] << 8 ) + data[ i+1 ];
+ }
+
+ break;
+ }
+
+ if ( data != NULL )
+ mlt_pool_release( data );
+}
+
+/** Generate a luma map from an RGB image.
+*/
+
+static void luma_read_yuv422( uint8_t *image, uint16_t **map, int width, int height )
+{
+ int i;
+ int size = width * height * 2;
+
+ // allocate the luma bitmap
+ uint16_t *p = *map = ( uint16_t* )mlt_pool_alloc( width * height * sizeof( uint16_t ) );
+ if ( *map == NULL )
+ return;
+
+ // proces the image data into the luma bitmap
+ for ( i = 0; i < size; i += 2 )
+ *p++ = ( image[ i ] - 16 ) * 299; // 299 = 65535 / 219
+}
+
+/** Generate a luma map from a YUV image.
+*/
+static void luma_read_rgb24( uint8_t *image, uint16_t **map, int width, int height )
+{
+}
+
+/** Get the image.
+*/
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the b frame from the stack
+ mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+
+ // Get the transition object
+ mlt_transition transition = mlt_frame_pop_service( a_frame );
+
+ // Get the properties of the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
+
+ // Get the properties of the a frame
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+ // Get the properties of the b frame
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ // This compositer is yuv422 only
+ *format = mlt_image_yuv422;
+
+ // The cached luma map information
+ int luma_width = mlt_properties_get_int( properties, "width" );
+ int luma_height = mlt_properties_get_int( properties, "height" );
+ uint16_t *luma_bitmap = mlt_properties_get_data( properties, "bitmap", NULL );
+ char *current_resource = mlt_properties_get( properties, "_resource" );
+
+ // If the filename property changed, reload the map
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ // Correct width/height if not specified
+ if ( luma_width == 0 || luma_height == 0 )
+ {
+ luma_width = mlt_properties_get_int( a_props, "width" );
+ luma_height = mlt_properties_get_int( a_props, "height" );
+ }
+
+ if ( resource != current_resource )
+ {
+ char temp[ 512 ];
+ char *extension = strrchr( resource, '.' );
+
+ if ( strchr( resource, '%' ) )
+ {
+ FILE *test;
+ sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 );
+ test = fopen( temp, "r" );
+ if ( test == NULL )
+ strcat( temp, ".png" );
+ else
+ fclose( test );
+ resource = temp;
+ extension = strrchr( resource, '.' );
+ }
+
+ // See if it is a PGM
+ if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 )
+ {
+ // Open PGM
+ FILE *f = fopen( resource, "r" );
+ if ( f != NULL )
+ {
+ // Load from PGM
+ luma_read_pgm( f, &luma_bitmap, &luma_width, &luma_height );
+ fclose( f );
+
+ // Set the transition properties
+ mlt_properties_set_int( properties, "width", luma_width );
+ mlt_properties_set_int( properties, "height", luma_height );
+ mlt_properties_set( properties, "_resource", resource );
+ mlt_properties_set_data( properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+ }
+ }
+ else if (!*resource)
+ {
+ luma_bitmap = NULL;
+ mlt_properties_set( properties, "_resource", NULL );
+ mlt_properties_set_data( properties, "bitmap", luma_bitmap, 0, mlt_pool_release, NULL );
+ }
+ else
+ {
+ // Get the factory producer service
+ char *factory = mlt_properties_get( properties, "factory" );
+
+ // Create the producer
+ mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
+ mlt_producer producer = mlt_factory_producer( profile, factory, resource );
+
+ // If we have one
+ if ( producer != NULL )
+ {
+ // Get the producer properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Ensure that we loop
+ mlt_properties_set( producer_properties, "eof", "loop" );
+
+ // Now pass all producer. properties on the transition down
+ mlt_properties_pass( producer_properties, properties, "producer." );
+
+ // We will get the alpha frame from the producer
+ mlt_frame luma_frame = NULL;
+
+ // Get the luma frame
+ if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 )
+ {
+ uint8_t *luma_image = NULL;
+ mlt_image_format luma_format = mlt_image_yuv422;
+
+ // Get image from the luma producer
+ mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "nearest" );
+ mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 );
+
+ // Generate the luma map
+ if ( luma_image != NULL && luma_format == mlt_image_yuv422 )
+ luma_read_yuv422( luma_image, &luma_bitmap, luma_width, luma_height );
+
+ else if ( luma_image != NULL && luma_format == mlt_image_rgb24 )
+ luma_read_rgb24( luma_image, &luma_bitmap, luma_width, luma_height );
+
+ // Set the transition properties
+ mlt_properties_set_int( properties, "width", luma_width );
+ mlt_properties_set_int( properties, "height", luma_height );
+ mlt_properties_set( properties, "_resource", resource);
+ mlt_properties_set_data( properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+
+ // Cleanup the luma frame
+ mlt_frame_close( luma_frame );
+ }
+
+ // Cleanup the luma producer
+ mlt_producer_close( producer );
+ }
+ }
+ }
+
+ // Arbitrary composite defaults
+ float mix = position_calculate( transition, a_frame );
+ float frame_delta = delta_calculate( transition, a_frame );
+
+ float luma_softness = mlt_properties_get_double( properties, "softness" );
+ int progressive =
+ mlt_properties_get_int( a_props, "consumer_deinterlace" ) ||
+ mlt_properties_get_int( properties, "progressive" ) ||
+ mlt_properties_get_int( b_props, "luma.progressive" );
+ int top_field_first = mlt_properties_get_int( b_props, "top_field_first" );
+ int reverse = mlt_properties_get_int( properties, "reverse" );
+ int invert = mlt_properties_get_int( properties, "invert" );
+
+ if ( mlt_properties_get( a_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+ mlt_properties_set( a_props, "rescale.interp", "nearest" );
+
+ // Since we are the consumer of the b_frame, we must pass along this
+ // consumer property from the a_frame
+ if ( mlt_properties_get_double( a_props, "aspect_ratio" ) == 0.0 )
+ mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+ if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+ mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+ mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+ // Honour the reverse here
+ if ( mix >= 1.0 )
+ mix -= floor( mix );
+
+ mix = reverse || invert ? 1 - mix : mix;
+ frame_delta *= reverse || invert ? -1.0 : 1.0;
+
+ // Ensure we get scaling on the b_frame
+ if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( b_props, "rescale.interp" ), "none" ) )
+ mlt_properties_set( b_props, "rescale.interp", "nearest" );
+
+ if ( mlt_properties_get( properties, "fixed" ) )
+ mix = mlt_properties_get_double( properties, "fixed" );
+
+ if ( luma_width > 0 && luma_height > 0 && luma_bitmap != NULL )
+ // Composite the frames using a luma map
+ luma_composite( !invert ? a_frame : b_frame, !invert ? b_frame : a_frame, luma_width, luma_height, luma_bitmap, mix, frame_delta,
+ luma_softness, progressive ? -1 : top_field_first, width, height );
+ else
+ // Dissolve the frames using the time offset for mix value
+ dissolve_yuv( a_frame, b_frame, mix, *width, *height );
+
+ // Extract the a_frame image info
+ *width = mlt_properties_get_int( !invert ? a_props : b_props, "width" );
+ *height = mlt_properties_get_int( !invert ? a_props : b_props, "height" );
+ *image = mlt_properties_get_data( !invert ? a_props : b_props, "image", NULL );
+
+ return 0;
+}
+
+
+/** Luma transition processing.
+*/
+
+static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
+{
+ // Get a unique name to store the frame position
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+
+ // Assign the current position to the name
+ mlt_properties_set_position( MLT_FRAME_PROPERTIES( a_frame ), name, mlt_frame_get_position( a_frame ) );
+
+ // Push the transition on to the frame
+ mlt_frame_push_service( a_frame, transition );
+
+ // Push the b_frame on to the stack
+ mlt_frame_push_frame( a_frame, b_frame );
+
+ // Push the transition method
+ mlt_frame_push_get_image( a_frame, transition_get_image );
+
+ return a_frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_transition transition_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *lumafile )
+{
+ mlt_transition transition = mlt_transition_new( );
+ if ( transition != NULL )
+ {
+ // Set the methods
+ transition->process = transition_process;
+
+ // Default factory
+ mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "factory", "fezzik" );
+
+ // Set the main property
+ mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "resource", lumafile );
+
+ // Inform apps and framework that this is a video only transition
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
+
+ return transition;
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ * transition_mix.c -- mix two audio streams
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_transition.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/** Get the audio.
+*/
+
+static int transition_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the b frame from the stack
+ mlt_frame b_frame = mlt_frame_pop_audio( frame );
+
+ // Get the effect
+ mlt_transition effect = mlt_frame_pop_audio( frame );
+
+ // Get the properties of the b frame
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( effect ), "combine" ) == 0 )
+ {
+ double mix_start = 0.5, mix_end = 0.5;
+ if ( mlt_properties_get( b_props, "audio.previous_mix" ) != NULL )
+ mix_start = mlt_properties_get_double( b_props, "audio.previous_mix" );
+ if ( mlt_properties_get( b_props, "audio.mix" ) != NULL )
+ mix_end = mlt_properties_get_double( b_props, "audio.mix" );
+ if ( mlt_properties_get_int( b_props, "audio.reverse" ) )
+ {
+ mix_start = 1 - mix_start;
+ mix_end = 1 - mix_end;
+ }
+
+ mlt_frame_mix_audio( frame, b_frame, mix_start, mix_end, buffer, format, frequency, channels, samples );
+ }
+ else
+ {
+ mlt_frame_combine_audio( frame, b_frame, buffer, format, frequency, channels, samples );
+ }
+
+ return 0;
+}
+
+
+/** Mix transition processing.
+*/
+
+static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ // Only if mix is specified, otherwise a producer may set the mix
+ if ( mlt_properties_get( properties, "start" ) != NULL )
+ {
+ // Determine the time position of this frame in the transition duration
+ mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL );
+ int always_active = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "always_active" );
+ mlt_position in = !always_active ? mlt_transition_get_in( this ) : mlt_properties_get_int( props, "in" );
+ mlt_position out = !always_active ? mlt_transition_get_out( this ) : mlt_properties_get_int( props, "out" );
+ int length = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "length" );
+ mlt_position time = !always_active ? mlt_frame_get_position( b_frame ) : mlt_properties_get_int( props, "_frame" );
+ double mix = ( double )( time - in ) / ( double )( out - in + 1 );
+
+ // TODO: Check the logic here - shouldn't we be computing current and next mixing levels in all cases?
+ if ( length == 0 )
+ {
+ // If there is an end mix level adjust mix to the range
+ if ( mlt_properties_get( properties, "end" ) != NULL )
+ {
+ double start = mlt_properties_get_double( properties, "start" );
+ double end = mlt_properties_get_double( properties, "end" );
+ mix = start + ( end - start ) * mix;
+ }
+ // A negative means total crossfade (uses position)
+ else if ( mlt_properties_get_double( properties, "start" ) >= 0 )
+ {
+ // Otherwise, start/constructor is a constant mix level
+ mix = mlt_properties_get_double( properties, "start" );
+ }
+
+ // Finally, set the mix property on the frame
+ mlt_properties_set_double( b_props, "audio.mix", mix );
+
+ // Initialise transition previous mix value to prevent an inadvertant jump from 0
+ mlt_position last_position = mlt_properties_get_position( properties, "_last_position" );
+ mlt_position current_position = mlt_frame_get_position( b_frame );
+ if ( mlt_properties_get( properties, "_previous_mix" ) == NULL
+ || current_position != last_position + 1 )
+ mlt_properties_set_double( properties, "_previous_mix", mix );
+
+ // Tell b frame what the previous mix level was
+ mlt_properties_set_double( b_props, "audio.previous_mix", mlt_properties_get_double( properties, "_previous_mix" ) );
+
+ // Save the current mix level for the next iteration
+ mlt_properties_set_double( properties, "_previous_mix", mlt_properties_get_double( b_props, "audio.mix" ) );
+
+ mlt_properties_set_double( b_props, "audio.reverse", mlt_properties_get_double( properties, "reverse" ) );
+ }
+ else
+ {
+ double level = mlt_properties_get_double( properties, "start" );
+ double mix_start = level;
+ double mix_end = mix_start;
+ double mix_increment = 1.0 / length;
+ if ( time - in < length )
+ {
+ mix_start = mix_start * ( ( double )( time - in ) / length );
+ mix_end = mix_start + mix_increment;
+ }
+ else if ( time > out - length )
+ {
+ mix_end = mix_start * ( ( double )( out - time - in ) / length );
+ mix_start = mix_end - mix_increment;
+ }
+
+ mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start;
+ mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end;
+ mlt_properties_set_double( b_props, "audio.previous_mix", mix_start );
+ mlt_properties_set_double( b_props, "audio.mix", mix_end );
+ }
+ }
+
+ // Override the get_audio method
+ mlt_frame_push_audio( a_frame, this );
+ mlt_frame_push_audio( a_frame, b_frame );
+ mlt_frame_push_audio( a_frame, transition_get_audio );
+
+ return a_frame;
+}
+
+/** Constructor for the transition.
+*/
+
+mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_transition this = calloc( sizeof( struct mlt_transition_s ), 1 );
+ if ( this != NULL && mlt_transition_init( this, NULL ) == 0 )
+ {
+ this->process = transition_process;
+ if ( arg != NULL )
+ mlt_properties_set_double( MLT_TRANSITION_PROPERTIES( this ), "start", atof( arg ) );
+ // Inform apps and framework that this is an audio only transition
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( this ), "_transition_type", 2 );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * transition_region.c -- region transition
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "transition_region.h"
+#include "transition_composite.h"
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int create_instance( mlt_transition this, char *name, char *value, int count )
+{
+ // Return from this function
+ int error = 0;
+
+ // Duplicate the value
+ char *type = strdup( value );
+
+ // Pointer to filter argument
+ char *arg = type == NULL ? NULL : strchr( type, ':' );
+
+ // New filter being created
+ mlt_filter filter = NULL;
+
+ // Cleanup type and arg
+ if ( arg != NULL )
+ *arg ++ = '\0';
+
+ // Create the filter
+ mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+ filter = mlt_factory_filter( profile, type, arg );
+
+ // If we have a filter, then initialise and store it
+ if ( filter != NULL )
+ {
+ // Properties of this
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // String to hold the property name
+ char id[ 256 ];
+
+ // String to hold the passdown key
+ char key[ 256 ];
+
+ // Construct id
+ sprintf( id, "_filter_%d", count );
+
+ // Counstruct key
+ sprintf( key, "%s.", name );
+
+ // Just in case, let's assume that the filter here has a composite
+ //mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "composite.geometry", "0%,0%:100%x100%" );
+ //mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "composite.fill", 1 );
+
+ // Pass all the key properties on the filter down
+ mlt_properties_pass( MLT_FILTER_PROPERTIES( filter ), properties, key );
+
+ // Ensure that filter is assigned
+ mlt_properties_set_data( properties, id, filter, 0, ( mlt_destructor )mlt_filter_close, NULL );
+ }
+ else
+ {
+ // Indicate that an error has occurred
+ error = 1;
+ }
+
+ // Cleanup
+ free( type );
+
+ // Return error condition
+ return error;
+}
+
+static uint8_t *filter_get_alpha_mask( mlt_frame this )
+{
+ uint8_t *alpha = NULL;
+
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the shape frame
+ mlt_frame shape_frame = mlt_properties_get_data( properties, "shape_frame", NULL );
+
+ // Get the width and height of the image
+ int region_width = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "width" );
+ int region_height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "height" );
+ uint8_t *image = NULL;
+ mlt_image_format format = mlt_image_yuv422;
+
+ // Get the shape image to trigger alpha creation
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( shape_frame ), "distort", 1 );
+ mlt_frame_get_image( shape_frame, &image, &format, ®ion_width, ®ion_height, 0 );
+
+ alpha = mlt_frame_get_alpha_mask( shape_frame );
+
+ // Generate from the Y component of the image if no alpha available
+ if ( alpha == NULL )
+ {
+ int size = region_width * region_height;
+ uint8_t *p = mlt_pool_alloc( size );
+ alpha = p;
+ while ( size -- )
+ {
+ *p ++ = ( int )( ( ( *image ++ - 16 ) * 299 ) / 255 );
+ image ++;
+ }
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", alpha, region_width * region_height, mlt_pool_release, NULL );
+ }
+ else
+ {
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", alpha, region_width * region_height, NULL, NULL );
+ }
+
+ this->get_alpha_mask = NULL;
+
+ return alpha;
+}
+
+/** Do it :-).
+*/
+
+static int transition_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Error we will return
+ int error = 0;
+
+ // We will get the 'b frame' from the frame stack
+ mlt_frame b_frame = mlt_frame_pop_frame( frame );
+
+ // Get the watermark transition object
+ mlt_transition this = mlt_frame_pop_service( frame );
+
+ // Get the properties of the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // Get the composite from the transition
+ mlt_transition composite = mlt_properties_get_data( properties, "composite", NULL );
+
+ // Look for the first filter
+ mlt_filter filter = mlt_properties_get_data( properties, "_filter_0", NULL );
+
+ // Get the unique id of the filter (used to reacquire the producer position)
+ char *name = mlt_properties_get( properties, "_unique_id" );
+
+ // Get the original producer position
+ mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
+
+ // Create a composite if we don't have one
+ if ( composite == NULL )
+ {
+ // Create composite via the factory
+ mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+ composite = mlt_factory_transition( profile, "composite", NULL );
+
+ // If we have one
+ if ( composite != NULL )
+ {
+ // Get the properties
+ mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
+
+ // We want to ensure that we don't get a wobble...
+ //mlt_properties_set_int( composite_properties, "distort", 1 );
+ mlt_properties_set_int( composite_properties, "progressive", 1 );
+
+ // Pass all the composite. properties on the transition down
+ mlt_properties_pass( composite_properties, properties, "composite." );
+
+ // Register the composite for reuse/destruction
+ mlt_properties_set_data( properties, "composite", composite, 0, ( mlt_destructor )mlt_transition_close, NULL );
+ }
+ }
+ else
+ {
+ // Pass all current properties down
+ mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
+ mlt_properties_pass( composite_properties, properties, "composite." );
+ }
+
+ // Create filters
+ if ( filter == NULL )
+ {
+ // Loop Variable
+ int i = 0;
+
+ // Number of filters created
+ int count = 0;
+
+ // Loop for all properties
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ {
+ // Get the name of this property
+ char *name = mlt_properties_get_name( properties, i );
+
+ // If the name does not contain a . and matches filter
+ if ( strchr( name, '.' ) == NULL && !strncmp( name, "filter", 6 ) )
+ {
+ // Get the filter constructor
+ char *value = mlt_properties_get_value( properties, i );
+
+ // Create an instance
+ if ( create_instance( this, name, value, count ) == 0 )
+ count ++;
+ }
+ }
+
+ // Look for the first filter again
+ filter = mlt_properties_get_data( properties, "_filter_0", NULL );
+ }
+ else
+ {
+ // Pass all properties down
+ mlt_filter temp = NULL;
+
+ // Loop Variable
+ int i = 0;
+
+ // Number of filters found
+ int count = 0;
+
+ // Loop for all properties
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ {
+ // Get the name of this property
+ char *name = mlt_properties_get_name( properties, i );
+
+ // If the name does not contain a . and matches filter
+ if ( strchr( name, '.' ) == NULL && !strncmp( name, "filter", 6 ) )
+ {
+ // Strings to hold the id and pass down key
+ char id[ 256 ];
+ char key[ 256 ];
+
+ // Construct id and key
+ sprintf( id, "_filter_%d", count );
+ sprintf( key, "%s.", name );
+
+ // Get the filter
+ temp = mlt_properties_get_data( properties, id, NULL );
+
+ if ( temp != NULL )
+ {
+ mlt_properties_pass( MLT_FILTER_PROPERTIES( temp ), properties, key );
+ count ++;
+ }
+ }
+ }
+ }
+
+ // Get the image
+ error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ // Only continue if we have both filter and composite
+ if ( composite != NULL )
+ {
+ // Get the resource of this filter (could be a shape [rectangle/circle] or an alpha provider of choice
+ const char *resource = mlt_properties_get( properties, "resource" );
+
+ // Get the old resource in case it's changed
+ char *old_resource = mlt_properties_get( properties, "_old_resource" );
+
+ // String to hold the filter to query on
+ char id[ 256 ];
+
+ // Index to hold the count
+ int i = 0;
+
+ // We will get the 'b frame' from the composite only if it's NULL
+ if ( b_frame == NULL )
+ {
+ // Copy the region
+ b_frame = composite_copy_region( composite, frame, position );
+
+ // Ensure a destructor
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), name, b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ }
+
+ // Set the position of the b_frame
+ mlt_frame_set_position( b_frame, position );
+
+ // Make sure the filter is in the correct position
+ while ( filter != NULL )
+ {
+ // Stack this filter
+ if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "off" ) == 0 )
+ mlt_filter_process( filter, b_frame );
+
+ // Generate the key for the next
+ sprintf( id, "_filter_%d", ++ i );
+
+ // Get the next filter
+ filter = mlt_properties_get_data( properties, id, NULL );
+ }
+
+ // Allow filters to be attached to a region filter
+ filter = mlt_properties_get_data( properties, "_region_filter", NULL );
+ if ( filter != NULL )
+ mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), b_frame, 0 );
+
+ // Hmm - this is probably going to go wrong....
+ mlt_frame_set_position( frame, position );
+
+ // Get the b frame and process with composite if successful
+ mlt_transition_process( composite, frame, b_frame );
+
+ // If we have a shape producer copy the alpha mask from the shape frame to the b_frame
+ if ( strcmp( resource, "rectangle" ) != 0 )
+ {
+ // Get the producer from the transition
+ mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+ // If We have no producer then create one
+ if ( producer == NULL || ( old_resource != NULL && strcmp( resource, old_resource ) ) )
+ {
+ // Get the factory producer service
+ char *factory = mlt_properties_get( properties, "factory" );
+
+ // Store the old resource
+ mlt_properties_set( properties, "_old_resource", resource );
+
+ // Special case circle resource
+ if ( strcmp( resource, "circle" ) == 0 )
+ resource = "pixbuf:<svg width='100' height='100'><circle cx='50' cy='50' r='50' fill='black'/></svg>";
+
+ // Create the producer
+ mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+ producer = mlt_factory_producer( profile, factory, resource );
+
+ // If we have one
+ if ( producer != NULL )
+ {
+ // Get the producer properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Ensure that we loop
+ mlt_properties_set( producer_properties, "eof", "loop" );
+
+ // Now pass all producer. properties on the transition down
+ mlt_properties_pass( producer_properties, properties, "producer." );
+
+ // Register the producer for reuse/destruction
+ mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ }
+ }
+
+ // Now use the shape producer
+ if ( producer != NULL )
+ {
+ // We will get the alpha frame from the producer
+ mlt_frame shape_frame = NULL;
+
+ // Make sure the producer is in the correct position
+ mlt_producer_seek( producer, position );
+
+ // Get the shape frame
+ if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &shape_frame, 0 ) == 0 )
+ {
+ // Ensure that the shape frame will be closed
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( b_frame ), "shape_frame", shape_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+
+ // Specify the callback for evaluation
+ b_frame->get_alpha_mask = filter_get_alpha_mask;
+ }
+ }
+ }
+
+ // Get the image
+ error = mlt_frame_get_image( frame, image, format, width, height, 0 );
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame );
+
+ // Get a unique name to store the frame position
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
+
+ // Assign the current position to the name
+ mlt_properties_set_position( properties, name, mlt_frame_get_position( a_frame ) );
+
+ // Push the transition on to the frame
+ mlt_frame_push_service( a_frame, this );
+
+ // Push the b_frame on to the stack
+ mlt_frame_push_frame( a_frame, b_frame );
+
+ // Push the transition method
+ mlt_frame_push_get_image( a_frame, transition_get_image );
+
+ // Return the frame
+ return a_frame;
+}
+
+/** Constructor for the transition.
+*/
+
+mlt_transition transition_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create a new transition
+ mlt_transition this = mlt_transition_new( );
+
+ // Further initialisation
+ if ( this != NULL )
+ {
+ // Get the properties from the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // Assign the transition process method
+ this->process = transition_process;
+
+ // Default factory
+ mlt_properties_set( properties, "factory", "fezzik" );
+
+ // Resource defines the shape of the region
+ mlt_properties_set( properties, "resource", arg == NULL ? "rectangle" : arg );
+
+ // Inform apps and framework that this is a video only transition
+ mlt_properties_set_int( properties, "_transition_type", 1 );
+ }
+
+ // Return the transition
+ return this;
+}
+
--- /dev/null
+/*
+ * transition_region.h -- region transition
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TRANSITION_REGION_H_
+#define _TRANSITION_REGION_H_
+
+#include <framework/mlt_transition.h>
+
+extern mlt_transition transition_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+#endif
--- /dev/null
+# This properties file describes the fx available to the data_send and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,70%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+.description=Top Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,5%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0%,93%:100%x7%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=100%,0%:300%x100%:100;-1=-300%,0%:300%x100%:100
+.filter[1].producer.font=San 32
+.filter[1].composite.titles=1
+
+#
+# ETV Location
+#
+
+location=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,80:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:100;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;24=0%,0%:100%x100%:0;49=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=center
+
+courtesy=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:0;12=-100%,0%:100%x100%:0;37=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;37=0%,0%:100%x100%:0;61=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+exclusive=region
+.description=Exclusive
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+file_shot=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=590,160:80x25
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=File Shot
+.filter[1].producer.font=San 18
+.filter[1].composite.geometry=0%,0%:100%x100%:15;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=0
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+special=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=465,375:255x35
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Special
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+ticker=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0,500:722x75
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Ticker - provided for reference
+.filter[1].composite.geometry=0%,0%:100%x100%:100
+.filter[1].composite.titles=0
+.filter[1].producer.font=San 24
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+.filter[1].composite.valign=centre
+
+super=region
+.description=Transcription
+.properties.0=filter[1].producer.markup
+.properties.1=filter[2].producer.markup
+.properties.align=filter[1].composite.valign
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.properties.length[2]=filter[2].composite.out
+.period=2
+.composite.geometry=0,410:720x90
+.filter[0]=watermark
+.filter[0].resource=colour:0xbbbbbb00
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[0].composite.luma=%luma18.pgm
+.filter[0].composite.out=25
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 32
+.filter[1].producer.fgcolour=0x6c0101ff
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=top
+.filter[2]=watermark
+.filter[2].resource=pango:
+.filter[2].producer.markup=
+.filter[2].producer.font=San 32
+.filter[2].producer.fgcolour=0x6c0101ff
+.filter[2].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[2].composite.titles=1
+.filter[2].composite.halign=centre
+.filter[2].composite.valign=bottom
+
+obscure=region
+.description=Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+.filter[0].start=0,0:100%x100%
+
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltdgraft$(LIBSUF)
+
+OBJS = factory.o \
+ filter_telecide.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2008 Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "telecide", filter_telecide_init );
+}
--- /dev/null
+/*
+ * filter_telecide.c -- Donald Graft's Inverse Telecine Filter
+ * Copyright (C) 2003 Donald A. Graft
+ * Copyright (C) 2008 Dan Dennedy <dan@dennedy.org>
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+//#define DEBUG_PATTERN_GUIDANCE
+
+#define MAX_CYCLE 6
+#define BLKSIZE 24
+#define BLKSIZE_TIMES2 (2 * BLKSIZE)
+#define GUIDE_32 1
+#define GUIDE_22 2
+#define GUIDE_32322 3
+#define AHEAD 0
+#define BEHIND 1
+#define POST_METRICS 1
+#define POST_FULL 2
+#define POST_FULL_MAP 3
+#define POST_FULL_NOMATCH 4
+#define POST_FULL_NOMATCH_MAP 5
+#define CACHE_SIZE 100000
+#define P 0
+#define C 1
+#define N 2
+#define PBLOCK 3
+#define CBLOCK 4
+
+#define NO_BACK 0
+#define BACK_ON_COMBED 1
+#define ALWAYS_BACK 2
+
+struct CACHE_ENTRY
+{
+ unsigned int frame;
+ unsigned int metrics[5];
+ unsigned int chosen;
+};
+
+struct PREDICTION
+{
+ unsigned int metric;
+ unsigned int phase;
+ unsigned int predicted;
+ unsigned int predicted_metric;
+};
+
+struct context_s {
+ int is_configured;
+ mlt_properties image_cache;
+ int out;
+
+ int tff, chroma, blend, hints, show, debug;
+ float dthresh, gthresh, vthresh, vthresh_saved, bthresh;
+ int y0, y1, nt, guide, post, back, back_saved;
+ int pitch, dpitch, pitchover2, pitchtimes4;
+ int w, h, wover2, hover2, hplus1over2, hminus2;
+ int xblocks, yblocks;
+#ifdef WINDOWED_MATCH
+ unsigned int *matchc, *matchp, highest_matchc, highest_matchp;
+#endif
+ unsigned int *sumc, *sump, highest_sumc, highest_sump;
+ int vmetric;
+ unsigned int *overrides, *overrides_p;
+ int film, override, inpattern, found;
+ int force;
+
+ // Used by field matching.
+ unsigned char *fprp, *fcrp, *fcrp_saved, *fnrp;
+// unsigned char *fprpU, *fcrpU, *fcrp_savedU, *fnrpU;
+// unsigned char *fprpV, *fcrpV, *fcrp_savedV, *fnrpV;
+ unsigned char *dstp, *finalp;
+// unsigned char *dstpU, *dstpV;
+ int chosen;
+ unsigned int p, c, pblock, cblock, lowest, predicted, predicted_metric;
+ unsigned int np, nc, npblock, ncblock, nframe;
+ float mismatch;
+ int pframe, x, y;
+ unsigned char *crp, *prp;
+ unsigned char *crpU, *prpU;
+ unsigned char *crpV, *prpV;
+ int hard;
+ char status[80];
+
+ // Metrics cache.
+ struct CACHE_ENTRY *cache;
+
+ // Pattern guidance data.
+ int cycle;
+ struct PREDICTION pred[MAX_CYCLE+1];
+};
+typedef struct context_s *context;
+
+
+static inline
+void BitBlt(uint8_t* dstp, int dst_pitch, const uint8_t* srcp,
+ int src_pitch, int row_size, int height)
+{
+ uint32_t y;
+ for(y=0;y<height;y++)
+ {
+ memcpy(dstp,srcp,row_size);
+ dstp+=dst_pitch;
+ srcp+=src_pitch;
+ }
+}
+
+static void Show(context cx, int frame, mlt_properties properties)
+{
+ char use;
+ char buf[512];
+
+ if (cx->chosen == P) use = 'p';
+ else if (cx->chosen == C) use = 'c';
+ else use = 'n';
+ snprintf(buf, sizeof(buf), "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np);
+ if ( cx->post )
+ snprintf(buf, sizeof(buf), "%sTelecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", buf, frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric);
+ if ( cx->guide )
+ snprintf(buf, sizeof(buf), "%spattern mismatch=%0.2f%%\n", buf, cx->mismatch);
+ snprintf(buf, sizeof(buf), "%sTelecide: frame %d: [%s %c]%s %s\n", buf, frame, cx->found ? "forcing" : "using", use,
+ cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "",
+ cx->guide ? cx->status : "");
+ mlt_properties_set( properties, "meta.attr.telecide.markup", buf );
+}
+
+static void Debug(context cx, int frame)
+{
+ char use;
+
+ if (cx->chosen == P) use = 'p';
+ else if (cx->chosen == C) use = 'c';
+ else use = 'n';
+ fprintf(stderr, "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np);
+ if ( cx->post )
+ fprintf(stderr, "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric);
+ if ( cx->guide )
+ fprintf(stderr, "pattern mismatch=%0.2f%%\n", cx->mismatch);
+ fprintf(stderr, "Telecide: frame %d: [%s %c]%s %s\n", frame, cx->found ? "forcing" : "using", use,
+ cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "",
+ cx->guide ? cx->status : "");
+}
+
+static void WriteHints(int film, int inpattern, mlt_properties frame_properties)
+{
+ mlt_properties_set_int( frame_properties, "telecide.progressive", film);
+ mlt_properties_set_int( frame_properties, "telecide.in_pattern", inpattern);
+}
+
+static void PutChosen(context cx, int frame, unsigned int chosen)
+{
+ int f = frame % CACHE_SIZE;
+ if (frame < 0 || frame > cx->out || cx->cache[f].frame != frame)
+ return;
+ cx->cache[f].chosen = chosen;
+}
+
+static void CacheInsert(context cx, int frame, unsigned int p, unsigned int pblock,
+ unsigned int c, unsigned int cblock)
+{
+ int f = frame % CACHE_SIZE;
+ if (frame < 0 || frame > cx->out)
+ fprintf( stderr, "%s: internal error: invalid frame %d for CacheInsert", __FUNCTION__, frame);
+ cx->cache[f].frame = frame;
+ cx->cache[f].metrics[P] = p;
+ if (f) cx->cache[f-1].metrics[N] = p;
+ cx->cache[f].metrics[C] = c;
+ cx->cache[f].metrics[PBLOCK] = pblock;
+ cx->cache[f].metrics[CBLOCK] = cblock;
+ cx->cache[f].chosen = 0xff;
+}
+
+static int CacheQuery(context cx, int frame, unsigned int *p, unsigned int *pblock,
+ unsigned int *c, unsigned int *cblock)
+{
+ int f;
+
+ f = frame % CACHE_SIZE;
+ if (frame < 0 || frame > cx->out)
+ fprintf( stderr, "%s: internal error: invalid frame %d for CacheQuery", __FUNCTION__, frame);
+ if (cx->cache[f].frame != frame)
+ {
+ return 0;
+ }
+ *p = cx->cache[f].metrics[P];
+ *c = cx->cache[f].metrics[C];
+ *pblock = cx->cache[f].metrics[PBLOCK];
+ *cblock = cx->cache[f].metrics[CBLOCK];
+ return 1;
+}
+
+static int PredictHardYUY2(context cx, int frame, unsigned int *predicted, unsigned int *predicted_metric)
+{
+ // Look for pattern in the actual delivered matches of the previous cycle of frames.
+ // If a pattern is found, use that to predict the current match.
+ if ( cx->guide == GUIDE_22 )
+ {
+ if (cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen == 0xff ||
+ cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen == 0xff)
+ return 0;
+ switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen << 4) +
+ (cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen))
+ {
+ case 0x11:
+ *predicted = C;
+ *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
+ break;
+ case 0x22:
+ *predicted = N;
+ *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
+ break;
+ default: return 0;
+ }
+ }
+ else if ( cx->guide == GUIDE_32 )
+ {
+ if (cx->cache[(frame-cx->cycle)%CACHE_SIZE ].chosen == 0xff ||
+ cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen == 0xff)
+ return 0;
+
+ switch ((cx->cache[(frame-cx->cycle)%CACHE_SIZE ].chosen << 16) +
+ (cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen << 12) +
+ (cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen << 8) +
+ (cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen << 4) +
+ (cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen))
+ {
+ case 0x11122:
+ case 0x11221:
+ case 0x12211:
+ case 0x12221:
+ case 0x21122:
+ case 0x11222:
+ *predicted = C;
+ *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
+ break;
+ case 0x22111:
+ case 0x21112:
+ case 0x22112:
+ case 0x22211:
+ *predicted = N;
+ *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
+ break;
+ default: return 0;
+ }
+ }
+ else if ( cx->guide == GUIDE_32322 )
+ {
+ if (cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen == 0xff ||
+ cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen == 0xff ||
+ cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen == 0xff)
+ return 0;
+
+ switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen << 20) +
+ (cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen << 16) +
+ (cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen << 12) +
+ (cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen << 8) +
+ (cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen << 4) +
+ (cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen))
+ {
+ case 0x111122:
+ case 0x111221:
+ case 0x112211:
+ case 0x122111:
+ case 0x111222:
+ case 0x112221:
+ case 0x122211:
+ case 0x222111:
+ *predicted = C;
+ *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
+ break;
+ case 0x221111:
+ case 0x211112:
+
+ case 0x221112:
+ case 0x211122:
+ *predicted = N;
+ *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
+ break;
+ default: return 0;
+ }
+ }
+#ifdef DEBUG_PATTERN_GUIDANCE
+ fprintf( stderr, "%s: pos=%d HARD: predicted=%d\n", __FUNCTION__, frame, *predicted);
+#endif
+ return 1;
+}
+
+static struct PREDICTION *PredictSoftYUY2(context cx, int frame )
+{
+ // Use heuristics to look forward for a match.
+ int i, j, y, c, n, phase;
+ unsigned int metric;
+
+ cx->pred[0].metric = 0xffffffff;
+ if (frame < 0 || frame > cx->out - cx->cycle) return cx->pred;
+
+ // Look at the next cycle of frames.
+ for (y = frame + 1; y <= frame + cx->cycle; y++)
+ {
+ // Look for a frame where the current and next match values are
+ // very close. Those are candidates to predict the phase, because
+ // that condition should occur only once per cycle. Store the candidate
+ // phases and predictions in a list sorted by goodness. The list will
+ // be used by the caller to try the phases in order.
+ c = cx->cache[y%CACHE_SIZE].metrics[C];
+ n = cx->cache[y%CACHE_SIZE].metrics[N];
+ if (c == 0) c = 1;
+ metric = (100 * abs (c - n)) / c;
+ phase = y % cx->cycle;
+ if (metric < 5)
+ {
+ // Place the new candidate phase in sorted order in the list.
+ // Find the insertion point.
+ i = 0;
+ while (metric > cx->pred[i].metric) i++;
+ // Find the end-of-list marker.
+ j = 0;
+ while (cx->pred[j].metric != 0xffffffff) j++;
+ // Shift all items below the insertion point down by one to make
+ // room for the insertion.
+ j++;
+ for (; j > i; j--)
+ {
+ cx->pred[j].metric = cx->pred[j-1].metric;
+ cx->pred[j].phase = cx->pred[j-1].phase;
+ cx->pred[j].predicted = cx->pred[j-1].predicted;
+ cx->pred[j].predicted_metric = cx->pred[j-1].predicted_metric;
+ }
+ // Insert the new candidate data.
+ cx->pred[j].metric = metric;
+ cx->pred[j].phase = phase;
+ if ( cx->guide == GUIDE_32 )
+ {
+ switch ((frame % cx->cycle) - phase)
+ {
+ case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case -3: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ }
+ }
+ else if ( cx->guide == GUIDE_32322 )
+ {
+ switch ((frame % cx->cycle) - phase)
+ {
+ case -5: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case -3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break;
+ case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ case +5: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break;
+ }
+ }
+ }
+#ifdef DEBUG_PATTERN_GUIDANCE
+ fprintf( stderr, "%s: pos=%d metric=%d phase=%d\n", __FUNCTION__, frame, metric, phase);
+#endif
+ }
+ return cx->pred;
+}
+
+static
+void CalculateMetrics(context cx, int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV,
+ unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV)
+{
+ int x, y, p, c, tmp1, tmp2, skip;
+ int vc;
+ unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2;
+ unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4;
+ unsigned char *a0, *a2, *b0, *b2, *b4;
+ unsigned int diff, index;
+# define T 4
+
+ /* Clear the block sums. */
+ for (y = 0; y < cx->yblocks; y++)
+ {
+ for (x = 0; x < cx->xblocks; x++)
+ {
+#ifdef WINDOWED_MATCH
+ matchp[y*xblocks+x] = 0;
+ matchc[y*xblocks+x] = 0;
+#endif
+ cx->sump[y * cx->xblocks + x] = 0;
+ cx->sumc[y * cx->xblocks + x] = 0;
+ }
+ }
+
+ /* Find the best field match. Subsample the frames for speed. */
+ currbot0 = fcrp + cx->pitch;
+ currbot2 = fcrp + 3 * cx->pitch;
+ currtop0 = fcrp;
+ currtop2 = fcrp + 2 * cx->pitch;
+ currtop4 = fcrp + 4 * cx->pitch;
+ prevbot0 = fprp + cx->pitch;
+ prevbot2 = fprp + 3 * cx->pitch;
+ prevtop0 = fprp;
+ prevtop2 = fprp + 2 * cx->pitch;
+ prevtop4 = fprp + 4 * cx->pitch;
+ if ( cx->tff )
+ {
+ a0 = prevbot0;
+ a2 = prevbot2;
+ b0 = currtop0;
+ b2 = currtop2;
+ b4 = currtop4;
+ }
+ else
+ {
+ a0 = currbot0;
+ a2 = currbot2;
+ b0 = prevtop0;
+ b2 = prevtop2;
+ b4 = prevtop4;
+ }
+ p = c = 0;
+
+ // Calculate the field match and film/video metrics.
+// if (vi.IsYV12()) skip = 1;
+// else
+ skip = 1 + ( !cx->chroma );
+ for (y = 0, index = 0; y < cx->h - 4; y+=4)
+ {
+ /* Exclusion band. Good for ignoring subtitles. */
+ if (cx->y0 == cx->y1 || y < cx->y0 || y > cx->y1)
+ {
+ for (x = 0; x < cx->w;)
+ {
+// if (vi.IsYV12())
+// index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
+// else
+ index = (y/BLKSIZE) * cx->xblocks + x/BLKSIZE_TIMES2;
+
+ // Test combination with current frame.
+ tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
+// diff = abs((long)currtop0[x] - (tmp1 >> 1));
+ diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
+ if (diff > cx->nt)
+ {
+ c += diff;
+#ifdef WINDOWED_MATCH
+ matchc[index] += diff;
+#endif
+ }
+
+ tmp1 = currbot0[x] + T;
+ tmp2 = currbot0[x] - T;
+ vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
+ (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
+ if (vc)
+ {
+ cx->sumc[index]++;
+ }
+
+ // Test combination with previous frame.
+ tmp1 = ((long)a0[x] + (long)a2[x]);
+ diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
+ if (diff > cx->nt)
+ {
+ p += diff;
+#ifdef WINDOWED_MATCH
+ matchp[index] += diff;
+#endif
+ }
+
+ tmp1 = a0[x] + T;
+ tmp2 = a0[x] - T;
+ vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
+ (tmp2 > b0[x] && tmp2 > b2[x]);
+ if (vc)
+ {
+ cx->sump[index]++;
+ }
+
+ x += skip;
+ if (!(x&3)) x += 4;
+ }
+ }
+ currbot0 += cx->pitchtimes4;
+ currbot2 += cx->pitchtimes4;
+ currtop0 += cx->pitchtimes4;
+ currtop2 += cx->pitchtimes4;
+ currtop4 += cx->pitchtimes4;
+ a0 += cx->pitchtimes4;
+ a2 += cx->pitchtimes4;
+ b0 += cx->pitchtimes4;
+ b2 += cx->pitchtimes4;
+ b4 += cx->pitchtimes4;
+ }
+
+// if (vi.IsYV12() && chroma == true)
+// {
+// int z;
+//
+// for (z = 0; z < 2; z++)
+// {
+// // Do the same for the U plane.
+// if (z == 0)
+// {
+// currbot0 = fcrpU + pitchover2;
+// currbot2 = fcrpU + 3 * pitchover2;
+// currtop0 = fcrpU;
+// currtop2 = fcrpU + 2 * pitchover2;
+// currtop4 = fcrpU + 4 * pitchover2;
+// prevbot0 = fprpU + pitchover2;
+// prevbot2 = fprpU + 3 * pitchover2;
+// prevtop0 = fprpU;
+// prevtop2 = fprpU + 2 * pitchover2;
+// prevtop4 = fprpU + 4 * pitchover2;
+// }
+// else
+// {
+// currbot0 = fcrpV + pitchover2;
+// currbot2 = fcrpV + 3 * pitchover2;
+// currtop0 = fcrpV;
+// currtop2 = fcrpV + 2 * pitchover2;
+// currtop4 = fcrpV + 4 * pitchover2;
+// prevbot0 = fprpV + pitchover2;
+// prevbot2 = fprpV + 3 * pitchover2;
+// prevtop0 = fprpV;
+// prevtop2 = fprpV + 2 * pitchover2;
+// prevtop4 = fprpV + 4 * pitchover2;
+// }
+// if (tff == true)
+// {
+// a0 = prevbot0;
+// a2 = prevbot2;
+// b0 = currtop0;
+// b2 = currtop2;
+// b4 = currtop4;
+// }
+// else
+// {
+// a0 = currbot0;
+// a2 = currbot2;
+// b0 = prevtop0;
+// b2 = prevtop2;
+// b4 = prevtop4;
+// }
+//
+// for (y = 0, index = 0; y < hover2 - 4; y+=4)
+// {
+// /* Exclusion band. Good for ignoring subtitles. */
+// if (y0 == y1 || y < y0/2 || y > y1/2)
+// {
+// for (x = 0; x < wover2;)
+// {
+// if (vi.IsYV12())
+// index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
+// else
+// index = (y/BLKSIZE)*xblocks + x/BLKSIZE_TIMES2;
+//
+// // Test combination with current frame.
+// tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
+// diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
+// if (diff > nt)
+// {
+// c += diff;
+//#ifdef WINDOWED_MATCH
+// matchc[index] += diff;
+//#endif
+// }
+//
+// tmp1 = currbot0[x] + T;
+// tmp2 = currbot0[x] - T;
+// vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
+// (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
+// if (vc)
+// {
+// sumc[index]++;
+// }
+//
+// // Test combination with previous frame.
+// tmp1 = ((long)a0[x] + (long)a2[x]);
+// diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
+// if (diff > nt)
+// {
+// p += diff;
+//#ifdef WINDOWED_MATCH
+// matchp[index] += diff;
+//#endif
+// }
+//
+// tmp1 = a0[x] + T;
+// tmp2 = a0[x] - T;
+// vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
+// (tmp2 > b0[x] && tmp2 > b2[x]);
+// if (vc)
+// {
+// sump[index]++;
+// }
+//
+// x ++;
+// if (!(x&3)) x += 4;
+// }
+// }
+// currbot0 += 4*pitchover2;
+// currbot2 += 4*pitchover2;
+// currtop0 += 4*pitchover2;
+// currtop2 += 4*pitchover2;
+// currtop4 += 4*pitchover2;
+// a0 += 4*pitchover2;
+// a2 += 4*pitchover2;
+// b0 += 4*pitchover2;
+// b2 += 4*pitchover2;
+// b4 += 4*pitchover2;
+// }
+// }
+// }
+//
+// // Now find the blocks that have the greatest differences.
+//#ifdef WINDOWED_MATCH
+// highest_matchp = 0;
+// for (y = 0; y < yblocks; y++)
+// {
+// for (x = 0; x < xblocks; x++)
+// {
+//if (frame == 45 && matchp[y * xblocks + x] > 2500)
+//{
+// sprintf(buf, "%d/%d = %d\n", x, y, matchp[y * xblocks + x]);
+// OutputDebugString(buf);
+//}
+// if (matchp[y * xblocks + x] > highest_matchp)
+// {
+// highest_matchp = matchp[y * xblocks + x];
+// }
+// }
+// }
+// highest_matchc = 0;
+// for (y = 0; y < yblocks; y++)
+// {
+// for (x = 0; x < xblocks; x++)
+// {
+//if (frame == 44 && matchc[y * xblocks + x] > 2500)
+//{
+// sprintf(buf, "%d/%d = %d\n", x, y, matchc[y * xblocks + x]);
+// OutputDebugString(buf);
+//}
+// if (matchc[y * xblocks + x] > highest_matchc)
+// {
+// highest_matchc = matchc[y * xblocks + x];
+// }
+// }
+// }
+//#endif
+ if ( cx->post )
+ {
+ cx->highest_sump = 0;
+ for (y = 0; y < cx->yblocks; y++)
+ {
+ for (x = 0; x < cx->xblocks; x++)
+ {
+ if (cx->sump[y * cx->xblocks + x] > cx->highest_sump)
+ {
+ cx->highest_sump = cx->sump[y * cx->xblocks + x];
+ }
+ }
+ }
+ cx->highest_sumc = 0;
+ for (y = 0; y < cx->yblocks; y++)
+ {
+ for (x = 0; x < cx->xblocks; x++)
+ {
+ if (cx->sumc[y * cx->xblocks + x] > cx->highest_sumc)
+ {
+ cx->highest_sumc = cx->sumc[y * cx->xblocks + x];
+ }
+ }
+ }
+ }
+#ifdef WINDOWED_MATCH
+ CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc);
+#else
+ CacheInsert( cx, frame, p, cx->highest_sump, c, cx->highest_sumc);
+#endif
+}
+
+
+/** Process the image.
+*/
+
+static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the filter service
+ mlt_filter filter = mlt_frame_pop_service( frame );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ mlt_properties frame_properties = mlt_frame_properties( frame );
+ context cx = mlt_properties_get_data( properties, "context", NULL );
+ mlt_service producer = mlt_service_producer( mlt_filter_service( filter ) );
+ cx->out = producer? mlt_producer_get_playtime( MLT_PRODUCER( producer ) ) : 999999;
+
+ if ( ! cx->is_configured )
+ {
+ cx->back = mlt_properties_get_int( properties, "back" );
+ cx->chroma = mlt_properties_get_int( properties, "chroma" );
+ cx->guide = mlt_properties_get_int( properties, "guide" );
+ cx->gthresh = mlt_properties_get_double( properties, "gthresh" );
+ cx->post = mlt_properties_get_int( properties, "post" );
+ cx->vthresh = mlt_properties_get_double( properties, "vthresh" );
+ cx->bthresh = mlt_properties_get_double( properties, "bthresh" );
+ cx->dthresh = mlt_properties_get_double( properties, "dthresh" );
+ cx->blend = mlt_properties_get_int( properties, "blend" );
+ cx->nt = mlt_properties_get_int( properties, "nt" );
+ cx->y0 = mlt_properties_get_int( properties, "y0" );
+ cx->y1 = mlt_properties_get_int( properties, "y1" );
+ cx->hints = mlt_properties_get_int( properties, "hints" );
+ cx->debug = mlt_properties_get_int( properties, "debug" );
+ cx->show = mlt_properties_get_int( properties, "show" );
+ }
+
+ // Get the image
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ if ( ! cx->sump )
+ {
+ int guide = mlt_properties_get_int( properties, "guide" );
+ cx->cycle = 0;
+ if ( guide == GUIDE_32 )
+ {
+ // 24fps to 30 fps telecine.
+ cx->cycle = 5;
+ }
+ else if ( guide == GUIDE_22 )
+ {
+ // PAL guidance (expect the current match to be continued).
+ cx->cycle = 2;
+ }
+ else if ( guide == GUIDE_32322 )
+ {
+ // 25fps to 30 fps telecine.
+ cx->cycle = 6;
+ }
+
+ cx->xblocks = (*width+BLKSIZE-1) / BLKSIZE;
+ cx->yblocks = (*height+BLKSIZE-1) / BLKSIZE;
+ cx->sump = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) );
+ cx->sumc = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) );
+ mlt_properties_set_data( properties, "sump", cx->sump, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "sumc", cx->sumc, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL );
+ cx->tff = mlt_properties_get_int( frame_properties, "top_field_first" );
+ // fprintf(stderr, "%s: TOP FIELD FIRST %d\n", __FUNCTION__, cx->tff );
+ }
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ // Put the current image into the image cache, keyed on position
+ size_t image_size = (*width * *height) << 1;
+ mlt_position pos = mlt_frame_get_position( frame );
+ uint8_t *image_copy = mlt_pool_alloc( image_size );
+ memcpy( image_copy, *image, image_size );
+ char key[20];
+ sprintf( key, "%d", pos );
+ mlt_properties_set_data( cx->image_cache, key, image_copy, image_size, (mlt_destructor)mlt_pool_release, NULL );
+
+ // Only if we have enough frame images cached
+ if ( pos > 1 && pos > cx->cycle + 1 )
+ {
+ pos -= cx->cycle + 1;
+ // Get the current frame image
+ sprintf( key, "%d", pos );
+ cx->fcrp = mlt_properties_get_data( cx->image_cache, key, NULL );
+ if (!cx->fcrp) return error;
+
+ // Get the previous frame image
+ cx->pframe = pos == 0 ? 0 : pos - 1;
+ sprintf( key, "%d", cx->pframe );
+ cx->fprp = mlt_properties_get_data( cx->image_cache, key, NULL );
+ if (!cx->fprp) return error;
+
+ // Get the next frame image
+ cx->nframe = pos > cx->out ? cx->out : pos + 1;
+ sprintf( key, "%d", cx->nframe );
+ cx->fnrp = mlt_properties_get_data( cx->image_cache, key, NULL );
+ if (!cx->fnrp) return error;
+
+ cx->pitch = *width << 1;
+ cx->pitchover2 = cx->pitch >> 1;
+ cx->pitchtimes4 = cx->pitch << 2;
+ cx->w = *width << 1;
+ cx->h = *height;
+ if ((cx->w/2) & 1)
+ fprintf( stderr, "%s: width must be a multiple of 2\n", __FUNCTION__ );
+ if (cx->h & 1)
+ fprintf( stderr, "%s: height must be a multiple of 2\n", __FUNCTION__ );
+ cx->wover2 = cx->w/2;
+ cx->hover2 = cx->h/2;
+ cx->hplus1over2 = (cx->h+1)/2;
+ cx->hminus2 = cx->h - 2;
+ cx->dpitch = cx->pitch;
+
+ // Ensure that the metrics for the frames
+ // after the current frame are in the cache. They will be used for
+ // pattern guidance.
+ if ( cx->guide )
+ {
+ for ( cx->y = pos + 1; (cx->y <= pos + cx->cycle + 1) && (cx->y <= cx->out); cx->y++ )
+ {
+ if ( ! CacheQuery( cx, cx->y, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) )
+ {
+ sprintf( key, "%d", cx->y );
+ cx->crp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL );
+ sprintf( key, "%d", cx->y ? cx->y - 1 : 1 );
+ cx->prp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL );
+ CalculateMetrics( cx, cx->y, cx->crp, NULL, NULL, cx->prp, NULL, NULL ); }
+ }
+ }
+
+ // Check for manual overrides of the field matching.
+ cx->found = 0;
+ cx->film = 1;
+ cx->override = 0;
+ cx->inpattern = 0;
+ cx->vthresh = cx->vthresh;
+ cx->back = cx->back_saved;
+
+ // Get the metrics for the current-previous (p), current-current (c), and current-next (n) match candidates.
+ if ( ! CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) )
+ {
+ CalculateMetrics( cx, pos, cx->fcrp, NULL, NULL, cx->fprp, NULL, NULL );
+ CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock );
+ }
+ if ( ! CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ) )
+ {
+ CalculateMetrics( cx, cx->nframe, cx->fnrp, NULL, NULL, cx->fcrp, NULL, NULL );
+ CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock );
+ }
+
+ // Determine the best candidate match.
+ if ( !cx->found )
+ {
+ cx->lowest = cx->c;
+ cx->chosen = C;
+ if ( cx->back == ALWAYS_BACK && cx->p < cx->lowest )
+ {
+ cx->lowest = cx->p;
+ cx->chosen = P;
+ }
+ if ( cx->np < cx->lowest )
+ {
+ cx->lowest = cx->np;
+ cx->chosen = N;
+ }
+ }
+ if ((pos == 0 && cx->chosen == P) || (pos == cx->out && cx->chosen == N))
+ {
+ cx->chosen = C;
+ cx->lowest = cx->c;
+ }
+
+ // See if we can apply pattern guidance.
+ cx->mismatch = 100.0;
+ if ( cx->guide )
+ {
+ cx->hard = 0;
+ if ( pos >= cx->cycle && PredictHardYUY2( cx, pos, &cx->predicted, &cx->predicted_metric) )
+ {
+ cx->inpattern = 1;
+ cx->mismatch = 0.0;
+ cx->hard = 1;
+ if ( cx->chosen != cx->predicted )
+ {
+ // The chosen frame doesn't match the prediction.
+ if ( cx->predicted_metric == 0 )
+ cx->mismatch = 0.0;
+ else
+ cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest ) ) / cx->predicted_metric;
+ if ( cx->mismatch < cx->gthresh )
+ {
+ // It's close enough, so use the predicted one.
+ if ( !cx->found )
+ {
+ cx->chosen = cx->predicted;
+ cx->override = 1;
+ }
+ }
+ else
+ {
+ cx->hard = 0;
+ cx->inpattern = 0;
+ }
+ }
+ }
+
+ if ( !cx->hard && cx->guide != GUIDE_22 )
+ {
+ int i;
+ struct PREDICTION *pred = PredictSoftYUY2( cx, pos );
+
+ if ( ( pos <= cx->out - cx->cycle) && ( pred[0].metric != 0xffffffff ) )
+ {
+ // Apply pattern guidance.
+ // If the predicted match metric is within defined percentage of the
+ // best calculated one, then override the calculated match with the
+ // predicted match.
+ i = 0;
+ while ( pred[i].metric != 0xffffffff )
+ {
+ cx->predicted = pred[i].predicted;
+ cx->predicted_metric = pred[i].predicted_metric;
+#ifdef DEBUG_PATTERN_GUIDANCE
+ fprintf(stderr, "%s: pos=%d predicted=%d\n", __FUNCTION__, pos, cx->predicted);
+#endif
+ if ( cx->chosen != cx->predicted )
+ {
+ // The chosen frame doesn't match the prediction.
+ if ( cx->predicted_metric == 0 )
+ cx->mismatch = 0.0;
+ else
+ cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest )) / cx->predicted_metric;
+ if ( (int) cx->mismatch <= cx->gthresh )
+ {
+ // It's close enough, so use the predicted one.
+ if ( !cx->found )
+ {
+ cx->chosen = cx->predicted;
+ cx->override = 1;
+ }
+ cx->inpattern = 1;
+ break;
+ }
+ else
+ {
+ // Looks like we're not in a predictable pattern.
+ cx->inpattern = 0;
+ }
+ }
+ else
+ {
+ cx->inpattern = 1;
+ cx->mismatch = 0.0;
+ break;
+ }
+ i++;
+ }
+ }
+ }
+ }
+
+ // Check the match for progressive versus interlaced.
+ if ( cx->post )
+ {
+ if (cx->chosen == P) cx->vmetric = cx->pblock;
+ else if (cx->chosen == C) cx->vmetric = cx->cblock;
+ else if (cx->chosen == N) cx->vmetric = cx->npblock;
+
+ if ( !cx->found && cx->back == BACK_ON_COMBED && cx->vmetric > cx->bthresh && cx->p < cx->lowest )
+ {
+ // Backward match.
+ cx->vmetric = cx->pblock;
+ cx->chosen = P;
+ cx->inpattern = 0;
+ cx->mismatch = 100;
+ }
+ if ( cx->vmetric > cx->vthresh )
+ {
+ // After field matching and pattern guidance the frame is still combed.
+ cx->film = 0;
+ if ( !cx->found && ( cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP ) )
+ {
+ cx->chosen = C;
+ cx->vmetric = cx->cblock;
+ cx->inpattern = 0;
+ cx->mismatch = 100;
+ }
+ }
+ }
+ cx->vthresh = cx->vthresh_saved;
+
+ // Setup strings for debug info.
+ if ( cx->inpattern && !cx->override ) strcpy( cx->status, "[in-pattern]" );
+ else if ( cx->inpattern && cx->override ) strcpy( cx->status, "[in-pattern*]" );
+ else strcpy( cx->status, "[out-of-pattern]" );
+
+ // Assemble and output the reconstructed frame according to the final match.
+ cx->dstp = *image;
+ if ( cx->chosen == N )
+ {
+ // The best match was with the next frame.
+ if ( cx->tff )
+ {
+ BitBlt( cx->dstp, 2 * cx->dpitch, cx->fnrp, 2 * cx->pitch, cx->w, cx->hover2 );
+ BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
+ }
+ else
+ {
+ BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+ BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fnrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
+ }
+ }
+ else if ( cx->chosen == C )
+ {
+ // The best match was with the current frame.
+ BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+ BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
+ }
+ else if ( ! cx->tff )
+ {
+ // The best match was with the previous frame.
+ BitBlt( cx->dstp, 2 * cx->dpitch, cx->fprp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+ BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
+ }
+ else
+ {
+ // The best match was with the previous frame.
+ BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+ BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fprp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
+ }
+ if ( cx->guide )
+ PutChosen( cx, pos, cx->chosen );
+
+ if ( !cx->post || cx->post == POST_METRICS )
+ {
+ if ( cx->force == '+') cx->film = 0;
+ else if ( cx->force == '-' ) cx->film = 1;
+ }
+ else if ((cx->force == '+') ||
+ ((cx->post == POST_FULL || cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP)
+ && (cx->film == 0 && cx->force != '-')))
+ {
+ unsigned char *dstpp, *dstpn;
+ int v1, v2;
+
+ if ( cx->blend )
+ {
+ // Do first and last lines.
+ uint8_t *final = mlt_pool_alloc( image_size );
+ cx->finalp = final;
+ mlt_properties_set_data( frame_properties, "image", final, image_size, (mlt_destructor)mlt_pool_release, NULL );
+ dstpn = cx->dstp + cx->dpitch;
+ for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+ {
+ cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpn[cx->x]) >> 1);
+ }
+ cx->finalp = final + (cx->h-1)*cx->dpitch;
+ cx->dstp = *image + (cx->h-1)*cx->dpitch;
+ dstpp = cx->dstp - cx->dpitch;
+ for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+ {
+ cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpp[cx->x]) >> 1);
+ }
+ // Now do the rest.
+ cx->dstp = *image + cx->dpitch;
+ dstpp = cx->dstp - cx->dpitch;
+ dstpn = cx->dstp + cx->dpitch;
+ cx->finalp = final + cx->dpitch;
+ for ( cx->y = 1; cx->y < cx->h - 1; cx->y++ )
+ {
+ for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+ {
+ v1 = (int) cx->dstp[cx->x] - cx->dthresh;
+ if ( v1 < 0 )
+ v1 = 0;
+ v2 = (int) cx->dstp[cx->x] + cx->dthresh;
+ if (v2 > 235) v2 = 235;
+ if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x]))
+ {
+ if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP )
+ {
+ if (cx->x & 1) cx->finalp[cx->x] = 128;
+ else cx->finalp[cx->x] = 235;
+ }
+ else
+ cx->finalp[cx->x] = ((int)dstpp[cx->x] + (int)dstpn[cx->x] + (int)cx->dstp[cx->x] + (int)cx->dstp[cx->x]) >> 2;
+ }
+ else cx->finalp[cx->x] = cx->dstp[cx->x];
+ }
+ cx->finalp += cx->dpitch;
+ cx->dstp += cx->dpitch;
+ dstpp += cx->dpitch;
+ dstpn += cx->dpitch;
+ }
+
+
+ if (cx->show ) Show( cx, pos, frame_properties);
+ if (cx->debug) Debug(cx, pos);
+ if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties);
+ goto final;
+ }
+
+ // Interpolate mode.
+ cx->dstp = *image + cx->dpitch;
+ dstpp = cx->dstp - cx->dpitch;
+ dstpn = cx->dstp + cx->dpitch;
+ for ( cx->y = 1; cx->y < cx->h - 1; cx->y+=2 )
+ {
+ for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+ {
+ v1 = (int) cx->dstp[cx->x] - cx->dthresh;
+ if (v1 < 0) v1 = 0;
+ v2 = (int) cx->dstp[cx->x] + cx->dthresh;
+ if (v2 > 235) v2 = 235;
+ if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x]))
+ {
+ if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP )
+ {
+ if (cx->x & 1) cx->dstp[cx->x] = 128;
+ else cx->dstp[cx->x] = 235;
+ }
+ else
+ cx->dstp[cx->x] = (dstpp[cx->x] + dstpn[cx->x]) >> 1;
+ }
+ }
+ cx->dstp += 2 * cx->dpitch;
+ dstpp += 2 * cx->dpitch;
+ dstpn += 2 * cx->dpitch;
+ }
+ }
+ if (cx->show ) Show( cx, pos, frame_properties);
+ if (cx->debug) Debug(cx, pos);
+ if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties);
+
+final:
+ // Flush frame at tail of period from the cache
+ sprintf( key, "%d", pos - 1 );
+ mlt_properties_set_data( cx->image_cache, key, NULL, 0, NULL, NULL );
+ }
+ else
+ {
+ // Signal the first {cycle} frames as invalid
+ mlt_properties_set_int( frame_properties, "garbage", 1 );
+ }
+ }
+ else if ( error == 0 && *format == mlt_image_yuv420p )
+ {
+ fprintf(stderr,"%s: %d pos %d\n", __FUNCTION__, *width * *height * 3/2, mlt_frame_get_position(frame) );
+ }
+
+ return error;
+}
+
+/** Process the frame object.
+*/
+
+static mlt_frame process( mlt_filter this, mlt_frame frame )
+{
+ // Push the filter on to the stack
+ mlt_frame_push_service( frame, this );
+
+ // Push the frame filter
+ mlt_frame_push_get_image( frame, get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = process;
+
+ // Allocate the context and set up for garbage collection
+ context cx = (context) mlt_pool_alloc( sizeof(struct context_s) );
+ memset( cx, 0, sizeof( struct context_s ) );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ mlt_properties_set_data( properties, "context", cx, sizeof(struct context_s), (mlt_destructor)mlt_pool_release, NULL );
+
+ // Allocate the metrics cache and set up for garbage collection
+ cx->cache = (struct CACHE_ENTRY *) mlt_pool_alloc(CACHE_SIZE * sizeof(struct CACHE_ENTRY ));
+ mlt_properties_set_data( properties, "cache", cx->cache, CACHE_SIZE * sizeof(struct CACHE_ENTRY), (mlt_destructor)mlt_pool_release, NULL );
+ int i;
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ cx->cache[i].frame = 0xffffffff;
+ cx->cache[i].chosen = 0xff;
+ }
+
+ // Allocate the image cache and set up for garbage collection
+ cx->image_cache = mlt_properties_new();
+ mlt_properties_set_data( properties, "image_cache", cx->image_cache, 0, (mlt_destructor)mlt_properties_close, NULL );
+
+ // Initialize the parameter defaults
+ mlt_properties_set_int( properties, "guide", 0 );
+ mlt_properties_set_int( properties, "back", 0 );
+ mlt_properties_set_int( properties, "chroma", 0 );
+ mlt_properties_set_int( properties, "post", POST_FULL );
+ mlt_properties_set_double( properties, "gthresh", 10.0 );
+ mlt_properties_set_double( properties, "vthresh", 50.0 );
+ mlt_properties_set_double( properties, "bthresh", 50.0 );
+ mlt_properties_set_double( properties, "dthresh", 7.0 );
+ mlt_properties_set_int( properties, "blend", 0 );
+ mlt_properties_set_int( properties, "nt", 10 );
+ mlt_properties_set_int( properties, "y0", 0 );
+ mlt_properties_set_int( properties, "y1", 0 );
+ mlt_properties_set_int( properties, "hints", 1 );
+ }
+ return this;
+}
+
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltdv$(LIBSUF)
+
+OBJS = factory.o \
+ producer_libdv.o \
+ consumer_libdv.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags libdv`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs libdv`
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ pkg-config libdv 2> /dev/null
+ disable_libdv=$?
+
+ if [ "$disable_libdv" != "0" ]
+ then
+ echo "- libdv not found: disabling"
+ touch ../disable-dv
+ exit 0
+ fi
+
+fi
+
--- /dev/null
+/*
+ * consumer_libdv.c -- a DV encoder based on libdv
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// mlt Header files
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+
+// System header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+// libdv header files
+#include <libdv/dv.h>
+
+#define FRAME_SIZE_525_60 10 * 150 * 80
+#define FRAME_SIZE_625_50 12 * 150 * 80
+
+// Forward references.
+static int consumer_start( mlt_consumer this );
+static int consumer_stop( mlt_consumer this );
+static int consumer_is_stopped( mlt_consumer this );
+static int consumer_encode_video( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame );
+static void consumer_encode_audio( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame );
+static void consumer_output( mlt_consumer this, uint8_t *dv_frame, int size, mlt_frame frame );
+static void *consumer_thread( void *arg );
+static void consumer_close( mlt_consumer this );
+
+/** Initialise the dv consumer.
+*/
+
+mlt_consumer consumer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Allocate the consumer
+ mlt_consumer this = calloc( 1, sizeof( struct mlt_consumer_s ) );
+
+ // If memory allocated and initialises without error
+ if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
+ {
+ // Get properties from the consumer
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Assign close callback
+ this->close = consumer_close;
+
+ // Interpret the argument
+ if ( arg != NULL )
+ mlt_properties_set( properties, "target", arg );
+
+ // Set the encode and output handling method
+ mlt_properties_set_data( properties, "video", consumer_encode_video, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "audio", consumer_encode_audio, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "output", consumer_output, 0, NULL, NULL );
+
+ // Terminate at end of the stream by default
+ mlt_properties_set_int( properties, "terminate_on_pause", 1 );
+
+ // Set up start/stop/terminated callbacks
+ this->start = consumer_start;
+ this->stop = consumer_stop;
+ this->is_stopped = consumer_is_stopped;
+ }
+ else
+ {
+ // Clean up in case of init failure
+ free( this );
+ this = NULL;
+ }
+
+ // Return this
+ return this;
+}
+
+/** Start the consumer.
+*/
+
+static int consumer_start( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Check that we're not already running
+ if ( !mlt_properties_get_int( properties, "running" ) )
+ {
+ // Allocate a thread
+ pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
+
+ // Assign the thread to properties
+ mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
+
+ // Set the running state
+ mlt_properties_set_int( properties, "running", 1 );
+
+ // Create the thread
+ pthread_create( thread, NULL, consumer_thread, this );
+ }
+ return 0;
+}
+
+/** Stop the consumer.
+*/
+
+static int consumer_stop( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Check that we're running
+ if ( mlt_properties_get_int( properties, "running" ) )
+ {
+ // Get the thread
+ pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
+
+ // Stop the thread
+ mlt_properties_set_int( properties, "running", 0 );
+
+ // Wait for termination
+ pthread_join( *thread, NULL );
+
+ // Close the output file :-) - this is obtuse - doesn't matter if output file
+ // exists or not - the destructor will kick in if it does
+ mlt_properties_set_data( properties, "output_file", NULL, 0, NULL, NULL );
+ }
+
+ return 0;
+}
+
+/** Determine if the consumer is stopped.
+*/
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ return !mlt_properties_get_int( properties, "running" );
+}
+
+/** Get or create a new libdv encoder.
+*/
+
+static dv_encoder_t *libdv_get_encoder( mlt_consumer this, mlt_frame frame )
+{
+ // Get the properties of the consumer
+ mlt_properties this_properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Obtain the dv_encoder
+ dv_encoder_t *encoder = mlt_properties_get_data( this_properties, "dv_encoder", NULL );
+
+ // Construct one if we don't have one
+ if ( encoder == NULL )
+ {
+ // Get the fps of the consumer (for now - should be from frame)
+ double fps = mlt_properties_get_double( this_properties, "fps" );
+
+ // Create the encoder
+ encoder = dv_encoder_new( 0, 0, 0 );
+
+ // Encoder settings
+ encoder->isPAL = fps == 25;
+ encoder->is16x9 = 0;
+ encoder->vlc_encode_passes = 1;
+ encoder->static_qno = 0;
+ encoder->force_dct = DV_DCT_AUTO;
+
+ // Store the encoder on the properties
+ mlt_properties_set_data( this_properties, "dv_encoder", encoder, 0, ( mlt_destructor )dv_encoder_free, NULL );
+ }
+
+ // Return the encoder
+ return encoder;
+}
+
+
+/** The libdv encode video method.
+*/
+
+static int consumer_encode_video( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame )
+{
+ // Obtain the dv_encoder
+ dv_encoder_t *encoder = libdv_get_encoder( this, frame );
+
+ // Get the properties of the consumer
+ mlt_properties this_properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // This will hold the size of the dv frame
+ int size = 0;
+
+ // Is the image rendered
+ int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered" );
+
+ // Get width and height
+ int width = mlt_properties_get_int( this_properties, "width" );
+ int height = mlt_properties_get_int( this_properties, "height" );
+
+ // If we get an encoder, then encode the image
+ if ( rendered && encoder != NULL )
+ {
+ // Specify desired image properties
+ mlt_image_format fmt = mlt_image_yuv422;
+ uint8_t *image = NULL;
+
+ // Get the image
+ mlt_events_fire( this_properties, "consumer-frame-show", frame, NULL );
+ mlt_frame_get_image( frame, &image, &fmt, &width, &height, 0 );
+
+ // Check that we get what we expected
+ if ( fmt != mlt_image_yuv422 ||
+ width != mlt_properties_get_int( this_properties, "width" ) ||
+ height != mlt_properties_get_int( this_properties, "height" ) ||
+ image == NULL )
+ {
+ // We should try to recover here
+ fprintf( stderr, "We have a problem houston...\n" );
+ }
+ else
+ {
+ // Calculate the size of the dv frame
+ size = height == 576 ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
+ }
+
+ // Process the frame
+ if ( size != 0 )
+ {
+ // Encode the image
+ dv_encode_full_frame( encoder, &image, e_dv_color_yuv, dv_frame );
+ }
+ }
+ else if ( encoder != NULL )
+ {
+ // Calculate the size of the dv frame (duplicate of previous)
+ size = height == 576 ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
+ }
+
+ return size;
+}
+
+/** The libdv encode audio method.
+*/
+
+static void consumer_encode_audio( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame )
+{
+ // Get the properties of the consumer
+ mlt_properties this_properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get the properties of the frame
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the dv_encoder
+ dv_encoder_t *encoder = libdv_get_encoder( this, frame );
+
+ // Only continue if we have an encoder
+ if ( encoder != NULL )
+ {
+ // Get the frame count
+ int count = mlt_properties_get_int( this_properties, "count" );
+
+ // Default audio args
+ mlt_audio_format fmt = mlt_audio_pcm;
+ int channels = 2;
+ int frequency = mlt_properties_get_int( this_properties, "frequency" );
+ int samples = mlt_sample_calculator( mlt_properties_get_double( this_properties, "fps" ), frequency, count );
+ int16_t *pcm = NULL;
+
+ // Get the frame number
+ time_t start = time( NULL );
+ int height = mlt_properties_get_int( this_properties, "height" );
+ int is_pal = height == 576;
+ int is_wide = mlt_properties_get_int( this_properties, "display_aspect_num" ) == 16;
+
+ // Temporary - audio buffer allocation
+ int16_t *audio_buffers[ 4 ];
+ int i = 0;
+ int j = 0;
+ for ( i = 0 ; i < 4; i ++ )
+ audio_buffers[ i ] = mlt_pool_alloc( 2 * DV_AUDIO_MAX_SAMPLES );
+
+ // Get the audio
+ mlt_frame_get_audio( frame, &pcm, &fmt, &frequency, &channels, &samples );
+
+ // Inform the encoder of the number of audio samples
+ encoder->samples_this_frame = samples;
+
+ // Fill the audio buffers correctly
+ if ( mlt_properties_get_double( frame_properties, "_speed" ) == 1.0 )
+ {
+ for ( i = 0; i < samples; i ++ )
+ for ( j = 0; j < channels; j++ )
+ audio_buffers[ j ][ i ] = *pcm ++;
+ }
+ else
+ {
+ for ( j = 0; j < channels; j++ )
+ memset( audio_buffers[ j ], 0, 2 * DV_AUDIO_MAX_SAMPLES );
+ }
+
+ // Encode audio on frame
+ dv_encode_full_audio( encoder, audio_buffers, channels, frequency, dv_frame );
+
+ // Specify meta data on the frame
+ dv_encode_metadata( dv_frame, is_pal, is_wide, &start, count );
+ dv_encode_timecode( dv_frame, is_pal, count );
+
+ // Update properties
+ mlt_properties_set_int( this_properties, "count", ++ count );
+
+ // Temporary - free audio buffers
+ for ( i = 0 ; i < 4; i ++ )
+ mlt_pool_release( audio_buffers[ i ] );
+ }
+}
+
+/** The libdv output method.
+*/
+
+static void consumer_output( mlt_consumer this, uint8_t *dv_frame, int size, mlt_frame frame )
+{
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ FILE *output = stdout;
+ char *target = mlt_properties_get( properties, "target" );
+
+ if ( target != NULL )
+ {
+ output = mlt_properties_get_data( properties, "output_file", NULL );
+ if ( output == NULL )
+ {
+ output = fopen( target, "w" );
+ if ( output != NULL )
+ mlt_properties_set_data( properties, "output_file", output, 0, ( mlt_destructor )fclose, 0 );
+ }
+ }
+
+ if ( output != NULL )
+ {
+ fwrite( dv_frame, size, 1, output );
+ fflush( output );
+ }
+ else
+ {
+ fprintf( stderr, "Unable to open %s\n", target );
+ }
+}
+
+/** The main thread - the argument is simply the consumer.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Map the argument to the object
+ mlt_consumer this = arg;
+
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get the terminate_on_pause property
+ int top = mlt_properties_get_int( properties, "terminate_on_pause" );
+
+ // Get the handling methods
+ int ( *video )( mlt_consumer, uint8_t *, mlt_frame ) = mlt_properties_get_data( properties, "video", NULL );
+ int ( *audio )( mlt_consumer, uint8_t *, mlt_frame ) = mlt_properties_get_data( properties, "audio", NULL );
+ int ( *output )( mlt_consumer, uint8_t *, int, mlt_frame ) = mlt_properties_get_data( properties, "output", NULL );
+
+ // Allocate a single PAL frame for encoding
+ uint8_t *dv_frame = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+ // Frame and size
+ mlt_frame frame = NULL;
+ int size = 0;
+
+ // Loop while running
+ while( mlt_properties_get_int( properties, "running" ) )
+ {
+ // Get the frame
+ frame = mlt_consumer_rt_frame( this );
+
+ // Check that we have a frame to work with
+ if ( frame != NULL )
+ {
+ // Terminate on pause
+ if ( top && mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0 )
+ {
+ mlt_frame_close( frame );
+ break;
+ }
+
+ // Obtain the dv_encoder
+ if ( libdv_get_encoder( this, frame ) != NULL )
+ {
+ // Encode the image
+ size = video( this, dv_frame, frame );
+
+ // Encode the audio
+ if ( size > 0 )
+ audio( this, dv_frame, frame );
+
+ // Output the frame
+ output( this, dv_frame, size, frame );
+
+ // Close the frame
+ mlt_frame_close( frame );
+ }
+ else
+ {
+ fprintf( stderr, "Unable to obtain dv encoder.\n" );
+ }
+ }
+ }
+
+ // Tidy up
+ mlt_pool_release( dv_frame );
+
+ mlt_consumer_stopped( this );
+
+ return NULL;
+}
+
+/** Close the consumer.
+*/
+
+static void consumer_close( mlt_consumer this )
+{
+ // Stop the consumer
+ mlt_consumer_stop( this );
+
+ // Close the parent
+ mlt_consumer_close( this );
+
+ // Free the memory
+ free( this );
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( consumer_type, "libdv", consumer_libdv_init );
+ MLT_REGISTER( producer_type, "libdv", producer_libdv_init );
+}
--- /dev/null
+/*
+ * producer_libdv.c -- simple libdv test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_profile.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libdv/dv.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define FRAME_SIZE_525_60 10 * 150 * 80
+#define FRAME_SIZE_625_50 12 * 150 * 80
+
+/** To conserve resources, we maintain a stack of dv decoders.
+*/
+
+static pthread_mutex_t decoder_lock = PTHREAD_MUTEX_INITIALIZER;
+static mlt_properties dv_decoders = NULL;
+
+dv_decoder_t *dv_decoder_alloc( )
+{
+ // We'll return a dv_decoder
+ dv_decoder_t *this = NULL;
+
+ // Lock the mutex
+ pthread_mutex_lock( &decoder_lock );
+
+ // Create the properties if necessary
+ if ( dv_decoders == NULL )
+ {
+ // Create the properties
+ dv_decoders = mlt_properties_new( );
+
+ // Create the stack
+ mlt_properties_set_data( dv_decoders, "stack", mlt_deque_init( ), 0, ( mlt_destructor )mlt_deque_close, NULL );
+
+ // Register the properties for clean up
+ mlt_factory_register_for_clean_up( dv_decoders, ( mlt_destructor )mlt_properties_close );
+ }
+
+ // Now try to obtain a decoder
+ if ( dv_decoders != NULL )
+ {
+ // Obtain the stack
+ mlt_deque stack = mlt_properties_get_data( dv_decoders, "stack", NULL );
+
+ // Pop the top of the stack
+ this = mlt_deque_pop_back( stack );
+
+ // Create a new decoder if none available
+ if ( this == NULL )
+ {
+ // We'll need a unique property ID for this
+ char label[ 256 ];
+
+ // Configure the decoder
+ this = dv_decoder_new( FALSE, FALSE, FALSE );
+ this->quality = DV_QUALITY_COLOR | DV_QUALITY_AC_2;
+ this->audio->arg_audio_emphasis = 2;
+ dv_set_audio_correction( this, DV_AUDIO_CORRECT_AVERAGE );
+ dv_set_error_log( this, NULL );
+
+ // Register it with the properties to ensure clean up
+ sprintf( label, "%p", this );
+ mlt_properties_set_data( dv_decoders, label, this, 0, ( mlt_destructor )dv_decoder_free, NULL );
+ }
+ }
+
+ // Unlock the mutex
+ pthread_mutex_unlock( &decoder_lock );
+
+ return this;
+}
+
+void dv_decoder_return( dv_decoder_t *this )
+{
+ // Lock the mutex
+ pthread_mutex_lock( &decoder_lock );
+
+ // Now try to return the decoder
+ if ( dv_decoders != NULL )
+ {
+ // Obtain the stack
+ mlt_deque stack = mlt_properties_get_data( dv_decoders, "stack", NULL );
+
+ // Push it back
+ mlt_deque_push_back( stack, this );
+ }
+
+ // Unlock the mutex
+ pthread_mutex_unlock( &decoder_lock );
+}
+
+
+typedef struct producer_libdv_s *producer_libdv;
+
+struct producer_libdv_s
+{
+ struct mlt_producer_s parent;
+ int fd;
+ int is_pal;
+ uint64_t file_size;
+ int frame_size;
+ long frames_in_file;
+ mlt_producer alternative;
+};
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+static int producer_collect_info( producer_libdv this, mlt_profile profile );
+
+mlt_producer producer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
+{
+ producer_libdv this = calloc( sizeof( struct producer_libdv_s ), 1 );
+
+ if ( filename != NULL && this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+ {
+ int destroy = 0;
+ mlt_producer producer = &this->parent;
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Set the resource property (required for all producers)
+ mlt_properties_set( properties, "resource", filename );
+
+ // Register transport implementation with the producer
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Register our get_frame implementation with the producer
+ producer->get_frame = producer_get_frame;
+
+ // If we have mov or dv, then we'll use an alternative producer
+ if ( strchr( filename, '.' ) != NULL && (
+ strncasecmp( strrchr( filename, '.' ), ".avi", 4 ) == 0 ||
+ strncasecmp( strrchr( filename, '.' ), ".mov", 4 ) == 0 ) )
+ {
+ // Load via an alternative mechanism
+ mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
+ this->alternative = mlt_factory_producer( profile, "kino", filename );
+
+ // If it's unavailable, then clean up
+ if ( this->alternative == NULL )
+ destroy = 1;
+ else
+ mlt_properties_pass( properties, MLT_PRODUCER_PROPERTIES( this->alternative ), "" );
+ this->is_pal = ( ( int ) mlt_producer_get_fps( producer ) ) == 25;
+ }
+ else
+ {
+ // Open the file if specified
+ this->fd = open( filename, O_RDONLY );
+
+ // Collect info
+ if ( this->fd == -1 || !producer_collect_info( this, profile ) )
+ destroy = 1;
+ }
+
+ // If we couldn't open the file, then destroy it now
+ if ( destroy )
+ {
+ mlt_producer_close( producer );
+ producer = NULL;
+ }
+
+ // Return the producer
+ return producer;
+ }
+ free( this );
+ return NULL;
+}
+
+static int read_frame( int fd, uint8_t* frame_buf, int *isPAL )
+{
+ int result = read( fd, frame_buf, FRAME_SIZE_525_60 ) == FRAME_SIZE_525_60;
+ if ( result )
+ {
+ *isPAL = ( frame_buf[3] & 0x80 );
+ if ( *isPAL )
+ {
+ int diff = FRAME_SIZE_625_50 - FRAME_SIZE_525_60;
+ result = read( fd, frame_buf + FRAME_SIZE_525_60, diff ) == diff;
+ }
+ }
+
+ return result;
+}
+
+static int producer_collect_info( producer_libdv this, mlt_profile profile )
+{
+ int valid = 0;
+
+ uint8_t *dv_data = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+ if ( dv_data != NULL )
+ {
+ // Read the first frame
+ valid = read_frame( this->fd, dv_data, &this->is_pal );
+
+ // If it looks like a valid frame, the get stats
+ if ( valid )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+
+ // Get a dv_decoder
+ dv_decoder_t *dv_decoder = dv_decoder_alloc( );
+
+ // Determine the file size
+ struct stat buf;
+ fstat( this->fd, &buf );
+
+ // Store the file size
+ this->file_size = buf.st_size;
+
+ // Determine the frame size
+ this->frame_size = this->is_pal ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
+
+ // Determine the number of frames in the file
+ this->frames_in_file = this->file_size / this->frame_size;
+
+ // Calculate default in/out points
+ int fps = 1000 * ( this->is_pal ? 25 : ( 30000.0 / 1001.0 ) );
+ if ( ( int )( mlt_profile_fps( profile ) * 1000 ) == fps )
+ {
+ if ( this->frames_in_file > 0 )
+ {
+ mlt_properties_set_position( properties, "length", this->frames_in_file );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", this->frames_in_file - 1 );
+ }
+ }
+ else
+ {
+ valid = 0;
+ }
+
+ // Parse the header for meta info
+ dv_parse_header( dv_decoder, dv_data );
+ mlt_properties_set_double( properties, "aspect_ratio",
+ dv_format_wide( dv_decoder ) ? ( this->is_pal ? 118.0/81.0 : 40.0/33.0 ) : ( this->is_pal ? 59.0/54.0 : 10.0/11.0 ) );
+ mlt_properties_set_double( properties, "source_fps", this->is_pal ? 25 : ( 30000.0 / 1001.0 ) );
+ mlt_properties_set_int( properties, "meta.media.nb_streams", 2 );
+ mlt_properties_set_int( properties, "video_index", 0 );
+ mlt_properties_set( properties, "meta.media.0.stream.type", "video" );
+ mlt_properties_set( properties, "meta.media.0.codec.name", "dvvideo" );
+ mlt_properties_set( properties, "meta.media.0.codec.long_name", "DV (Digital Video)" );
+ mlt_properties_set_int( properties, "audio_index", 1 );
+ mlt_properties_set( properties, "meta.media.1.stream.type", "audio" );
+ mlt_properties_set( properties, "meta.media.1.codec.name", "pcm_s16le" );
+ mlt_properties_set( properties, "meta.media.1.codec.long_name", "signed 16-bit little-endian PCM" );
+
+ // Return the decoder
+ dv_decoder_return( dv_decoder );
+ }
+
+ mlt_pool_release( dv_data );
+ }
+
+ return valid;
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int pitches[3] = { 0, 0, 0 };
+ uint8_t *pixels[3] = { NULL, NULL, NULL };
+
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get a dv_decoder
+ dv_decoder_t *decoder = dv_decoder_alloc( );
+
+ // Get the dv data
+ uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL );
+
+ // Get and set the quality request
+ char *quality = mlt_frame_pop_service( this );
+
+ if ( quality != NULL )
+ {
+ if ( strncmp( quality, "fast", 4 ) == 0 )
+ decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_DC );
+ else if ( strncmp( quality, "best", 4 ) == 0 )
+ decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_AC_2 );
+ else
+ decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_AC_1 );
+ }
+
+ // Parse the header for meta info
+ dv_parse_header( decoder, dv_data );
+
+ // Assign width and height according to the frame
+ *width = 720;
+ *height = dv_data[ 3 ] & 0x80 ? 576 : 480;
+
+ // Extract an image of the format requested
+ if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
+ {
+ // Allocate an image
+ uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 2 );
+
+ // Pass to properties for clean up
+ mlt_properties_set_data( properties, "image", image, *width * ( *height + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Decode the image
+ pitches[ 0 ] = *width * 2;
+ pixels[ 0 ] = image;
+ dv_decode_full_frame( decoder, dv_data, e_dv_color_yuv, pixels, pitches );
+
+ // Assign result
+ *buffer = image;
+ *format = mlt_image_yuv422;
+ }
+ else
+ {
+ // Allocate an image
+ uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 3 );
+
+ // Pass to properties for clean up
+ mlt_properties_set_data( properties, "image", image, *width * ( *height + 1 ) * 3, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Decode the frame
+ pitches[ 0 ] = 720 * 3;
+ pixels[ 0 ] = image;
+ dv_decode_full_frame( decoder, dv_data, e_dv_color_rgb, pixels, pitches );
+
+ // Assign result
+ *buffer = image;
+ *format = mlt_image_rgb24;
+ }
+
+ // Return the decoder
+ dv_decoder_return( decoder );
+
+ return 0;
+}
+
+static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ int16_t *p;
+ int i, j;
+ int16_t *audio_channels[ 4 ];
+
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get a dv_decoder
+ dv_decoder_t *decoder = dv_decoder_alloc( );
+
+ // Get the dv data
+ uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL );
+
+ // Parse the header for meta info
+ dv_parse_header( decoder, dv_data );
+
+ // Check that we have audio
+ if ( decoder->audio->num_channels > 0 )
+ {
+ // Obtain required values
+ *frequency = decoder->audio->frequency;
+ *samples = decoder->audio->samples_this_frame;
+ *channels = decoder->audio->num_channels;
+
+ // Create a temporary workspace
+ for ( i = 0; i < 4; i++ )
+ audio_channels[ i ] = mlt_pool_alloc( DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) );
+
+ // Create a workspace for the result
+ *buffer = mlt_pool_alloc( *channels * DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) );
+
+ // Pass the allocated audio buffer as a property
+ mlt_properties_set_data( properties, "audio", *buffer, *channels * DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ), ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Decode the audio
+ dv_decode_full_audio( decoder, dv_data, audio_channels );
+
+ // Interleave the audio
+ p = *buffer;
+ for ( i = 0; i < *samples; i++ )
+ for ( j = 0; j < *channels; j++ )
+ *p++ = audio_channels[ j ][ i ];
+
+ // Free the temporary work space
+ for ( i = 0; i < 4; i++ )
+ mlt_pool_release( audio_channels[ i ] );
+ }
+ else
+ {
+ // No audio available on the frame, so get test audio (silence)
+ mlt_frame_get_audio( this, buffer, format, frequency, channels, samples );
+ }
+
+ // Return the decoder
+ dv_decoder_return( decoder );
+
+ return 0;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Access the private data
+ producer_libdv this = producer->child;
+
+ // Will carry the frame data
+ uint8_t *data = NULL;
+
+ // Obtain the current frame number
+ uint64_t position = mlt_producer_frame( producer );
+
+ if ( this->alternative == NULL )
+ {
+ // Convert timecode to a file position (ensuring that we're on a frame boundary)
+ uint64_t offset = position * this->frame_size;
+
+ // Allocate space
+ data = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+ // Create an empty frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ // Seek and fetch
+ if ( this->fd != 0 &&
+ lseek( this->fd, offset, SEEK_SET ) == offset &&
+ read_frame( this->fd, data, &this->is_pal ) )
+ {
+ // Pass the dv data
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "dv_data", data, FRAME_SIZE_625_50, ( mlt_destructor )mlt_pool_release, NULL );
+ }
+ else
+ {
+ mlt_pool_release( data );
+ data = NULL;
+ }
+ }
+ else
+ {
+ // Seek
+ mlt_producer_seek( this->alternative, position );
+
+ // Fetch
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( this->alternative ), frame, 0 );
+
+ // Verify
+ if ( *frame != NULL )
+ data = mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "dv_data", NULL );
+ }
+
+ if ( data != NULL )
+ {
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Get a dv_decoder
+ dv_decoder_t *dv_decoder = dv_decoder_alloc( );
+
+ mlt_properties_set_int( properties, "test_image", 0 );
+ mlt_properties_set_int( properties, "test_audio", 0 );
+
+ // Update other info on the frame
+ mlt_properties_set_int( properties, "width", 720 );
+ mlt_properties_set_int( properties, "height", this->is_pal ? 576 : 480 );
+ mlt_properties_set_int( properties, "real_width", 720 );
+ mlt_properties_set_int( properties, "real_height", this->is_pal ? 576 : 480 );
+ mlt_properties_set_int( properties, "top_field_first", !this->is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 );
+
+ // Parse the header for meta info
+ dv_parse_header( dv_decoder, data );
+ //mlt_properties_set_int( properties, "progressive", dv_is_progressive( dv_decoder ) );
+ mlt_properties_set_double( properties, "aspect_ratio",
+ dv_format_wide( dv_decoder ) ? ( this->is_pal ? 118.0/81.0 : 40.0/33.0 ) : ( this->is_pal ? 59.0/54.0 : 10.0/11.0 ) );
+
+
+ mlt_properties_set_int( properties, "frequency", dv_decoder->audio->frequency );
+ mlt_properties_set_int( properties, "channels", dv_decoder->audio->num_channels );
+
+ // Register audio callback
+ if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "audio_index" ) > 0 )
+ mlt_frame_push_audio( *frame, producer_get_audio );
+
+ if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "video_index" ) > -1 )
+ {
+ // Push the quality string
+ mlt_frame_push_service( *frame, mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "quality" ) );
+
+ // Push the get_image method on to the stack
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+
+ // Return the decoder
+ dv_decoder_return( dv_decoder );
+ }
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ // Obtain this
+ producer_libdv this = parent->child;
+
+ // Close the file
+ if ( this->fd > 0 )
+ close( this->fd );
+
+ if ( this->alternative )
+ mlt_producer_close( this->alternative );
+
+ // Close the parent
+ parent->close = NULL;
+ mlt_producer_close( parent );
+
+ // Free the memory
+ free( this );
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmlteffectv$(LIBSUF)
+
+OBJS = factory.o \
+ filter_burn.o \
+ image.o \
+ utils.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2007 Stephane Fillod
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt.h>
+#include <string.h>
+
+extern mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "BurningTV", filter_burn_init );
+ MLT_REGISTER( filter_type, "burningtv", filter_burn_init );
+}
--- /dev/null
+/*
+ * filter_burn.c -- burning filter
+ * Copyright (C) 2007 Stephane Fillod
+ *
+ * Filter taken from EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * BurningTV - burns incoming objects.
+ * Copyright (C) 2001-2002 FUKUCHI Kentaro
+ *
+ * Fire routine is taken from Frank Jan Sorensen's demo program.
+ *
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "utils.h"
+
+
+#define MaxColor 120
+#define Decay 15
+#define MAGIC_THRESHOLD "50"
+
+static RGB32 palette[256];
+
+/* FIXME: endianess? */
+static void makePalette(void)
+{
+ int i, r, g, b;
+
+ for(i=0; i<MaxColor; i++) {
+ HSItoRGB(4.6-1.5*i/MaxColor, (double)i/MaxColor, (double)i/MaxColor, &r, &g, &b);
+ palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
+ }
+ for(i=MaxColor; i<256; i++) {
+ if(r<255)r++;if(r<255)r++;if(r<255)r++;
+ if(g<255)g++;
+ if(g<255)g++;
+ if(b<255)b++;
+ if(b<255)b++;
+ palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
+ }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ RGB32 *background;
+ unsigned char *diff;
+ unsigned char *buffer;
+
+ // Get the filter
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Get the image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ // Get the "Burn the foreground" value
+ int burn_foreground = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "foreground" );
+ int y_threshold = image_set_threshold_y(
+ mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "threshold" ));
+
+ // We'll process pixel by pixel
+ int x = 0;
+ int y = 0;
+ int i;
+
+ int video_width = *width;
+ int video_height = *height;
+ int video_area = video_width * video_height;
+ // We need to create a new frame as this effect modifies the input
+ RGB32 *dest = mlt_pool_alloc( video_area * sizeof(RGB32) );
+ RGB32 *src = (RGB32*)dest;
+
+ unsigned char v, w;
+ RGB32 a, b;
+
+ mlt_convert_yuv422_to_rgb24a(*image, (uint8_t *)dest, video_area);
+
+
+ diff = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ),
+ "_diff", NULL );
+ if (diff == NULL)
+ {
+ diff = mlt_pool_alloc(video_area*sizeof(unsigned char));
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_diff",
+ diff, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
+ }
+
+ buffer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ),
+ "_buffer", NULL );
+ if (buffer == NULL)
+ {
+ buffer = mlt_pool_alloc(video_area*sizeof(unsigned char));
+ memset(buffer, 0, video_area*sizeof(unsigned char));
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_buffer",
+ buffer, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
+ }
+
+
+ if (burn_foreground == 1) {
+ /* to burn the foreground, we need a background */
+ background = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ),
+ "_background", NULL );
+ if (background == NULL)
+ {
+ background = mlt_pool_alloc(video_area*sizeof(RGB32));
+ image_bgset_y(background, src, video_area, y_threshold);
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_background",
+ background, video_area*sizeof(RGB32), mlt_pool_release, NULL );
+ }
+ }
+
+ if (burn_foreground == 1) {
+ image_bgsubtract_y(diff, background, src, video_area, y_threshold);
+ } else {
+ /* default */
+ image_y_over(diff, src, video_area, y_threshold);
+ }
+
+ for(x=1; x<video_width-1; x++) {
+ v = 0;
+ for(y=0; y<video_height-1; y++) {
+ w = diff[y*video_width+x];
+ buffer[y*video_width+x] |= v ^ w;
+ v = w;
+ }
+ }
+ for(x=1; x<video_width-1; x++) {
+ i = video_width + x;
+ for(y=1; y<video_height; y++) {
+ v = buffer[i];
+ if(v<Decay)
+ buffer[i-video_width] = 0;
+ else
+ buffer[i-video_width+fastrand()%3-1] = v - (fastrand()&Decay);
+ i += video_width;
+ }
+ }
+
+ i = 1;
+ for(y=0; y<video_height; y++) {
+ for(x=1; x<video_width-1; x++) {
+ /* FIXME: endianess? */
+ a = (src[i] & 0xfefeff) + palette[buffer[i]];
+ b = a & 0x1010100;
+ dest[i] = a | (b - (b >> 8));
+ i++;
+ }
+ i += 2;
+ }
+
+ mlt_convert_rgb24a_to_yuv422((uint8_t *)dest, *width, *height, *width * sizeof(RGB32),
+ *image, NULL );
+
+ mlt_pool_release(dest);
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the frame filter
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "foreground", "0" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "threshold", MAGIC_THRESHOLD );
+ }
+ if (!palette[128])
+ {
+ makePalette();
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * image.c: utilities for image processing.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "utils.h"
+
+
+/*
+ * Collection of background subtraction functions
+ */
+
+/* checks only fake-Y value */
+/* In these function Y value is treated as R*2+G*4+B. */
+
+int image_set_threshold_y(int threshold)
+{
+ int y_threshold = threshold * 7; /* fake-Y value is timed by 7 */
+
+ return y_threshold;
+}
+
+void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold)
+{
+ int i;
+ int R, G, B;
+ const RGB32 *p;
+ short *q;
+
+ p = src;
+ q = (short *)background;
+ for(i=0; i<video_area; i++) {
+ /* FIXME: endianess */
+
+ R = ((*p)&0xff0000)>>(16-1);
+ G = ((*p)&0xff00)>>(8-2);
+ B = (*p)&0xff;
+ *q = (short)(R + G + B);
+ p++;
+ q++;
+ }
+}
+
+void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold)
+{
+ int i;
+ int R, G, B;
+ const RGB32 *p;
+ const short *q;
+ unsigned char *r;
+ int v;
+
+ p = src;
+ q = (const short *)background;
+ r = diff;
+ for(i=0; i<video_area; i++) {
+ /* FIXME: endianess */
+
+ R = ((*p)&0xff0000)>>(16-1);
+ G = ((*p)&0xff00)>>(8-2);
+ B = (*p)&0xff;
+ v = (R + G + B) - (int)(*q);
+ *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24);
+
+ p++;
+ q++;
+ r++;
+ }
+
+/* The origin of subtraction function is;
+ * diff(src, dest) = (abs(src - dest) > threshold) ? 0xff : 0;
+ *
+ * This functions is transformed to;
+ * (threshold > (src - dest) > -threshold) ? 0 : 0xff;
+ *
+ * (v + threshold)>>24 is 0xff when v is less than -threshold.
+ * (v - threshold)>>24 is 0xff when v is less than threshold.
+ * So, ((v + threshold)>>24) | ((threshold - v)>>24) will become 0xff when
+ * abs(src - dest) > threshold.
+ */
+}
+
+/* Background image is refreshed every frame */
+void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold)
+{
+ int i;
+ int R, G, B;
+ const RGB32 *p;
+ short *q;
+ unsigned char *r;
+ int v;
+
+ p = src;
+ q = (short *)background;
+ r = diff;
+ for(i=0; i<video_area; i++) {
+ /* FIXME: endianess */
+
+ R = ((*p)&0xff0000)>>(16-1);
+ G = ((*p)&0xff00)>>(8-2);
+ B = (*p)&0xff;
+ v = (R + G + B) - (int)(*q);
+ *q = (short)(R + G + B);
+ *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24);
+
+ p++;
+ q++;
+ r++;
+ }
+}
+
+/* checks each RGB value */
+
+/* The range of r, g, b are [0..7] */
+RGB32 image_set_threshold_RGB(int r, int g, int b)
+{
+ unsigned char R, G, B;
+ RGB32 rgb_threshold;
+
+ R = G = B = 0xff;
+ R = R<<r;
+ G = G<<g;
+ B = B<<b;
+ rgb_threshold = (RGB32)(R<<16 | G<<8 | B);
+
+ return rgb_threshold;
+}
+
+void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area)
+{
+ int i;
+ RGB32 *p;
+
+ p = background;
+ for(i=0; i<video_area; i++) {
+ *p++ = (*src++) & 0xfefefe;
+ }
+}
+
+void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold)
+{
+ int i;
+ const RGB32 *p, *q;
+ unsigned a, b;
+ unsigned char *r;
+
+ p = src;
+ q = background;
+ r = diff;
+ for(i=0; i<video_area; i++) {
+ a = (*p++)|0x1010100;
+ b = *q++;
+ a = a - b;
+ b = a & 0x1010100;
+ b = b - (b>>8);
+ b = b ^ 0xffffff;
+ a = a ^ b;
+ a = a & rgb_threshold;
+ *r++ = (0 - a)>>24;
+ }
+}
+
+void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold)
+{
+ int i;
+ const RGB32 *p;
+ RGB32 *q;
+ unsigned a, b;
+ unsigned char *r;
+
+ p = src;
+ q = background;
+ r = diff;
+ for(i=0; i<video_area; i++) {
+ a = *p|0x1010100;
+ b = *q&0xfefefe;
+ *q++ = *p++;
+ a = a - b;
+ b = a & 0x1010100;
+ b = b - (b>>8);
+ b = b ^ 0xffffff;
+ a = a ^ b;
+ a = a & rgb_threshold;
+ *r++ = (0 - a)>>24;
+ }
+}
+
+/* noise filter for subtracted image. */
+void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height)
+{
+ int x, y;
+ const unsigned char *src;
+ unsigned char *dest;
+ unsigned int count;
+ unsigned int sum1, sum2, sum3;
+
+ src = diff;
+ dest = diff2 + width +1;
+ for(y=1; y<height-1; y++) {
+ sum1 = src[0] + src[width] + src[width*2];
+ sum2 = src[1] + src[width+1] + src[width*2+1];
+ src += 2;
+ for(x=1; x<width-1; x++) {
+ sum3 = src[0] + src[width] + src[width*2];
+ count = sum1 + sum2 + sum3;
+ sum1 = sum2;
+ sum2 = sum3;
+ *dest++ = (0xff*3 - count)>>24;
+ src++;
+ }
+ dest += 2;
+ }
+}
+
+/* Y value filters */
+void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold)
+{
+ int i;
+ int R, G, B, v;
+ unsigned char *p = diff;
+
+ for(i = video_area; i>0; i--) {
+ R = ((*src)&0xff0000)>>(16-1);
+ G = ((*src)&0xff00)>>(8-2);
+ B = (*src)&0xff;
+ v = y_threshold - (R + G + B);
+ *p = (unsigned char)(v>>24);
+ src++;
+ p++;
+ }
+}
+
+void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold)
+{
+ int i;
+ int R, G, B, v;
+ unsigned char *p = diff;
+
+ for(i = video_area; i>0; i--) {
+ R = ((*src)&0xff0000)>>(16-1);
+ G = ((*src)&0xff00)>>(8-2);
+ B = (*src)&0xff;
+ v = (R + G + B) - y_threshold;
+ *p = (unsigned char)(v>>24);
+ src++;
+ p++;
+ }
+}
+
+/* tiny edge detection */
+void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold)
+{
+ int x, y;
+ const unsigned char *p;
+ unsigned char *q;
+ int r, g, b;
+ int ar, ag, ab;
+ int w;
+
+ p = (const unsigned char *)src;
+ q = diff2;
+ w = width * sizeof(RGB32);
+
+ for(y=0; y<height - 1; y++) {
+ for(x=0; x<width - 1; x++) {
+ b = p[0];
+ g = p[1];
+ r = p[2];
+ ab = abs(b - p[4]);
+ ag = abs(g - p[5]);
+ ar = abs(r - p[6]);
+ ab += abs(b - p[w]);
+ ag += abs(g - p[w+1]);
+ ar += abs(r - p[w+2]);
+ b = ab+ag+ar;
+ if(b > y_threshold) {
+ *q = 255;
+ } else {
+ *q = 0;
+ }
+ q++;
+ p += 4;
+ }
+ p += 4;
+ *q++ = 0;
+ }
+ memset(q, 0, width);
+}
+
+/* horizontal flipping */
+void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height)
+{
+ int x, y;
+
+ src += width - 1;
+ for(y=0; y<height; y++) {
+ for(x=0; x<width; x++) {
+ *dest++ = *src--;
+ }
+ src += width * 2;
+ }
+}
+
--- /dev/null
+/*
+ * EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * utils.c: utilities
+ *
+ */
+
+#include <math.h>
+#include "utils.h"
+
+/*
+ * HSI color system utilities
+ */
+static int itrunc(double f)
+{
+ int i;
+
+ i=(int)f;
+ if(i<0)i=0;
+ if(i>255)i=255;
+ return i;
+}
+
+void HSItoRGB(double H, double S, double I, int *r, int *g, int *b)
+{
+ double T,Rv,Gv,Bv;
+
+ Rv=1+S*sin(H-2*M_PI/3);
+ Gv=1+S*sin(H);
+ Bv=1+S*sin(H+2*M_PI/3);
+ T=255.999*I/2;
+ *r=itrunc(Rv*T);
+ *g=itrunc(Gv*T);
+ *b=itrunc(Bv*T);
+}
+
+/*
+ * fastrand - fast fake random number generator
+ * Warning: The low-order bits of numbers generated by fastrand()
+ * are bad as random numbers. For example, fastrand()%4
+ * generates 1,2,3,0,1,2,3,0...
+ * You should use high-order bits.
+ */
+#ifdef __DARWIN__
+static
+#endif
+unsigned int fastrand_val;
+
+unsigned int fastrand(void)
+{
+ return (fastrand_val=fastrand_val*1103515245+12345);
+}
+
+void fastsrand(unsigned int seed)
+{
+ fastrand_val = seed;
+}
--- /dev/null
+/*
+ * EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * utils.h: header file for utils
+ *
+ */
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <inttypes.h>
+
+typedef uint32_t RGB32;
+
+/* DEFINE's by nullset@dookie.net */
+#define RED(n) ((n>>16) & 0x000000FF)
+#define GREEN(n) ((n>>8) & 0x000000FF)
+#define BLUE(n) ((n>>0) & 0x000000FF)
+#define RGB(r,g,b) ((0<<24) + (r<<16) + (g <<8) + (b))
+#define INTENSITY(n) ( ( (RED(n)+GREEN(n)+BLUE(n))/3))
+
+/* utils.c */
+void HSItoRGB(double H, double S, double I, int *r, int *g, int *b);
+
+#ifndef __DARWIN__
+extern unsigned int fastrand_val;
+#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345)
+#endif
+unsigned int fastrand(void);
+void fastsrand(unsigned int);
+
+/* image.c */
+int image_set_threshold_y(int threshold);
+void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold);
+void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold);
+void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold);
+RGB32 image_set_threshold_RGB(int r, int g, int b);
+void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area);
+void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold);
+void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold);
+void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height);
+void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold);
+void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold);
+void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold);
+void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height);
+
+#endif /* __UTILS_H__ */
--- /dev/null
+include ../../../config.mak
+
+all:
+
+depend:
+
+distclean:
+
+clean:
+
+install: all
+ install -d $(DESTDIR)$(prefix)/share/mlt/feeds/PAL
+ install -d $(DESTDIR)$(prefix)/share/mlt/feeds/NTSC
+ install -m 644 PAL/*.* $(DESTDIR)$(prefix)/share/mlt/feeds/PAL
+ install -m 644 NTSC/*.* $(DESTDIR)$(prefix)/share/mlt/feeds/NTSC
--- /dev/null
+# This properties file describes the fx available to the data_feed and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,70%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+.description=Top Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,5%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0%,93%:100%x7%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=100%,0%:300%x100%:100;-1=-300%,0%:300%x100%:100
+.filter[1].producer.font=San 32
+.filter[1].composite.titles=1
+
+#
+# ETV Location
+#
+
+location=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,80:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:100;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;24=0%,0%:100%x100%:0;49=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=center
+
+courtesy=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:0;12=-100%,0%:100%x100%:0;37=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;37=0%,0%:100%x100%:0;61=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+exclusive=region
+.description=Exclusive
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+file_shot=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=590,160:80x25
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=File Shot
+.filter[1].producer.font=San 20
+.filter[1].composite.geometry=1%,1%:99%x99%:15;25=1%,1%:99%x99%:100
+.filter[1].composite.titles=0
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+special=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=465,375:255x35
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Special
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+ticker=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0,500:722x75
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Ticker - provided for reference
+.filter[1].composite.geometry=0%,0%:100%x100%:100
+.filter[1].composite.titles=0
+.filter[1].producer.font=San 24
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+.filter[1].composite.valign=centre
+
+super=region
+.description=Transcription
+.properties.0=filter[1].producer.markup
+.properties.1=filter[2].producer.markup
+.properties.align=filter[1].composite.valign
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.properties.length[2]=filter[2].composite.out
+.period=2
+.composite.geometry=0,410:720x90
+.filter[0]=watermark
+.filter[0].resource=colour:0xbbbbbb00
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[0].composite.luma=%luma18.pgm
+.filter[0].composite.out=25
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 32
+.filter[1].producer.fgcolour=0x6c0101ff
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=top
+.filter[2]=watermark
+.filter[2].resource=pango:
+.filter[2].producer.markup=
+.filter[2].producer.font=San 32
+.filter[2].producer.fgcolour=0x6c0101ff
+.filter[2].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[2].composite.titles=1
+.filter[2].composite.halign=centre
+.filter[2].composite.valign=bottom
+
+obscure=region
+.description=Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+.filter[0].start=0,0:100%x100%
+
--- /dev/null
+# This properties file describes the fx available to the data_feed and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+obscure=region
+.description=Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+.filter[0].start=0,0:100%x100%
+
--- /dev/null
+border_left=watermark
+.description=Border Left
+.resource=colour:black
+.reverse=1
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,0:100%x100%;25=2.5%,17.5%:45%x45%
+.composite.halign=c
+.composite.valign=c
+.composite.fill=1
+
+border_right=watermark
+.description=Border Right
+.resource=colour:black
+.reverse=1
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,0:100%x100%;25=52.5%,17.5%:45%x45%
+.composite.halign=c
+.composite.valign=c
+.composite.fill=1
+
--- /dev/null
+# This properties file describes the fx available to the data_send and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,70%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+.description=Top Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,5%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0%,93%:100%x7%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=100%,0%:300%x100%:100;-1=-300%,0%:300%x100%:100
+.filter[1].producer.font=San 32
+.filter[1].composite.titles=1
+
--- /dev/null
+# This properties file describes the fx available to the data_feed and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+location=region
+.description=Titles
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,80:230x30:0;12=,:x:100
+.composite.luma=%luma01.pgm
+.composite.softness=.3
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].composite.geometry=0,0:95%x100%
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=center
+
+courtesy=region
+.description=Courtesy
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,115:230x30:0;12=,:x:100
+.composite.luma=%luma01.pgm
+.composite.softness=.3
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].composite.geometry=0,0:95%x100%
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=centre
+
+exclusive=region
+.description=Exclusive
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=-230,115:230x30;12=0
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=ETV Exclusive
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].producer.weight=700
+.filter[1].composite.geometry=0,0:95%x100%
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=centre
+
+file_shot=region
+.description=Titles
+.period=2
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.properties.length[0]=composite.out
+.composite.geometry=590,160:80x25:0;12=,:x:100
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=File Shot
+.filter[1].producer.font=Sans
+.filter[1].producer.size=18
+.filter[1].producer.weight=700
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+special=region
+.description=Special
+.period=2
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=465,375:255x35
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.geometry=100%,0%:100%x100%:0;12=0%,0%:x:100
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=Special
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].producer.weight=700
+.filter[1].composite.geometry=100%,0%:100%x100%:0;12=0%,0%:x:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+ticker=region
+.description=Tickertape
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0,500:722x75
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=Ticker - provided for reference
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].producer.weight=700
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+super=region
+.description=Transcription
+.properties.0=filter[1].producer.text
+.properties.1=filter[2].producer.text
+.properties.align=filter[1].composite.valign
+.properties.weight=filter[1].producer.weight
+.properties.f0=filter[1].producer.font
+.properties.s0=filter[1].producer.size
+.properties.f1=filter[2].producer.font
+.properties.s1=filter[2].producer.size
+.properties.length[0]=composite.out
+.period=2
+.composite.geometry=0,410:720x90:0;25=,:x:100
+.composite.luma=%luma01.pgm
+.composite.luma_invert=1
+.composite.softness=.3
+.filter[0]=watermark
+.filter[0].resource=colour:0xbbbbbbff
+.filter[0].composite.geometry=0,0:100%:100%:70
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=
+.filter[1].producer.font=Sans
+.filter[1].producer.size=32
+.filter[1].producer.weight=700
+.filter[1].producer.fgcolour=0x6c0101ff
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=top
+.filter[2]=watermark
+.filter[2].resource=pango:
+.filter[2].producer.text=
+.filter[2].producer.font=Sans
+.filter[2].producer.size=32
+.filter[2].producer.fgcolour=0x6c0101ff
+.filter[2].composite.titles=1
+.filter[2].composite.halign=centre
+.filter[2].composite.valign=bottom
+
--- /dev/null
+greyscale=greyscale
+.description=Greyscale
+
+sepia=sepia
+.description=Sepia
+
+charcoal=charcoal
+.description=Charcoal
+
+invert=invert
+.description=Invert
+
--- /dev/null
+# This properties file describes the fx available to the data_feed and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+obscure0=region
+.description=Primary Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+
+obscure1=region
+.description=Secondary Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+
--- /dev/null
+http://*=avformat
+<?xml*=westley-xml
+*.westley=westley
+*.kdenlive=westley
+*.inigo=inigo_file
+*.asf=avformat
+*.avi=mcdv,avformat,libdv
+*.bmp=pixbuf,qimage,sdl_image
+*.dv=mcdv,avformat,libdv
+*.dif=mcdv,avformat,libdv
+*.exr=qimage
+*.gif=pixbuf,qimage,sdl_image
+*.graphics=westley
+*.jfx=westley
+*.jef=westley
+*.jpg=pixbuf,qimage,sdl_image
+*.jpeg=pixbuf,qimage,sdl_image
+*.kino=westley
+*.mp3=avformat
+*.mov=mcdv,avformat,libdv
+*.mpg=mcmpeg,avformat
+*.mpeg=mcmpeg,avformat
+*.mpl=pango
+*.ogg=avformat,vorbis
+*.pcx=pixbuf,qimage,sdl_image
+*.pgm=pgm,pixbuf,qimage,sdl_image
+*.png=pixbuf,qimage,sdl_image
+*.psd=qimage
+*.story=westley
+*.svg=pixbuf,qimage
+*.tga=pixbuf,qimage,sdl_image
+*.tif=pixbuf,qimage,sdl_image
+*.tiff=pixbuf,qimage,sdl_image
+*.txt=pango
+*.vob=mcmpeg,avformat
+*.wav=avformat
+*.wmv=avformat
+*.xcf=qimage,sdl_image
+*=avformat
--- /dev/null
+# This file defines the normalisers, their fallbacks and the order they're attached
+#
+# The names on the left are arbitrary, but the order in which they occur is the
+# order in which they're applied.
+#
+# The names of the services on the right dictate the preference used (if unavailable
+# the second and third are applied as applicable).
+
+crop=crop:1
+deinterlace=deinterlace,avdeinterlace
+rescaler=mcrescale,gtkrescale,rescale,swscale
+resizer=resize
+resampler=resample,soxresample,avresample
+data=data_feed:attr_check
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltfezzik$(LIBSUF)
+
+OBJS = factory.o \
+ producer_fezzik.o \
+ producer_hold.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+ install -m 644 ../fezzik.dict "$(DESTDIR)$(prefix)/share/mlt"
+ install -m 644 ../fezzik.ini "$(DESTDIR)$(prefix)/share/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+#include <string.h>
+
+extern mlt_producer producer_fezzik_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( producer_type, "fezzik", producer_fezzik_init );
+ MLT_REGISTER( producer_type, "hold", producer_hold_init );
+}
--- /dev/null
+/*
+ * producer_fezzik.c -- a normalising filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fnmatch.h>
+#include <assert.h>
+
+#include <framework/mlt.h>
+
+static mlt_properties dictionary = NULL;
+static mlt_properties normalisers = NULL;
+
+static mlt_producer create_from( mlt_profile profile, char *file, char *services )
+{
+ mlt_producer producer = NULL;
+ char *temp = strdup( services );
+ char *service = temp;
+ do
+ {
+ char *p = strchr( service, ',' );
+ if ( p != NULL )
+ *p ++ = '\0';
+ producer = mlt_factory_producer( profile, service, file );
+ service = p;
+ }
+ while ( producer == NULL && service != NULL );
+ free( temp );
+ return producer;
+}
+
+static mlt_producer create_producer( mlt_profile profile, char *file )
+{
+ mlt_producer result = NULL;
+
+ // 1st Line - check for service:resource handling
+ if ( strchr( file, ':' ) )
+ {
+ char *temp = strdup( file );
+ char *service = temp;
+ char *resource = strchr( temp, ':' );
+ *resource ++ = '\0';
+ result = mlt_factory_producer( profile, service, resource );
+ free( temp );
+ }
+
+ // 2nd Line preferences
+ if ( result == NULL )
+ {
+ int i = 0;
+ char *lookup = strdup( file );
+ char *p = lookup;
+
+ // We only need to load the dictionary once
+ if ( dictionary == NULL )
+ {
+ char temp[ 1024 ];
+ sprintf( temp, "%s/fezzik.dict", mlt_environment( "MLT_DATA" ) );
+ dictionary = mlt_properties_load( temp );
+ mlt_factory_register_for_clean_up( dictionary, ( mlt_destructor )mlt_properties_close );
+ }
+
+ // Convert the lookup string to lower case
+ while ( *p )
+ {
+ *p = tolower( *p );
+ p ++;
+ }
+
+ // Iterate through the dictionary
+ for ( i = 0; result == NULL && i < mlt_properties_count( dictionary ); i ++ )
+ {
+ char *name = mlt_properties_get_name( dictionary, i );
+ if ( fnmatch( name, lookup, 0 ) == 0 )
+ result = create_from( profile, file, mlt_properties_get_value( dictionary, i ) );
+ }
+
+ free( lookup );
+ }
+
+ // Finally, try just loading as service
+ if ( result == NULL )
+ result = mlt_factory_producer( profile, file, NULL );
+
+ return result;
+}
+
+static void create_filter( mlt_profile profile, mlt_producer producer, char *effect, int *created )
+{
+ // The swscale filter can not handle images with a width > 2048 and the
+ // sdl_image producer does not scale on its own
+ if ( strncmp( effect, "swscale", 7 ) == 0 &&
+ mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "_real_width" ) > 2048 &&
+ strcmp( mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" ), "sdl_image" ) == 0 )
+ return;
+
+ char *id = strdup( effect );
+ char *arg = strchr( id, ':' );
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ mlt_filter filter = mlt_factory_filter( profile, id, arg );
+ if ( filter != NULL )
+ {
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_fezzik", 1 );
+ mlt_producer_attach( producer, filter );
+ mlt_filter_close( filter );
+ *created = 1;
+ }
+ free( id );
+}
+
+static void attach_normalisers( mlt_profile profile, mlt_producer producer )
+{
+ // Loop variable
+ int i;
+
+ // Tokeniser
+ mlt_tokeniser tokeniser = mlt_tokeniser_init( );
+
+ // We only need to load the normalising properties once
+ if ( normalisers == NULL )
+ {
+ char temp[ 1024 ];
+ sprintf( temp, "%s/fezzik.ini", mlt_environment( "MLT_DATA" ) );
+ normalisers = mlt_properties_load( temp );
+ mlt_factory_register_for_clean_up( normalisers, ( mlt_destructor )mlt_properties_close );
+ }
+
+ // Apply normalisers
+ for ( i = 0; i < mlt_properties_count( normalisers ); i ++ )
+ {
+ int j = 0;
+ int created = 0;
+ char *value = mlt_properties_get_value( normalisers, i );
+ mlt_tokeniser_parse_new( tokeniser, value, "," );
+ for ( j = 0; !created && j < mlt_tokeniser_count( tokeniser ); j ++ )
+ create_filter( profile, producer, mlt_tokeniser_get_string( tokeniser, j ), &created );
+ }
+
+ // Close the tokeniser
+ mlt_tokeniser_close( tokeniser );
+}
+
+mlt_producer producer_fezzik_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create the producer
+ mlt_producer producer = NULL;
+ mlt_properties properties = NULL;
+
+ if ( arg != NULL )
+ producer = create_producer( profile, arg );
+
+ if ( producer != NULL )
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Attach filters if we have a producer and it isn't already westley'd :-)
+ if ( producer != NULL && mlt_properties_get( properties, "westley" ) == NULL && \
+ mlt_properties_get( properties, "_westley" ) == NULL && \
+ mlt_properties_get( properties, "fezzik_normalised" ) == NULL )
+ attach_normalisers( profile, producer );
+
+ // Now make sure we don't lose our identity
+ if ( properties != NULL )
+ mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
+
+ // Return the producer
+ return producer;
+}
--- /dev/null
+/*
+ * producer_hold.c -- frame holding producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <framework/mlt.h>
+
+// Forward references
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer this );
+
+/** Constructor for the frame holding producer. Basically, all this producer does is
+ provide a producer wrapper for the requested producer, allows the specifcation of
+ the frame required and will then repeatedly obtain that frame for each get_frame
+ and get_image requested.
+*/
+
+mlt_producer producer_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Construct a new holding producer
+ mlt_producer this = mlt_producer_new( );
+
+ // Construct the requested producer via fezzik
+ mlt_producer producer = mlt_factory_producer( profile, "fezzik", arg );
+
+ // Initialise the frame holding capabilities
+ if ( this != NULL && producer != NULL )
+ {
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Store the producer
+ mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+
+ // Set frame, in, out and length for this producer
+ mlt_properties_set_position( properties, "frame", 0 );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", 25 );
+ mlt_properties_set_position( properties, "length", 15000 );
+ mlt_properties_set( properties, "resource", arg );
+ mlt_properties_set( properties, "method", "onefield" );
+
+ // Override the get_frame method
+ this->get_frame = producer_get_frame;
+ this->close = ( mlt_destructor )producer_close;
+ }
+ else
+ {
+ // Clean up (not sure which one failed, can't be bothered to find out, so close both)
+ if ( this )
+ mlt_producer_close( this );
+ if ( producer )
+ mlt_producer_close( producer );
+
+ // Make sure we return NULL
+ this = NULL;
+ }
+
+ // Return this producer
+ return this;
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the real frame
+ mlt_frame real_frame = mlt_frame_pop_service( frame );
+
+ // Get the image from the real frame
+ int size = 0;
+ *buffer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", &size );
+ *width = mlt_properties_get_int( MLT_FRAME_PROPERTIES( real_frame ), "width" );
+ *height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( real_frame ), "height" );
+
+ // If this is the first time, get it from the producer
+ if ( *buffer == NULL )
+ {
+ mlt_properties_pass( MLT_FRAME_PROPERTIES( real_frame ), properties, "" );
+
+ // We'll deinterlace on the downstream deinterlacer
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( real_frame ), "consumer_deinterlace", 1 );
+
+ // We want distorted to ensure we don't hit the resize filter twice
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( real_frame ), "distort", 1 );
+
+ // Get the image
+ mlt_frame_get_image( real_frame, buffer, format, width, height, writable );
+
+ // Make sure we get the size
+ *buffer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", &size );
+ }
+
+ mlt_properties_pass( properties, MLT_FRAME_PROPERTIES( real_frame ), "" );
+
+ // Set the values obtained on the frame
+ if ( *buffer != NULL )
+ {
+ uint8_t *image = mlt_pool_alloc( size );
+ memcpy( image, *buffer, size );
+ *buffer = image;
+ mlt_properties_set_data( properties, "image", *buffer, size, mlt_pool_release, NULL );
+ }
+ else
+ {
+ // Pass the current image as is
+ mlt_properties_set_data( properties, "image", *buffer, size, NULL, NULL );
+ }
+
+ // Make sure that no further scaling is done
+ mlt_properties_set( properties, "rescale.interps", "none" );
+ mlt_properties_set( properties, "scale", "off" );
+
+ // All done
+ return 0;
+}
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Construct a new frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+ // If we have a frame, then stack the producer itself and the get_image method
+ if ( *frame != NULL )
+ {
+ // Define the real frame
+ mlt_frame real_frame = mlt_properties_get_data( properties, "real_frame", NULL );
+
+ // Obtain real frame if we don't have it
+ if ( real_frame == NULL )
+ {
+ // Get the producer
+ mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+ // Get the frame position requested
+ mlt_position position = mlt_properties_get_position( properties, "frame" );
+
+ // Seek the producer to the correct place
+ mlt_producer_seek( producer, position );
+
+ // Get the real frame
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &real_frame, index );
+
+ // Ensure that the real frame gets wiped eventually
+ mlt_properties_set_data( properties, "real_frame", real_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ }
+ else
+ {
+ // Temporary fix - ensure that we aren't seen as a test frame
+ int8_t *image = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", NULL );
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "image", image, 0, NULL, NULL );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 0 );
+ }
+
+ // Stack the real frame and method
+ mlt_frame_push_service( *frame, real_frame );
+ mlt_frame_push_service( *frame, producer_get_image );
+
+ // Ensure that the consumer sees what the real frame has
+ mlt_properties_pass( MLT_FRAME_PROPERTIES( *frame ), MLT_FRAME_PROPERTIES( real_frame ), "" );
+
+ mlt_properties_set( MLT_FRAME_PROPERTIES( real_frame ), "deinterlace_method",
+ mlt_properties_get( properties, "method" ) );
+ }
+
+ // Move to the next position
+ mlt_producer_prepare_next( this );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer this )
+{
+ this->close = NULL;
+ mlt_producer_close( this );
+ free( this );
+}
+
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltfrei0r$(LIBSUF)
+
+OBJS = factory.o \
+ producer_frei0r.o \
+ filter_frei0r.o \
+ transition_frei0r.o \
+ frei0r_helper.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#! /bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ echo "#include <frei0r.h> int main(){ f0r_plugin_info_t test; test.name;return 0;}"| gcc $CFLAGS -c -x c - >/dev/null 2>&1
+
+ if [ "$?" = "1" ]
+ then
+ touch ../disable-frei0r
+ echo "- frei0r plugin disabled. Install frei0r-plugins and make sure frei0r.h is available."
+ fi
+
+fi
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+#include <frei0r.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#define FREI0R_PLUGIN_PATH "/usr/lib/frei0r-1:/usr/local/lib/frei0r-1:/usr/lib64/frei0r-1:/opt/local/lib/frei0r-1"
+
+extern mlt_filter filter_frei0r_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_frame filter_process( mlt_filter this, mlt_frame frame );
+extern void filter_close( mlt_filter this );
+extern int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+extern void producer_close( mlt_producer this );
+extern void transition_close( mlt_transition this );
+extern mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame );
+
+static mlt_properties fill_param_info ( mlt_service_type type, const char *service_name, char *name )
+{
+ char file[ PATH_MAX ];
+ char servicetype[ 1024 ]="";
+ struct stat stat_buff;
+
+ switch ( type ) {
+ case producer_type:
+ strcpy ( servicetype , "producer" );
+ break;
+ case filter_type:
+ strcpy ( servicetype , "filter" );
+ break;
+ case transition_type:
+ strcpy ( servicetype , "transition" ) ;
+ break;
+ default:
+ strcpy ( servicetype , "" );
+ };
+
+ snprintf( file, PATH_MAX, "%s/frei0r/%s_%s.yml", mlt_environment( "MLT_DATA" ), servicetype, service_name );
+ stat(file,&stat_buff);
+
+ if (S_ISREG(stat_buff.st_mode)){
+ return mlt_properties_parse_yaml( file );
+ }
+
+ void* handle=dlopen(name,RTLD_LAZY);
+ if (!handle) return NULL;
+ void (*plginfo)(f0r_plugin_info_t*)=dlsym(handle,"f0r_get_plugin_info");
+ void (*param_info)(f0r_param_info_t*,int param_index)=dlsym(handle,"f0r_get_param_info");
+ if (!plginfo || !param_info) {
+ dlclose(handle);
+ return NULL;
+ }
+ mlt_properties metadata = mlt_properties_new();
+ f0r_plugin_info_t info;
+ char string[48];
+ int j=0;
+
+ plginfo(&info);
+ snprintf ( string, sizeof(string) , "%d.%d" , info.major_version , info.minor_version );
+ mlt_properties_set ( metadata, "schema_version" , "0.1" );
+ mlt_properties_set ( metadata, "title" , info.name );
+ mlt_properties_set ( metadata, "version", string );
+ mlt_properties_set ( metadata, "identifier" , service_name );
+ mlt_properties_set ( metadata, "description" , info.explanation );
+ mlt_properties_set ( metadata, "creator" , info.author );
+ switch (type){
+ case producer_type:
+ mlt_properties_set ( metadata, "type" , "producer" );
+ break;
+ case filter_type:
+ mlt_properties_set ( metadata, "type" , "filter" );
+ break;
+ case transition_type:
+ mlt_properties_set ( metadata, "type" , "transition" );
+ break;
+ default:
+ break;
+ }
+
+ mlt_properties parameter = mlt_properties_new ( );
+ mlt_properties_set_data ( metadata , "parameters" , parameter , 0 , ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_properties tags = mlt_properties_new ( );
+ mlt_properties_set_data ( metadata , "tags" , tags , 0 , ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_properties_set ( tags , "0" , "Video" );
+
+ for (j=0;j<info.num_params;j++){
+ snprintf ( string , sizeof(string), "%d" , j );
+ mlt_properties pnum = mlt_properties_new ( );
+ mlt_properties_set_data ( parameter , string , pnum , 0 , ( mlt_destructor )mlt_properties_close, NULL );
+ f0r_param_info_t paraminfo;
+ param_info(¶minfo,j);
+ mlt_properties_set ( pnum , "identifier" , paraminfo.name );
+ mlt_properties_set ( pnum , "title" , paraminfo.name );
+ mlt_properties_set ( pnum , "description" , paraminfo.explanation);
+ if ( paraminfo.type == F0R_PARAM_DOUBLE ){
+ mlt_properties_set ( pnum , "type" , "float" );
+ mlt_properties_set ( pnum , "minimum" , "0" );
+ mlt_properties_set ( pnum , "maximum" , "1" );
+ mlt_properties_set ( pnum , "readonly" , "no" );
+ mlt_properties_set ( pnum , "widget" , "spinner" );
+ }else
+ if ( paraminfo.type == F0R_PARAM_BOOL ){
+ mlt_properties_set ( pnum , "type" , "boolean" );
+ mlt_properties_set ( pnum , "minimum" , "0" );
+ mlt_properties_set ( pnum , "maximum" , "1" );
+ mlt_properties_set ( pnum , "readonly" , "no" );
+ }else
+ if ( paraminfo.type == F0R_PARAM_COLOR ){
+ mlt_properties_set ( pnum , "type" , "color" );
+ mlt_properties_set ( pnum , "readonly" , "no" );
+ }else
+ if ( paraminfo.type == F0R_PARAM_STRING ){
+ mlt_properties_set ( pnum , "type" , "string" );
+ mlt_properties_set ( pnum , "readonly" , "no" );
+ }
+ }
+ dlclose(handle);
+ free(name);
+
+ return metadata;
+}
+
+static void * load_lib( mlt_profile profile, mlt_service_type type , void* handle){
+
+ int i=0;
+ void (*f0r_get_plugin_info)(f0r_plugin_info_t*),
+ *f0r_construct , *f0r_update , *f0r_destruct,
+ (*f0r_get_param_info)(f0r_param_info_t* info, int param_index),
+ (*f0r_init)(void) , *f0r_deinit ,
+ (*f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index),
+ (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index),
+ (*f0r_update2) (f0r_instance_t instance, double time, const uint32_t* inframe1,
+ const uint32_t* inframe2,const uint32_t* inframe3, uint32_t* outframe);
+
+ if ( ( f0r_construct = dlsym(handle, "f0r_construct") ) &&
+ (f0r_update = dlsym(handle,"f0r_update") ) &&
+ (f0r_destruct = dlsym(handle,"f0r_destruct") ) &&
+ (f0r_get_plugin_info = dlsym(handle,"f0r_get_plugin_info") ) &&
+ (f0r_get_param_info = dlsym(handle,"f0r_get_param_info") ) &&
+ (f0r_set_param_value= dlsym(handle,"f0r_set_param_value" ) ) &&
+ (f0r_get_param_value= dlsym(handle,"f0r_get_param_value" ) ) &&
+ (f0r_init= dlsym(handle,"f0r_init" ) ) &&
+ (f0r_deinit= dlsym(handle,"f0r_deinit" ) )
+ ){
+
+ f0r_update2=dlsym(handle,"f0r_update2");
+
+ f0r_plugin_info_t info;
+ f0r_get_plugin_info(&info);
+
+ void* ret=NULL;
+ mlt_properties properties=NULL;
+
+ if (type == producer_type && info.plugin_type == F0R_PLUGIN_TYPE_SOURCE ){
+ mlt_producer this = mlt_producer_new( );
+ if ( this != NULL )
+ {
+ this->get_frame = producer_get_frame;
+ this->close = ( mlt_destructor )producer_close;
+ f0r_init();
+ properties=MLT_PRODUCER_PROPERTIES ( this );
+
+ for (i=0;i<info.num_params;i++){
+ f0r_param_info_t pinfo;
+ f0r_get_param_info(&pinfo,i);
+
+ }
+
+ ret=this;
+ }
+ } else if (type == filter_type && info.plugin_type == F0R_PLUGIN_TYPE_FILTER ){
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ this->close = filter_close;
+ f0r_init();
+ properties=MLT_FILTER_PROPERTIES ( this );
+
+ for (i=0;i<info.num_params;i++){
+ f0r_param_info_t pinfo;
+ f0r_get_param_info(&pinfo,i);
+
+ }
+
+ ret=this;
+ }
+ }else if (type == transition_type && info.plugin_type == F0R_PLUGIN_TYPE_MIXER2){
+ mlt_transition transition = mlt_transition_new( );
+ if ( transition != NULL )
+ {
+ transition->process = transition_process;
+ transition->close = transition_close;
+ properties=MLT_TRANSITION_PROPERTIES( transition );
+ mlt_properties_set_int(properties, "_transition_type", 1 );
+
+ ret=transition;
+ }
+ }
+ mlt_properties_set_data(properties, "_dlclose_handle", handle , sizeof (void*) , NULL , NULL );
+ mlt_properties_set_data(properties, "_dlclose", dlclose , sizeof (void*) , NULL , NULL );
+ mlt_properties_set_data(properties, "f0r_construct", f0r_construct , sizeof(void*),NULL,NULL);
+ mlt_properties_set_data(properties, "f0r_update", f0r_update , sizeof(void*),NULL,NULL);
+ if (f0r_update2)
+ mlt_properties_set_data(properties, "f0r_update2", f0r_update2 , sizeof(void*),NULL,NULL);
+ mlt_properties_set_data(properties, "f0r_destruct", f0r_destruct , sizeof(void*),NULL,NULL);
+ mlt_properties_set_data(properties, "f0r_get_plugin_info", f0r_get_plugin_info , sizeof(void*),NULL,NULL);
+ mlt_properties_set_data(properties, "f0r_get_param_info", f0r_get_param_info , sizeof(void*),NULL,NULL);
+ mlt_properties_set_data(properties, "f0r_set_param_value", f0r_set_param_value , sizeof(void*),NULL,NULL);
+ mlt_properties_set_data(properties, "f0r_get_param_value", f0r_get_param_value , sizeof(void*),NULL,NULL);
+
+
+ return ret;
+ }else{
+ printf("some was wrong\n");
+ dlerror();
+ }
+ return NULL;
+}
+
+static void * create_frei0r_item ( mlt_profile profile, mlt_service_type type, const char *id, void *arg){
+
+ mlt_tokeniser tokeniser = mlt_tokeniser_init ( );
+ int dircount=mlt_tokeniser_parse_new (
+ tokeniser,
+ getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH,
+ ":"
+ );
+ void* ret=NULL;
+ while (dircount--){
+ char soname[1024]="";
+
+ char *save_firstptr = NULL;
+ char *firstname=strtok_r(strdup(id),".",&save_firstptr);
+
+ firstname=strtok_r(NULL,".",&save_firstptr);
+ sprintf(soname,"%s/%s.so", mlt_tokeniser_get_string( tokeniser , dircount ) , firstname );
+
+ if (firstname){
+
+ void* handle=dlopen(soname,RTLD_LAZY);
+
+ if (handle ){
+ ret=load_lib ( profile , type , handle );
+ }else{
+ dlerror();
+ }
+ }
+ }
+ mlt_tokeniser_close ( tokeniser );
+ return ret;
+}
+
+
+MLT_REPOSITORY
+{
+ int i=0;
+ mlt_tokeniser tokeniser = mlt_tokeniser_init ( );
+ int dircount=mlt_tokeniser_parse_new (
+ tokeniser ,
+ getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH,
+ ":"
+ );
+
+ while (dircount--){
+
+ mlt_properties direntries = mlt_properties_new();
+ char* dirname = mlt_tokeniser_get_string ( tokeniser , dircount ) ;
+ mlt_properties_dir_list(direntries, dirname ,"*.so",1);
+
+ for (i=0;i<mlt_properties_count(direntries);i++){
+ char* name=mlt_properties_get_value(direntries,i);
+ char* shortname=name+strlen(dirname)+1;
+ char fname[1024]="";
+
+ strcat(fname,dirname);
+ strcat(fname,shortname);
+
+ char *save_firstptr = NULL;
+ char pluginname[1024]="frei0r.";
+ char* firstname = strtok_r ( shortname , "." , &save_firstptr );
+ strcat(pluginname,firstname);
+
+ void* handle=dlopen(strcat(name,".so"),RTLD_LAZY);
+ if (handle){
+ void (*plginfo)(f0r_plugin_info_t*)=dlsym(handle,"f0r_get_plugin_info");
+
+ if (plginfo){
+ f0r_plugin_info_t info;
+ plginfo(&info);
+ if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_SOURCE){
+ MLT_REGISTER( producer_type, pluginname, create_frei0r_item );
+ MLT_REGISTER_METADATA( producer_type, pluginname, fill_param_info, strdup(name) );
+ }
+ else if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_FILTER){
+ MLT_REGISTER( filter_type, pluginname, create_frei0r_item );
+ MLT_REGISTER_METADATA( filter_type, pluginname, fill_param_info, strdup(name) );
+ }
+ else if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_MIXER2 ){
+ MLT_REGISTER( transition_type, pluginname, create_frei0r_item );
+ MLT_REGISTER_METADATA( transition_type, pluginname, fill_param_info, strdup(name) );
+ }
+ }
+ dlclose(handle);
+ }
+ }
+ mlt_properties_close(direntries);
+ }
+ mlt_tokeniser_close ( tokeniser );
+}
--- /dev/null
+/*
+ * filter_frei0r.c -- frei0r filter
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+
+#include "frei0r_helper.h"
+#include <string.h>
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+ mlt_position in = mlt_filter_get_in( filter );
+ mlt_position out = mlt_filter_get_out( filter );
+ mlt_position time = mlt_frame_get_position( this );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+
+ process_frei0r_item( filter_type , position, MLT_FILTER_PROPERTIES ( filter ), this , image, format , width , height , writable );
+
+ }
+
+ return error;
+}
+
+
+mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+void filter_close( mlt_filter this ){
+
+ destruct( MLT_FILTER_PROPERTIES ( this ) );
+
+}
--- /dev/null
+/*
+ * frei0r_helper.c -- frei0r helper
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "frei0r_helper.h"
+#include <frei0r.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void parse_color( int color, f0r_param_color_t *fcolor )
+{
+ fcolor->r = ( color >> 24 ) & 0xff;
+ fcolor->r /= 255;
+ fcolor->g = ( color >> 16 ) & 0xff;
+ fcolor->g /= 255;
+ fcolor->b = ( color >> 8 ) & 0xff;
+ fcolor->b /= 255;
+}
+
+int process_frei0r_item( mlt_service_type type, double position , mlt_properties prop , mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ){
+
+ int i=0;
+ f0r_instance_t ( *f0r_construct ) ( unsigned int , unsigned int ) = mlt_properties_get_data( prop , "f0r_construct" ,NULL);
+ void (*f0r_update)(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)=mlt_properties_get_data( prop , "f0r_update" ,NULL);
+ void (*f0r_destruct)(f0r_instance_t instance)=mlt_properties_get_data( prop , "f0r_destruct" ,NULL);
+
+ void (*f0r_get_plugin_info)(f0r_plugin_info_t*)=mlt_properties_get_data( prop, "f0r_get_plugin_info" ,NULL);
+ void (*f0r_get_param_info)(f0r_param_info_t* info, int param_index)=mlt_properties_get_data( prop , "f0r_get_param_info" ,NULL);
+ void (*f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index)=mlt_properties_get_data( prop , "f0r_set_param_value" ,NULL);
+ void (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index)=mlt_properties_get_data( prop , "f0r_get_param_value" ,NULL);
+ void (*f0r_update2) (f0r_instance_t instance, double time,
+ const uint32_t* inframe1,const uint32_t* inframe2,const uint32_t* inframe3,
+ uint32_t* outframe)=mlt_properties_get_data( prop , "f0r_update2" ,NULL);
+
+
+ //use as name the width and height
+ f0r_instance_t inst;
+ char ctorname[1024]="";
+ sprintf(ctorname,"ctor-%dx%d",*width,*height);
+
+ void* neu=mlt_properties_get_data( prop , ctorname ,NULL );
+ if (!f0r_construct){
+ //printf("no ctor\n");
+ return -1;
+ }
+ if ( neu == 0 ){
+ inst= f0r_construct(*width,*height);
+ mlt_properties_set_data( prop , ctorname , inst, sizeof(void*) , f0r_destruct , NULL );;
+ }else{
+ inst=mlt_properties_get_data( prop , ctorname , NULL );
+ }
+ if (f0r_get_plugin_info){
+ f0r_plugin_info_t info;
+ f0r_get_plugin_info(&info);
+ for (i=0;i<info.num_params;i++){
+ f0r_param_info_t pinfo;
+ f0r_get_param_info(&pinfo,i);
+ mlt_geometry geom=mlt_geometry_init();
+ struct mlt_geometry_item_s item;
+ //set param if found
+
+ double t=0.0;
+ f0r_get_param_value(inst,&t,i);
+ char *val;
+ if (mlt_properties_get( prop , pinfo.name ) !=NULL ){
+ switch (pinfo.type) {
+ case F0R_PARAM_DOUBLE:
+ case F0R_PARAM_BOOL:
+ val=mlt_properties_get(prop, pinfo.name );
+ mlt_geometry_parse(geom,val,-1,-1,-1);
+ mlt_geometry_fetch(geom,&item,position);
+ t=item.x;
+ f0r_set_param_value(inst,&t,i);
+ break;
+ case F0R_PARAM_COLOR:
+ {
+ f0r_param_color_t color;
+ parse_color(mlt_properties_get_int(prop , pinfo.name), &color);
+ f0r_set_param_value(inst, &color, i);
+ break;
+ }
+ }
+ }
+
+ mlt_geometry_close(geom);
+ }
+ }
+
+ int video_area = *width * *height;
+ uint32_t *img_a;
+
+ if ( type != producer_type )
+ img_a = mlt_pool_alloc( video_area * sizeof(uint32_t) );
+ uint32_t *img_b = mlt_pool_alloc( video_area * sizeof(uint32_t) );
+
+ if (type==producer_type) {
+ f0r_update ( inst , position , NULL , img_b );
+ mlt_convert_rgb24a_to_yuv422((uint8_t *)img_b , *width, *height, *width * sizeof(uint32_t),*image, NULL);
+ } else if (type==filter_type) {
+ mlt_convert_yuv422_to_rgb24a(*image, (uint8_t *)img_a, video_area);
+ f0r_update ( inst , position , img_a , img_b );
+ mlt_convert_rgb24a_to_yuv422((uint8_t *)img_b , *width, *height, *width * sizeof(uint32_t),
+ *image, NULL );
+ } else if (type==transition_type && f0r_update2 ){
+ uint32_t *result = mlt_pool_alloc( video_area * sizeof(uint32_t) );
+
+ mlt_convert_yuv422_to_rgb24a ( image[0] , (uint8_t *)img_a , video_area );
+ mlt_convert_yuv422_to_rgb24a ( image[1] , (uint8_t *)img_b , video_area );
+ f0r_update2 ( inst , position , img_a , img_b , NULL , result );
+
+ uint8_t * image_ptr=mlt_properties_get_data(MLT_FRAME_PROPERTIES(this), "image", NULL );
+ if (image_ptr)
+ mlt_convert_rgb24a_to_yuv422((uint8_t *)result, *width, *height, *width * sizeof(uint32_t), image_ptr , NULL );
+
+ mlt_pool_release(result);
+ }
+ if ( type != producer_type ) mlt_pool_release(img_a);
+ mlt_pool_release(img_b);
+
+ return 0;
+}
+
+void destruct (mlt_properties prop ) {
+
+ void (*f0r_destruct)(f0r_instance_t instance)=mlt_properties_get_data( prop , "f0r_destruct" , NULL );
+ void (*f0r_deinit)(void)=mlt_properties_get_data ( prop , "f0r_deinit" , NULL);
+ int i=0;
+
+ if ( f0r_deinit != NULL )
+ f0r_deinit();
+
+ for ( i=0 ; i < mlt_properties_count ( prop ) ; i++ ){
+ if ( strstr ( mlt_properties_get_name ( prop , i ) , "ctor-" ) != NULL ){
+ void * inst=mlt_properties_get_data( prop , mlt_properties_get_name ( prop , i ) , NULL );
+ if ( inst != NULL ){
+ f0r_destruct( (f0r_instance_t) inst);
+ }
+ }
+ }
+ void (*dlclose)(void*)=mlt_properties_get_data ( prop , "_dlclose" , NULL);
+ void *handle=mlt_properties_get_data ( prop , "_dlclose_handle" , NULL);
+
+ if (handle && dlclose ){
+ dlclose ( handle );
+ }
+
+}
--- /dev/null
+/*
+ * frei0r_helper.h -- frei0r helper
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <framework/mlt.h>
+
+int process_frei0r_item( mlt_service_type type, double position , mlt_properties prop , mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable );
+void destruct (mlt_properties prop );
--- /dev/null
+/*
+ * producer_frei0r.c -- frei0r producer
+ * Copyright (c) 2009 Jean-Baptiste Mardelle <jb@kdenlive.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+
+#include "frei0r_helper.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer for this frame
+ mlt_producer producer = mlt_properties_get_data( properties, "producer_frei0r", NULL );
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Allocate the image
+ int size = *width * ( *height + 1 ) * 2;
+
+ // Allocate the image
+ *buffer = mlt_pool_alloc( size );
+
+ // Update the frame
+ mlt_properties_set_data( properties, "image", *buffer, size, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+
+ *format = mlt_image_yuv422;
+ if ( *buffer != NULL )
+ {
+ mlt_position in = mlt_producer_get_in( producer );
+ mlt_position out = mlt_producer_get_out( producer );
+ mlt_position time = mlt_frame_get_position( frame );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ process_frei0r_item( producer_type , position, producer_props, frame , buffer, format , width , height , writable );
+ }
+
+ return 0;
+}
+
+int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ if ( *frame != NULL )
+ {
+ // Obtain properties of frame and producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Set the producer on the frame properties
+ mlt_properties_set_data( properties, "producer_frei0r", producer, 0, NULL, NULL );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", 1 );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+
+ // Push the get_image method
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+void producer_close( mlt_producer producer )
+{
+ producer->close = NULL;
+ mlt_producer_close( producer );
+ free( producer );
+}
--- /dev/null
+/*
+ * transition_frei0r.c -- frei0r transition
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+#include "frei0r_helper.h"
+#include <string.h>
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ){
+
+ if (*format!=mlt_image_yuv422 ){
+ return -1;
+ }
+
+ mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+ mlt_transition transition = mlt_frame_pop_service( a_frame );
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ int invert = mlt_properties_get_int( properties, "invert" );
+
+ if ( mlt_properties_get( a_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+ mlt_properties_set( a_props, "rescale.interp", "nearest" );
+
+ // set consumer_aspect_ratio for a and b frame
+ if ( mlt_properties_get_double( a_props, "aspect_ratio" ) == 0.0 )
+ mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+ if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+ mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+ mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+ if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( b_props, "rescale.interp" ), "none" ) )
+ mlt_properties_set( b_props, "rescale.interp", "nearest" );
+
+ uint8_t *images[]={NULL,NULL,NULL};
+
+ mlt_frame_get_image( a_frame, &images[0], format, width, height, 1 );
+ mlt_frame_get_image( b_frame, &images[1], format, width, height, 1 );
+
+ mlt_position in = mlt_transition_get_in( transition );
+ mlt_position out = mlt_transition_get_out( transition );
+
+ // Get the position of the frame
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+ mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( a_frame ), name );
+
+ float pos=( float )( position - in ) / ( float )( out - in + 1 );
+
+ process_frei0r_item( transition_type , pos , properties, !invert ? a_frame : b_frame , images , format, width,height, writable );
+
+ *width = mlt_properties_get_int( !invert ? a_props : b_props, "width" );
+ *height = mlt_properties_get_int( !invert ? a_props : b_props, "height" );
+ *image = mlt_properties_get_data( !invert ? a_props : b_props , "image", NULL );
+ return 0;
+}
+
+mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
+{
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+ mlt_properties_set_position( MLT_FRAME_PROPERTIES( a_frame ), name, mlt_frame_get_position( a_frame ) );
+ mlt_frame_push_service( a_frame, transition );
+ mlt_frame_push_frame( a_frame, b_frame );
+ mlt_frame_push_get_image( a_frame, transition_get_image );
+ return a_frame;
+}
+
+void transition_close( mlt_transition this ){
+ destruct ( MLT_TRANSITION_PROPERTIES ( this ) );
+}
--- /dev/null
+include ../../../config.mak
+include config.mak
+
+TARGET = ../libmltgtk2$(LIBSUF)
+
+OBJS = factory.o
+
+CFLAGS += -I../..
+LDFLAGS += -L../../framework -lmlt
+
+ifdef USE_GTK2
+OBJS += consumer_gtk2.o
+CFLAGS += `pkg-config gtk+-2.0 --cflags`
+LDFLAGS += `pkg-config gtk+-2.0 --libs`
+endif
+
+ifdef USE_PIXBUF
+OBJS += producer_pixbuf.o pixops.o filter_rescale.o
+CFLAGS += `pkg-config gdk-pixbuf-2.0 --cflags`
+LDFLAGS += `pkg-config gdk-pixbuf-2.0 --libs`
+endif
+
+ifdef MMX_FLAGS
+ifndef ARCH_X86_64
+ASM_OBJS = have_mmx.o scale_line_22_yuv_mmx.o
+endif
+endif
+
+ifdef USE_PANGO
+OBJS += producer_pango.o
+CFLAGS += `pkg-config pangoft2 --cflags`
+LDFLAGS += `pkg-config pangoft2 --libs`
+endif
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS) $(ASM_OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(ASM_OBJS) $(LDFLAGS)
+
+have_mmx.o:
+ $(CC) -o $@ -c have_mmx.S
+
+scale_line_22_yuv_mmx.o: scale_line_22_yuv_mmx.S
+ $(CC) -o $@ -c scale_line_22_yuv_mmx.S
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(ASM_OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ pkg-config gtk+-2.0 2> /dev/null
+ disable_gtk2=$?
+
+ pkg-config gdk-pixbuf-2.0 2> /dev/null
+ disable_pixbuf=$?
+
+ pkg-config pangoft2 2> /dev/null
+ disable_pango=$?
+
+ if [ "$disable_gtk2" != "0" -a "$disable_pixbuf" != 0 -a "$disable_pango" != "0" ]
+ then
+ echo "- GTK2 components not found: disabling"
+ touch ../disable-gtk2
+ exit 0
+ fi
+
+ [ "$disable_gtk2" != "0" ] && echo "- gtk2 not found: gtk2 preview disabled"
+ [ "$disable_pixbuf" != "0" ] && echo "- pixbuf not found: pixbuf loader and rescaler disabled"
+ [ "$disable_pango" != "0" ] && echo "- pango not found: pango titler disabled"
+
+ echo > config.h
+ [ "$disable_gtk2" = "0" ] && echo "#define USE_GTK2" >> config.h
+ [ "$disable_pixbuf" = "0" ] && echo "#define USE_PIXBUF" >> config.h
+ [ "$disable_pango" = "0" ] && echo "#define USE_PANGO" >> config.h
+
+ echo > config.mak
+ [ "$disable_gtk2" = "0" ] && echo "USE_GTK2=1" >> config.mak
+ [ "$disable_pixbuf" = "0" ] && echo "USE_PIXBUF=1" >> config.mak
+ [ "$disable_pango" = "0" ] && echo "USE_PANGO=1" >> config.mak
+
+ exit 0
+fi
+
--- /dev/null
+/*
+ * consumer_gtk2.c -- A consumer for GTK2 apps
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_factory.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+mlt_consumer consumer_gtk2_preview_init( mlt_profile profile, GtkWidget *widget )
+{
+ // Create an sdl preview consumer
+ mlt_consumer consumer = NULL;
+
+ // This is a nasty little hack which is required by SDL
+ if ( widget != NULL )
+ {
+ Window xwin = GDK_WINDOW_XWINDOW( widget->window );
+ char windowhack[ 32 ];
+ sprintf( windowhack, "%ld", xwin );
+ setenv( "SDL_WINDOWID", windowhack, 1 );
+ }
+
+ // Create an sdl preview consumer
+ consumer = mlt_factory_consumer( profile, "sdl_preview", NULL );
+
+ // Now assign the lock/unlock callbacks
+ if ( consumer != NULL )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+ mlt_properties_set_int( properties, "app_locked", 1 );
+ mlt_properties_set_data( properties, "app_lock", gdk_threads_enter, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "app_unlock", gdk_threads_leave, 0, NULL, NULL );
+ }
+
+ return consumer;
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include <string.h>
+#include <framework/mlt.h>
+#include <gdk/gdk.h>
+
+#ifdef USE_PIXBUF
+extern mlt_producer producer_pixbuf_init( char *filename );
+extern mlt_filter filter_rescale_init( mlt_profile profile, char *arg );
+#endif
+
+#ifdef USE_GTK2
+extern mlt_consumer consumer_gtk2_preview_init( mlt_profile profile, void *widget );
+#endif
+
+#ifdef USE_PANGO
+extern mlt_producer producer_pango_init( const char *filename );
+#endif
+
+static void initialise( )
+{
+ static int init = 0;
+ if ( init == 0 )
+ {
+ init = 1;
+ g_type_init( );
+ }
+}
+
+void *create_service( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ initialise( );
+
+#ifdef USE_PIXBUF
+ if ( !strcmp( id, "pixbuf" ) )
+ return producer_pixbuf_init( arg );
+#endif
+
+#ifdef USE_PANGO
+ if ( !strcmp( id, "pango" ) )
+ return producer_pango_init( arg );
+#endif
+
+#ifdef USE_PIXBUF
+ if ( !strcmp( id, "gtkrescale" ) )
+ return filter_rescale_init( profile, arg );
+#endif
+
+#ifdef USE_GTK2
+ if ( !strcmp( id, "gtk2_preview" ) )
+ return consumer_gtk2_preview_init( profile, arg );
+#endif
+
+ return NULL;
+}
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( consumer_type, "gtk2_preview", create_service );
+ MLT_REGISTER( filter_type, "gtkrescale", create_service );
+ MLT_REGISTER( producer_type, "pango", create_service );
+ MLT_REGISTER( producer_type, "pixbuf", create_service );
+}
--- /dev/null
+/*
+ * filter_rescale.c -- scale the producer video frame size to match the consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "pixops.h"
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_factory.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight )
+{
+ // Get the properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Get the requested interpolation method
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+
+ // Convert to the GTK flag
+ int interp = PIXOPS_INTERP_BILINEAR;
+
+ if ( strcmp( interps, "nearest" ) == 0 )
+ interp = PIXOPS_INTERP_NEAREST;
+ else if ( strcmp( interps, "tiles" ) == 0 )
+ interp = PIXOPS_INTERP_TILES;
+ else if ( strcmp( interps, "hyper" ) == 0 )
+ interp = PIXOPS_INTERP_HYPER;
+
+ // Carry out the rescaling
+ if ( iformat == mlt_image_yuv422 && oformat == mlt_image_yuv422 )
+ {
+ // Create the output image
+ uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+ // Calculate strides
+ int istride = iwidth * 2;
+ int ostride = owidth * 2;
+
+ yuv422_scale_simple( output, owidth, oheight, ostride, *image, iwidth, iheight, istride, interp );
+
+ // Now update the frame
+ mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+
+ // Return the output
+ *image = output;
+ }
+ else if ( iformat == mlt_image_rgb24 || iformat == mlt_image_rgb24a )
+ {
+ int bpp = (iformat == mlt_image_rgb24a ? 4 : 3 );
+
+ // Create the yuv image
+ uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+ if ( strcmp( interps, "none" ) && ( iwidth != owidth || iheight != oheight ) )
+ {
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( *image, GDK_COLORSPACE_RGB,
+ ( iformat == mlt_image_rgb24a ), 8, iwidth, iheight,
+ iwidth * bpp, NULL, NULL );
+
+ GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf, owidth, oheight, interp );
+ g_object_unref( pixbuf );
+
+ // Extract YUV422 and alpha
+ if ( bpp == 4 )
+ {
+ // Allocate the alpha mask
+ uint8_t *alpha = mlt_pool_alloc( owidth * ( oheight + 1 ) );
+
+ // Convert the image and extract alpha
+ mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( scaled ), owidth, oheight, gdk_pixbuf_get_rowstride( scaled ), output, alpha );
+
+ mlt_properties_set_data( properties, "alpha", alpha, owidth * ( oheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+ }
+ else
+ {
+ // No alpha to extract
+ mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( scaled ), owidth, oheight, gdk_pixbuf_get_rowstride( scaled ), output );
+ }
+ g_object_unref( scaled );
+ }
+ else
+ {
+ // Extract YUV422 and alpha
+ if ( bpp == 4 )
+ {
+ // Allocate the alpha mask
+ uint8_t *alpha = mlt_pool_alloc( owidth * ( oheight + 1 ) );
+
+ // Convert the image and extract alpha
+ mlt_convert_rgb24a_to_yuv422( *image, owidth, oheight, owidth * 4, output, alpha );
+
+ mlt_properties_set_data( properties, "alpha", alpha, owidth * ( oheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+ }
+ else
+ {
+ // No alpha to extract
+ mlt_convert_rgb24_to_yuv422( *image, owidth, oheight, owidth * 3, output );
+ }
+ }
+
+ // Now update the frame
+ mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", owidth );
+ mlt_properties_set_int( properties, "height", oheight );
+
+ *image = output;
+ }
+
+ return 0;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_rescale_init( mlt_profile profile, char *arg )
+{
+ // Create a new scaler
+ mlt_filter this = mlt_factory_filter( profile, "rescale", arg );
+
+ // If successful, then initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Set the inerpolation
+ mlt_properties_set( properties, "interpolation", arg == NULL ? "bilinear" : arg );
+
+ // Set the method
+ mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL );
+ }
+
+ return this;
+}
+
--- /dev/null
+ .file "have_mmx.S"
+ .version "01.01"
+gcc2_compiled.:
+.text
+ .align 16
+
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)
+
+.globl pixops_have_mmx
+ .type pixops_have_mmx,@function
+pixops_have_mmx:
+
+#else
+
+.globl _pixops_have_mmx
+_pixops_have_mmx:
+
+#endif
+
+ push %ebx
+
+# Check if bit 21 in flags word is writeable
+
+ pushfl
+ popl %eax
+ movl %eax,%ebx
+ xorl $0x00200000, %eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+
+ cmpl %eax, %ebx
+
+ je .notfound
+
+# OK, we have CPUID
+
+ movl $1, %eax
+ cpuid
+
+ test $0x00800000, %edx
+ jz .notfound
+
+ movl $1, %eax
+ jmp .out
+
+.notfound:
+ movl $0, %eax
+.out:
+ popl %ebx
+ ret
+
--- /dev/null
+/* GdkPixbuf library - Scaling and compositing functions
+ *
+ * Original:
+ * Copyright (C) 2000 Red Hat, Inc
+ * Author: Owen Taylor <otaylor@redhat.com>
+ *
+ * Modification for MLT:
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <glib.h>
+#include <stdio.h>
+
+#include "pixops.h"
+
+#define SUBSAMPLE_BITS 4
+#define SUBSAMPLE (1 << SUBSAMPLE_BITS)
+#define SUBSAMPLE_MASK ((1 << SUBSAMPLE_BITS)-1)
+#define SCALE_SHIFT 16
+
+typedef struct _PixopsFilter PixopsFilter;
+typedef struct _PixopsFilterDimension PixopsFilterDimension;
+
+struct _PixopsFilterDimension
+{
+ int n;
+ double offset;
+ double *weights;
+};
+
+struct _PixopsFilter
+{
+ PixopsFilterDimension x;
+ PixopsFilterDimension y;
+ double overall_alpha;
+};
+
+typedef guchar *( *PixopsLineFunc ) ( int *weights, int n_x, int n_y,
+ guchar *dest, int dest_x, guchar *dest_end,
+ guchar **src,
+ int x_init, int x_step, int src_width );
+
+typedef void ( *PixopsPixelFunc ) ( guchar *dest, guint y1, guint cr, guint y2, guint cb );
+
+
+/* mmx function declarations */
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+guchar *pixops_scale_line_22_yuv_mmx ( guint32 weights[ 16 ][ 8 ], guchar *p, guchar *q1, guchar *q2, int x_step, guchar *p_stop, int x_init, int destx );
+int pixops_have_mmx ( void );
+#endif
+
+static inline int
+get_check_shift ( int check_size )
+{
+ int check_shift = 0;
+ g_return_val_if_fail ( check_size >= 0, 4 );
+
+ while ( !( check_size & 1 ) )
+ {
+ check_shift++;
+ check_size >>= 1;
+ }
+
+ return check_shift;
+}
+
+static inline void
+pixops_scale_nearest ( guchar *dest_buf,
+ int render_x0,
+ int render_y0,
+ int render_x1,
+ int render_y1,
+ int dest_rowstride,
+ const guchar *src_buf,
+ int src_width,
+ int src_height,
+ int src_rowstride,
+ double scale_x,
+ double scale_y )
+{
+ register int i, j;
+ register int x_step = ( 1 << SCALE_SHIFT ) / scale_x;
+ register int y_step = ( 1 << SCALE_SHIFT ) / scale_y;
+ register int x, x_scaled;
+
+ for ( i = 0; i < ( render_y1 - render_y0 ); i++ )
+ {
+ const guchar *src = src_buf + ( ( ( i + render_y0 ) * y_step + ( y_step >> 1 ) ) >> SCALE_SHIFT ) * src_rowstride;
+ guchar *dest = dest_buf + i * dest_rowstride;
+ x = render_x0 * x_step + ( x_step >> 1 );
+
+ for ( j = 0; j < ( render_x1 - render_x0 ); j++ )
+ {
+ x_scaled = x >> SCALE_SHIFT;
+ *dest++ = src[ x_scaled << 1 ];
+ *dest++ = src[ ( ( x_scaled >> 1 ) << 2 ) + ( ( j & 1 ) << 1 ) + 1 ];
+ x += x_step;
+ }
+ }
+}
+
+
+static inline guchar *
+scale_line ( int *weights, int n_x, int n_y,
+ guchar *dest, int dest_x, guchar *dest_end,
+ guchar **src,
+ int x_init, int x_step, int src_width )
+{
+ register int x = x_init;
+ register int i, j, x_scaled, y_index, uv_index;
+
+ while ( dest < dest_end )
+ {
+ unsigned int y = 0, uv = 0;
+ int *pixel_weights = weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * n_x * n_y;
+
+ x_scaled = x >> SCALE_SHIFT;
+ y_index = x_scaled << 1;
+ uv_index = ( ( x_scaled >> 1 ) << 2 ) + ( ( dest_x & 1 ) << 1 ) + 1;
+
+ for ( i = 0; i < n_y; i++ )
+ {
+ int *line_weights = pixel_weights + n_x * i;
+ guchar *q = src[ i ];
+
+ for ( j = 0; j < n_x; j ++ )
+ {
+ unsigned int ta = line_weights[ j ];
+
+ y += ta * q[ y_index ];
+ uv += ta * q[ uv_index ];
+ }
+ }
+
+ *dest++ = ( y + 0xffff ) >> SCALE_SHIFT;
+ *dest++ = ( uv + 0xffff ) >> SCALE_SHIFT;
+
+ x += x_step;
+ dest_x++;
+ }
+
+ return dest;
+}
+
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+static inline guchar *
+scale_line_22_yuv_mmx_stub ( int *weights, int n_x, int n_y,
+ guchar *dest, int dest_x, guchar *dest_end,
+ guchar **src,
+ int x_init, int x_step, int src_width )
+{
+ guint32 mmx_weights[ 16 ][ 8 ];
+ int j;
+
+ for ( j = 0; j < 16; j++ )
+ {
+ mmx_weights[ j ][ 0 ] = 0x00010001 * ( weights[ 4 * j ] >> 8 );
+ mmx_weights[ j ][ 1 ] = 0x00010001 * ( weights[ 4 * j ] >> 8 );
+ mmx_weights[ j ][ 2 ] = 0x00010001 * ( weights[ 4 * j + 1 ] >> 8 );
+ mmx_weights[ j ][ 3 ] = 0x00010001 * ( weights[ 4 * j + 1 ] >> 8 );
+ mmx_weights[ j ][ 4 ] = 0x00010001 * ( weights[ 4 * j + 2 ] >> 8 );
+ mmx_weights[ j ][ 5 ] = 0x00010001 * ( weights[ 4 * j + 2 ] >> 8 );
+ mmx_weights[ j ][ 6 ] = 0x00010001 * ( weights[ 4 * j + 3 ] >> 8 );
+ mmx_weights[ j ][ 7 ] = 0x00010001 * ( weights[ 4 * j + 3 ] >> 8 );
+ }
+
+ return pixops_scale_line_22_yuv_mmx ( mmx_weights, dest, src[ 0 ], src[ 1 ], x_step, dest_end, x_init, dest_x );
+}
+#endif /* USE_MMX */
+
+static inline guchar *
+scale_line_22_yuv ( int *weights, int n_x, int n_y,
+ guchar *dest, int dest_x, guchar *dest_end,
+ guchar **src,
+ int x_init, int x_step, int src_width )
+{
+ register int x = x_init;
+ register guchar *src0 = src[ 0 ];
+ register guchar *src1 = src[ 1 ];
+ register unsigned int p;
+ register guchar *q0, *q1;
+ register int w1, w2, w3, w4;
+ register int x_scaled, x_aligned, uv_index;
+
+ while ( dest < dest_end )
+ {
+ int *pixel_weights = weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * 4;
+
+ x_scaled = x >> SCALE_SHIFT;
+
+ w1 = pixel_weights[ 0 ];
+ w2 = pixel_weights[ 1 ];
+ w3 = pixel_weights[ 2 ];
+ w4 = pixel_weights[ 3 ];
+
+ /* process Y */
+ q0 = src0 + ( x_scaled << 1 );
+ q1 = src1 + ( x_scaled << 1 );
+ p = w1 * q0[ 0 ];
+ p += w2 * q0[ 2 ];
+ p += w3 * q1[ 0 ];
+ p += w4 * q1[ 2 ];
+ *dest++ = ( p + 0x8000 ) >> SCALE_SHIFT;
+
+ /* process U/V */
+ x_aligned = ( ( x_scaled >> 1 ) << 2 );
+ uv_index = ( ( dest_x & 1 ) << 1 ) + 1;
+
+ q0 = src0 + x_aligned;
+ q1 = src1 + x_aligned;
+ p = w1 * q0[ uv_index ];
+ p += w3 * q1[ uv_index ];
+ p += w2 * q0[ uv_index ];
+ p += w4 * q1[ uv_index ];
+
+ x += x_step;
+ dest_x ++;
+
+ *dest++ = ( p + 0x8000 ) >> SCALE_SHIFT;
+ }
+
+ return dest;
+}
+
+
+static inline void
+process_pixel ( int *weights, int n_x, int n_y,
+ guchar *dest, int dest_x, int dest_channels,
+ guchar **src, int src_channels,
+ int x_start, int src_width )
+{
+ register unsigned int y = 0, uv = 0;
+ register int i, j;
+ int uv_index = ( ( dest_x & 1 ) << 1 ) + 1;
+
+ for ( i = 0; i < n_y; i++ )
+ {
+ int *line_weights = weights + n_x * i;
+
+ for ( j = 0; j < n_x; j++ )
+ {
+ unsigned int ta = 0xff * line_weights[ j ];
+
+ if ( x_start + j < 0 )
+ {
+ y += ta * src[ i ][ 0 ];
+ uv += ta * src[ i ][ uv_index ];
+ }
+ else if ( x_start + j < src_width )
+ {
+ y += ta * src[ i ][ ( x_start + j ) << 1 ];
+ uv += ta * src[ i ][ ( ( ( x_start + j ) >> 1 ) << 2) + uv_index ];
+ }
+ else
+ {
+ y += ta * src[ i ][ ( src_width - 1 ) << 1 ];
+ uv += ta * src[ i ][ ( ( ( src_width - 1 ) >> 1 ) << 2) + uv_index ];
+ }
+ }
+ }
+
+ *dest++ = ( y + 0xffffff ) >> 24;
+ *dest++ = ( uv + 0xffffff ) >> 24;
+}
+
+
+static inline void
+correct_total ( int *weights,
+ int n_x,
+ int n_y,
+ int total,
+ double overall_alpha )
+{
+ int correction = ( int ) ( 0.5 + 65536 * overall_alpha ) - total;
+ int remaining, c, d, i;
+
+ if ( correction != 0 )
+ {
+ remaining = correction;
+ for ( d = 1, c = correction; c != 0 && remaining != 0; d++, c = correction / d )
+ for ( i = n_x * n_y - 1; i >= 0 && c != 0 && remaining != 0; i-- )
+ if ( *( weights + i ) + c >= 0 )
+ {
+ *( weights + i ) += c;
+ remaining -= c;
+ if ( ( 0 < remaining && remaining < c ) ||
+ ( 0 > remaining && remaining > c ) )
+ c = remaining;
+ }
+ }
+}
+
+
+static inline int *
+make_filter_table ( PixopsFilter *filter )
+{
+ int i_offset, j_offset;
+ int n_x = filter->x.n;
+ int n_y = filter->y.n;
+ int *weights = g_new ( int, SUBSAMPLE * SUBSAMPLE * n_x * n_y );
+
+ for ( i_offset = 0; i_offset < SUBSAMPLE; i_offset++ )
+ for ( j_offset = 0; j_offset < SUBSAMPLE; j_offset++ )
+ {
+ double weight;
+ int *pixel_weights = weights + ( ( i_offset * SUBSAMPLE ) + j_offset ) * n_x * n_y;
+ int total = 0;
+ int i, j;
+
+ for ( i = 0; i < n_y; i++ )
+ for ( j = 0; j < n_x; j++ )
+ {
+ weight = filter->x.weights[ ( j_offset * n_x ) + j ] *
+ filter->y.weights[ ( i_offset * n_y ) + i ] *
+ filter->overall_alpha * 65536 + 0.5;
+
+ total += ( int ) weight;
+
+ *( pixel_weights + n_x * i + j ) = weight;
+ }
+
+ correct_total ( pixel_weights, n_x, n_y, total, filter->overall_alpha );
+ }
+
+ return weights;
+}
+
+
+static inline void
+pixops_process ( guchar *dest_buf,
+ int render_x0,
+ int render_y0,
+ int render_x1,
+ int render_y1,
+ int dest_rowstride,
+ int dest_channels,
+ gboolean dest_has_alpha,
+ const guchar *src_buf,
+ int src_width,
+ int src_height,
+ int src_rowstride,
+ int src_channels,
+ gboolean src_has_alpha,
+ double scale_x,
+ double scale_y,
+ int check_x,
+ int check_y,
+ int check_size,
+ guint32 color1,
+ guint32 color2,
+ PixopsFilter *filter,
+ PixopsLineFunc line_func )
+{
+ int i, j;
+ int x, y; /* X and Y position in source (fixed_point) */
+
+ guchar **line_bufs = g_new ( guchar *, filter->y.n );
+ int *filter_weights = make_filter_table ( filter );
+
+ int x_step = ( 1 << SCALE_SHIFT ) / scale_x; /* X step in source (fixed point) */
+ int y_step = ( 1 << SCALE_SHIFT ) / scale_y; /* Y step in source (fixed point) */
+
+ int check_shift = check_size ? get_check_shift ( check_size ) : 0;
+
+ int scaled_x_offset = floor ( filter->x.offset * ( 1 << SCALE_SHIFT ) );
+
+ /* Compute the index where we run off the end of the source buffer. The furthest
+ * source pixel we access at index i is:
+ *
+ * ((render_x0 + i) * x_step + scaled_x_offset) >> SCALE_SHIFT + filter->x.n - 1
+ *
+ * So, run_end_index is the smallest i for which this pixel is src_width, i.e, for which:
+ *
+ * (i + render_x0) * x_step >= ((src_width - filter->x.n + 1) << SCALE_SHIFT) - scaled_x_offset
+ *
+ */
+#define MYDIV(a,b) ((a) > 0 ? (a) / (b) : ((a) - (b) + 1) / (b)) /* Division so that -1/5 = -1 */
+
+ int run_end_x = ( ( ( src_width - filter->x.n + 1 ) << SCALE_SHIFT ) - scaled_x_offset );
+ int run_end_index = MYDIV ( run_end_x + x_step - 1, x_step ) - render_x0;
+ run_end_index = MIN ( run_end_index, render_x1 - render_x0 );
+
+ y = render_y0 * y_step + floor ( filter->y.offset * ( 1 << SCALE_SHIFT ) );
+ for ( i = 0; i < ( render_y1 - render_y0 ); i++ )
+ {
+ int dest_x;
+ int y_start = y >> SCALE_SHIFT;
+ int x_start;
+ int *run_weights = filter_weights +
+ ( ( y >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) *
+ filter->x.n * filter->y.n * SUBSAMPLE;
+ guchar *new_outbuf;
+ guint32 tcolor1, tcolor2;
+
+ guchar *outbuf = dest_buf + dest_rowstride * i;
+ guchar *outbuf_end = outbuf + dest_channels * ( render_x1 - render_x0 );
+
+ if ( ( ( i + check_y ) >> check_shift ) & 1 )
+ {
+ tcolor1 = color2;
+ tcolor2 = color1;
+ }
+ else
+ {
+ tcolor1 = color1;
+ tcolor2 = color2;
+ }
+
+ for ( j = 0; j < filter->y.n; j++ )
+ {
+ if ( y_start < 0 )
+ line_bufs[ j ] = ( guchar * ) src_buf;
+ else if ( y_start < src_height )
+ line_bufs[ j ] = ( guchar * ) src_buf + src_rowstride * y_start;
+ else
+ line_bufs[ j ] = ( guchar * ) src_buf + src_rowstride * ( src_height - 1 );
+
+ y_start++;
+ }
+
+ dest_x = check_x;
+ x = render_x0 * x_step + scaled_x_offset;
+ x_start = x >> SCALE_SHIFT;
+
+ while ( x_start < 0 && outbuf < outbuf_end )
+ {
+ process_pixel ( run_weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * ( filter->x.n * filter->y.n ),
+ filter->x.n, filter->y.n,
+ outbuf, dest_x, dest_channels,
+ line_bufs, src_channels,
+ x >> SCALE_SHIFT, src_width );
+
+ x += x_step;
+ x_start = x >> SCALE_SHIFT;
+ dest_x++;
+ outbuf += dest_channels;
+ }
+
+ new_outbuf = ( *line_func ) ( run_weights, filter->x.n, filter->y.n,
+ outbuf, dest_x,
+ dest_buf + dest_rowstride * i + run_end_index * dest_channels,
+ line_bufs,
+ x, x_step, src_width );
+
+ dest_x += ( new_outbuf - outbuf ) / dest_channels;
+
+ x = ( dest_x - check_x + render_x0 ) * x_step + scaled_x_offset;
+ outbuf = new_outbuf;
+
+ while ( outbuf < outbuf_end )
+ {
+ process_pixel ( run_weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * ( filter->x.n * filter->y.n ),
+ filter->x.n, filter->y.n,
+ outbuf, dest_x, dest_channels,
+ line_bufs, src_channels,
+ x >> SCALE_SHIFT, src_width );
+
+ x += x_step;
+ dest_x++;
+ outbuf += dest_channels;
+ }
+
+ y += y_step;
+ }
+
+ g_free ( line_bufs );
+ g_free ( filter_weights );
+}
+
+
+/* Compute weights for reconstruction by replication followed by
+ * sampling with a box filter
+ */
+static inline void
+tile_make_weights ( PixopsFilterDimension *dim,
+ double scale )
+{
+ int n = ceil ( 1 / scale + 1 );
+ double *pixel_weights = g_new ( double, SUBSAMPLE * n );
+ int offset;
+ int i;
+
+ dim->n = n;
+ dim->offset = 0;
+ dim->weights = pixel_weights;
+
+ for ( offset = 0; offset < SUBSAMPLE; offset++ )
+ {
+ double x = ( double ) offset / SUBSAMPLE;
+ double a = x + 1 / scale;
+
+ for ( i = 0; i < n; i++ )
+ {
+ if ( i < x )
+ {
+ if ( i + 1 > x )
+ * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - x ) * scale;
+ else
+ *( pixel_weights++ ) = 0;
+ }
+ else
+ {
+ if ( a > i )
+ * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - i ) * scale;
+ else
+ *( pixel_weights++ ) = 0;
+ }
+ }
+ }
+}
+
+/* Compute weights for a filter that, for minification
+ * is the same as 'tiles', and for magnification, is bilinear
+ * reconstruction followed by a sampling with a delta function.
+ */
+static inline void
+bilinear_magnify_make_weights ( PixopsFilterDimension *dim,
+ double scale )
+{
+ double * pixel_weights;
+ int n;
+ int offset;
+ int i;
+
+ if ( scale > 1.0 ) /* Linear */
+ {
+ n = 2;
+ dim->offset = 0.5 * ( 1 / scale - 1 );
+ }
+ else /* Tile */
+ {
+ n = ceil ( 1.0 + 1.0 / scale );
+ dim->offset = 0.0;
+ }
+
+ dim->n = n;
+ dim->weights = g_new ( double, SUBSAMPLE * n );
+
+ pixel_weights = dim->weights;
+
+ for ( offset = 0; offset < SUBSAMPLE; offset++ )
+ {
+ double x = ( double ) offset / SUBSAMPLE;
+
+ if ( scale > 1.0 ) /* Linear */
+ {
+ for ( i = 0; i < n; i++ )
+ *( pixel_weights++ ) = ( ( ( i == 0 ) ? ( 1 - x ) : x ) / scale ) * scale;
+ }
+ else /* Tile */
+ {
+ double a = x + 1 / scale;
+
+ /* x
+ * ---------|--.-|----|--.-|------- SRC
+ * ------------|---------|--------- DEST
+ */
+ for ( i = 0; i < n; i++ )
+ {
+ if ( i < x )
+ {
+ if ( i + 1 > x )
+ * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - x ) * scale;
+ else
+ *( pixel_weights++ ) = 0;
+ }
+ else
+ {
+ if ( a > i )
+ * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - i ) * scale;
+ else
+ *( pixel_weights++ ) = 0;
+ }
+ }
+ }
+ }
+}
+
+/* Computes the integral from b0 to b1 of
+ *
+ * f(x) = x; 0 <= x < 1
+ * f(x) = 0; otherwise
+ *
+ * We combine two of these to compute the convolution of
+ * a box filter with a triangular spike.
+ */
+static inline double
+linear_box_half ( double b0, double b1 )
+{
+ double a0, a1;
+ double x0, x1;
+
+ a0 = 0.;
+ a1 = 1.;
+
+ if ( a0 < b0 )
+ {
+ if ( a1 > b0 )
+ {
+ x0 = b0;
+ x1 = MIN ( a1, b1 );
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ if ( b1 > a0 )
+ {
+ x0 = a0;
+ x1 = MIN ( a1, b1 );
+ }
+ else
+ return 0;
+ }
+
+ return 0.5 * ( x1 * x1 - x0 * x0 );
+}
+
+/* Compute weights for reconstructing with bilinear
+ * interpolation, then sampling with a box filter
+ */
+static inline void
+bilinear_box_make_weights ( PixopsFilterDimension *dim,
+ double scale )
+{
+ int n = ceil ( 1 / scale + 2.0 );
+ double *pixel_weights = g_new ( double, SUBSAMPLE * n );
+ double w;
+ int offset, i;
+
+ dim->offset = -1.0;
+ dim->n = n;
+ dim->weights = pixel_weights;
+
+ for ( offset = 0 ; offset < SUBSAMPLE; offset++ )
+ {
+ double x = ( double ) offset / SUBSAMPLE;
+ double a = x + 1 / scale;
+
+ for ( i = 0; i < n; i++ )
+ {
+ w = linear_box_half ( 0.5 + i - a, 0.5 + i - x );
+ w += linear_box_half ( 1.5 + x - i, 1.5 + a - i );
+
+ *( pixel_weights++ ) = w * scale;
+ }
+ }
+}
+
+
+static inline void
+make_weights ( PixopsFilter *filter,
+ PixopsInterpType interp_type,
+ double scale_x,
+ double scale_y )
+{
+ switch ( interp_type )
+ {
+ case PIXOPS_INTERP_NEAREST:
+ g_assert_not_reached ();
+ break;
+
+ case PIXOPS_INTERP_TILES:
+ tile_make_weights ( &filter->x, scale_x );
+ tile_make_weights ( &filter->y, scale_y );
+ break;
+
+ case PIXOPS_INTERP_BILINEAR:
+ bilinear_magnify_make_weights ( &filter->x, scale_x );
+ bilinear_magnify_make_weights ( &filter->y, scale_y );
+ break;
+
+ case PIXOPS_INTERP_HYPER:
+ bilinear_box_make_weights ( &filter->x, scale_x );
+ bilinear_box_make_weights ( &filter->y, scale_y );
+ break;
+ }
+}
+
+
+void
+yuv422_scale ( guchar *dest_buf,
+ int render_x0,
+ int render_y0,
+ int render_x1,
+ int render_y1,
+ int dest_rowstride,
+ int dest_channels,
+ gboolean dest_has_alpha,
+ const guchar *src_buf,
+ int src_width,
+ int src_height,
+ int src_rowstride,
+ int src_channels,
+ gboolean src_has_alpha,
+ double scale_x,
+ double scale_y,
+ PixopsInterpType interp_type )
+{
+ PixopsFilter filter = { { 0, 0, 0}, { 0, 0, 0 }, 0 };
+ PixopsLineFunc line_func;
+
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+ gboolean found_mmx = pixops_have_mmx();
+#endif
+
+ //g_return_if_fail ( !( dest_channels == 3 && dest_has_alpha ) );
+ //g_return_if_fail ( !( src_channels == 3 && src_has_alpha ) );
+ //g_return_if_fail ( !( src_has_alpha && !dest_has_alpha ) );
+
+ if ( scale_x == 0 || scale_y == 0 )
+ return ;
+
+ if ( interp_type == PIXOPS_INTERP_NEAREST )
+ {
+ pixops_scale_nearest ( dest_buf, render_x0, render_y0, render_x1, render_y1,
+ dest_rowstride,
+ src_buf, src_width, src_height, src_rowstride,
+ scale_x, scale_y );
+ return;
+ }
+
+ filter.overall_alpha = 1.0;
+ make_weights ( &filter, interp_type, scale_x, scale_y );
+
+ if ( filter.x.n == 2 && filter.y.n == 2 )
+ {
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+ if ( found_mmx )
+ {
+ //fprintf( stderr, "rescale: using mmx\n" );
+ line_func = scale_line_22_yuv_mmx_stub;
+ }
+ else
+#endif
+
+ line_func = scale_line_22_yuv;
+ }
+ else
+ line_func = scale_line;
+
+ pixops_process ( dest_buf, render_x0, render_y0, render_x1, render_y1,
+ dest_rowstride, dest_channels, dest_has_alpha,
+ src_buf, src_width, src_height, src_rowstride, src_channels,
+ src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0,
+ &filter, line_func );
+
+ g_free ( filter.x.weights );
+ g_free ( filter.y.weights );
+}
+
--- /dev/null
+/* GdkPixbuf library - Scaling and compositing functions
+ *
+ * Original:
+ * Copyright (C) 2000 Red Hat, Inc
+ * Author: Owen Taylor <otaylor@redhat.com>
+ *
+ * Modification for MLT:
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PIXOPS_H
+#define PIXOPS_H
+
+#include <glib.h>
+
+/* Interpolation modes; must match GdkInterpType */
+typedef enum {
+ PIXOPS_INTERP_NEAREST,
+ PIXOPS_INTERP_TILES,
+ PIXOPS_INTERP_BILINEAR,
+ PIXOPS_INTERP_HYPER
+} PixopsInterpType;
+
+/* Scale src_buf from src_width / src_height by factors scale_x, scale_y
+ * and composite the portion corresponding to
+ * render_x, render_y, render_width, render_height in the new
+ * coordinate system into dest_buf starting at 0, 0
+ */
+void yuv422_scale (guchar *dest_buf,
+ int render_x0,
+ int render_y0,
+ int render_x1,
+ int render_y1,
+ int dest_rowstride,
+ int dest_channels,
+ int dest_has_alpha,
+ const guchar *src_buf,
+ int src_width,
+ int src_height,
+ int src_rowstride,
+ int src_channels,
+ int src_has_alpha,
+ double scale_x,
+ double scale_y,
+ PixopsInterpType interp_type);
+
+#define yuv422_scale_simple( dest_buf, dest_width, dest_height, dest_rowstride, src_buf, src_width, src_height, src_rowstride, interp_type ) \
+ yuv422_scale( (dest_buf), 0, 0, \
+ (dest_width), (dest_height), \
+ (dest_rowstride), 2, 0, \
+ (src_buf), (src_width), (src_height), \
+ (src_rowstride), 2, 0, \
+ (double) (dest_width) / (src_width), (double) (dest_height) / (src_height), \
+ (PixopsInterpType) interp_type );
+
+#endif
--- /dev/null
+/*
+ * producer_pango.c -- a pango-based titler
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_geometry.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <pango/pangoft2.h>
+#include <freetype/freetype.h>
+#include <iconv.h>
+#include <pthread.h>
+#include <ctype.h>
+
+typedef struct producer_pango_s *producer_pango;
+
+typedef enum
+{
+ pango_align_left = 0,
+ pango_align_center,
+ pango_align_right
+} pango_align;
+
+static pthread_mutex_t pango_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct producer_pango_s
+{
+ struct mlt_producer_s parent;
+ int width;
+ int height;
+ uint8_t *image;
+ uint8_t *alpha;
+ char *fgcolor;
+ char *bgcolor;
+ int align;
+ int pad;
+ char *markup;
+ char *text;
+ char *font;
+ int weight;
+};
+
+// special color type used by internal pango routines
+typedef struct
+{
+ uint8_t r, g, b, a;
+} rgba_color;
+
+// Forward declarations
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg );
+static GdkPixbuf *pango_get_pixbuf( const char *markup, const char *text, const char *font,
+ rgba_color fg, rgba_color bg, int pad, int align, int weight, int size );
+
+/** Return nonzero if the two strings are equal, ignoring case, up to
+ the first n characters.
+*/
+int strncaseeq(const char *s1, const char *s2, size_t n)
+{
+ for ( ; n > 0; n--)
+ {
+ if (tolower(*s1++) != tolower(*s2++))
+ return 0;
+ }
+ return 1;
+}
+
+/** Parse the alignment property.
+*/
+
+static int alignment_parse( char* align )
+{
+ int ret = pango_align_left;
+
+ if ( align == NULL );
+ else if ( isdigit( align[ 0 ] ) )
+ ret = atoi( align );
+ else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
+ ret = pango_align_center;
+ else if ( align[ 0 ] == 'r' )
+ ret = pango_align_right;
+
+ return ret;
+}
+
+static PangoFT2FontMap *fontmap = NULL;
+
+mlt_producer producer_pango_init( const char *filename )
+{
+ producer_pango this = calloc( sizeof( struct producer_pango_s ), 1 );
+ if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+ {
+ mlt_producer producer = &this->parent;
+
+ pthread_mutex_lock( &pango_mutex );
+ if ( fontmap == NULL )
+ fontmap = (PangoFT2FontMap*) pango_ft2_font_map_new();
+ g_type_init();
+ pthread_mutex_unlock( &pango_mutex );
+
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Get the properties interface
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+
+ // Set the default properties
+ mlt_properties_set( properties, "fgcolour", "0xffffffff" );
+ mlt_properties_set( properties, "bgcolour", "0x00000000" );
+ mlt_properties_set_int( properties, "align", pango_align_left );
+ mlt_properties_set_int( properties, "pad", 0 );
+ mlt_properties_set( properties, "text", "" );
+ mlt_properties_set( properties, "font", "Sans 48" );
+ mlt_properties_set( properties, "encoding", "UTF-8" );
+ mlt_properties_set_int( properties, "weight", PANGO_WEIGHT_NORMAL );
+
+ if ( filename == NULL )
+ {
+ mlt_properties_set( properties, "markup", "" );
+ }
+ else if ( filename[ 0 ] == '+' || strstr( filename, "/+" ) )
+ {
+ char *copy = strdup( filename + 1 );
+ char *markup = copy;
+ if ( strstr( markup, "/+" ) )
+ markup = strstr( markup, "/+" ) + 2;
+ ( *strrchr( markup, '.' ) ) = '\0';
+ while ( strchr( markup, '~' ) )
+ ( *strchr( markup, '~' ) ) = '\n';
+ mlt_properties_set( properties, "resource", filename );
+ mlt_properties_set( properties, "markup", markup );
+ free( copy );
+ }
+ else if ( strstr( filename, ".mpl" ) )
+ {
+ int i = 0;
+ mlt_properties contents = mlt_properties_load( filename );
+ mlt_geometry key_frames = mlt_geometry_init( );
+ struct mlt_geometry_item_s item;
+ mlt_properties_set( properties, "resource", filename );
+ mlt_properties_set_data( properties, "contents", contents, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_properties_set_data( properties, "key_frames", key_frames, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+
+ // Make sure we have at least one entry
+ if ( mlt_properties_get( contents, "0" ) == NULL )
+ mlt_properties_set( contents, "0", "" );
+
+ for ( i = 0; i < mlt_properties_count( contents ); i ++ )
+ {
+ char *name = mlt_properties_get_name( contents, i );
+ char *value = mlt_properties_get_value( contents, i );
+ while ( value != NULL && strchr( value, '~' ) )
+ ( *strchr( value, '~' ) ) = '\n';
+ item.frame = atoi( name );
+ mlt_geometry_insert( key_frames, &item );
+ }
+ }
+ else
+ {
+ FILE *f = fopen( filename, "r" );
+ if ( f != NULL )
+ {
+ char line[81];
+ char *markup = NULL;
+ size_t size = 0;
+ line[80] = '\0';
+
+ while ( fgets( line, 80, f ) )
+ {
+ size += strlen( line ) + 1;
+ if ( markup )
+ {
+ markup = realloc( markup, size );
+ strcat( markup, line );
+ }
+ else
+ {
+ markup = strdup( line );
+ }
+ }
+ fclose( f );
+
+ if ( markup[ strlen( markup ) - 1 ] == '\n' )
+ markup[ strlen( markup ) - 1 ] = '\0';
+
+ mlt_properties_set( properties, "resource", filename );
+ mlt_properties_set( properties, "markup", ( markup == NULL ? "" : markup ) );
+ free( markup );
+ }
+ else
+ {
+ mlt_properties_set( properties, "markup", "" );
+ }
+ }
+
+ return producer;
+ }
+ free( this );
+ return NULL;
+}
+
+static void set_string( char **string, const char *value, const char *fallback )
+{
+ if ( value != NULL )
+ {
+ free( *string );
+ *string = strdup( value );
+ }
+ else if ( *string == NULL && fallback != NULL )
+ {
+ *string = strdup( fallback );
+ }
+ else if ( *string != NULL && fallback == NULL )
+ {
+ free( *string );
+ *string = NULL;
+ }
+}
+
+rgba_color parse_color( char *color )
+{
+ rgba_color result = { 0xff, 0xff, 0xff, 0xff };
+
+ if ( !strncmp( color, "0x", 2 ) )
+ {
+ unsigned int temp = 0;
+ sscanf( color + 2, "%x", &temp );
+ result.r = ( temp >> 24 ) & 0xff;
+ result.g = ( temp >> 16 ) & 0xff;
+ result.b = ( temp >> 8 ) & 0xff;
+ result.a = ( temp ) & 0xff;
+ }
+ else if ( !strcmp( color, "red" ) )
+ {
+ result.r = 0xff;
+ result.g = 0x00;
+ result.b = 0x00;
+ }
+ else if ( !strcmp( color, "green" ) )
+ {
+ result.r = 0x00;
+ result.g = 0xff;
+ result.b = 0x00;
+ }
+ else if ( !strcmp( color, "blue" ) )
+ {
+ result.r = 0x00;
+ result.g = 0x00;
+ result.b = 0xff;
+ }
+ else
+ {
+ unsigned int temp = 0;
+ sscanf( color, "%d", &temp );
+ result.r = ( temp >> 24 ) & 0xff;
+ result.g = ( temp >> 16 ) & 0xff;
+ result.b = ( temp >> 8 ) & 0xff;
+ result.a = ( temp ) & 0xff;
+ }
+
+ return result;
+}
+
+/** Convert a string property to UTF-8
+*/
+static int iconv_utf8( mlt_properties properties, const char *prop_name, const char* encoding )
+{
+ char *text = mlt_properties_get( properties, prop_name );
+ int result = -1;
+
+ iconv_t cd = iconv_open( "UTF-8", encoding );
+ if ( cd != ( iconv_t )-1 )
+ {
+ char *inbuf_p = text;
+ size_t inbuf_n = strlen( text );
+ size_t outbuf_n = inbuf_n * 6;
+ char *outbuf = mlt_pool_alloc( outbuf_n );
+ char *outbuf_p = outbuf;
+
+ memset( outbuf, 0, outbuf_n );
+
+ if ( text != NULL && strcmp( text, "" ) && iconv( cd, &inbuf_p, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 )
+ mlt_properties_set( properties, prop_name, outbuf );
+ else
+ mlt_properties_set( properties, prop_name, "" );
+
+ mlt_pool_release( outbuf );
+ iconv_close( cd );
+ result = 0;
+ }
+ return result;
+}
+
+static void refresh_image( mlt_frame frame, int width, int height )
+{
+ // Pixbuf
+ GdkPixbuf *pixbuf = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "pixbuf", NULL );
+
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer pango for this frame
+ producer_pango this = mlt_properties_get_data( properties, "producer_pango", NULL );
+
+ // Obtain the producer
+ mlt_producer producer = &this->parent;
+
+ // Obtain the producer properties
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Get producer properties
+ char *fg = mlt_properties_get( producer_props, "fgcolour" );
+ char *bg = mlt_properties_get( producer_props, "bgcolour" );
+ int align = alignment_parse( mlt_properties_get( producer_props, "align" ) );
+ int pad = mlt_properties_get_int( producer_props, "pad" );
+ char *markup = mlt_properties_get( producer_props, "markup" );
+ char *text = mlt_properties_get( producer_props, "text" );
+ char *font = mlt_properties_get( producer_props, "font" );
+ char *encoding = mlt_properties_get( producer_props, "encoding" );
+ int weight = mlt_properties_get_int( producer_props, "weight" );
+ int size = mlt_properties_get_int( producer_props, "size" );
+ int property_changed = 0;
+
+ if ( pixbuf == NULL )
+ {
+ // Check for file support
+ int position = mlt_properties_get_position( properties, "pango_position" );
+ mlt_properties contents = mlt_properties_get_data( producer_props, "contents", NULL );
+ mlt_geometry key_frames = mlt_properties_get_data( producer_props, "key_frames", NULL );
+ struct mlt_geometry_item_s item;
+ if ( contents != NULL )
+ {
+ char temp[ 20 ];
+ mlt_geometry_prev_key( key_frames, &item, position );
+ sprintf( temp, "%d", item.frame );
+ markup = mlt_properties_get( contents, temp );
+ }
+
+ // See if any properties changed
+ property_changed = ( align != this->align );
+ property_changed = property_changed || ( this->fgcolor == NULL || ( fg && strcmp( fg, this->fgcolor ) ) );
+ property_changed = property_changed || ( this->bgcolor == NULL || ( bg && strcmp( bg, this->bgcolor ) ) );
+ property_changed = property_changed || ( pad != this->pad );
+ property_changed = property_changed || ( markup && this->markup && strcmp( markup, this->markup ) );
+ property_changed = property_changed || ( text && this->text && strcmp( text, this->text ) );
+ property_changed = property_changed || ( font && this->font && strcmp( font, this->font ) );
+ property_changed = property_changed || ( weight != this->weight );
+
+ // Save the properties for next comparison
+ this->align = align;
+ this->pad = pad;
+ set_string( &this->fgcolor, fg, "0xffffffff" );
+ set_string( &this->bgcolor, bg, "0x00000000" );
+ set_string( &this->markup, markup, NULL );
+ set_string( &this->text, text, NULL );
+ set_string( &this->font, font, "Sans 48" );
+ this->weight = weight;
+ }
+
+ if ( pixbuf == NULL && property_changed )
+ {
+ rgba_color fgcolor = parse_color( this->fgcolor );
+ rgba_color bgcolor = parse_color( this->bgcolor );
+
+ mlt_pool_release( this->image );
+ mlt_pool_release( this->alpha );
+ this->image = NULL;
+ this->alpha = NULL;
+
+ // Convert from specified encoding to UTF-8
+ if ( encoding != NULL && !strncaseeq( encoding, "utf-8", 5 ) && !strncaseeq( encoding, "utf8", 4 ) )
+ {
+ if ( markup != NULL && iconv_utf8( producer_props, "markup", encoding ) != -1 )
+ {
+ markup = mlt_properties_get( producer_props, "markup" );
+ set_string( &this->markup, markup, NULL );
+ }
+ if ( text != NULL && iconv_utf8( producer_props, "text", encoding ) != -1 )
+ {
+ text = mlt_properties_get( producer_props, "text" );
+ set_string( &this->text, text, NULL );
+ }
+ }
+
+ // Render the title
+ pixbuf = pango_get_pixbuf( markup, text, font, fgcolor, bgcolor, pad, align, weight, size );
+
+ if ( pixbuf != NULL )
+ {
+ // Register this pixbuf for destruction and reuse
+ mlt_properties_set_data( producer_props, "pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref, NULL );
+ g_object_ref( pixbuf );
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref, NULL );
+
+ mlt_properties_set_int( producer_props, "real_width", gdk_pixbuf_get_width( pixbuf ) );
+ mlt_properties_set_int( producer_props, "real_height", gdk_pixbuf_get_height( pixbuf ) );
+
+ // Store the width/height of the pixbuf temporarily
+ this->width = gdk_pixbuf_get_width( pixbuf );
+ this->height = gdk_pixbuf_get_height( pixbuf );
+ }
+ }
+ else if ( pixbuf == NULL && ( width > 0 && ( this->image == NULL || width != this->width || height != this->height ) ) )
+ {
+ mlt_pool_release( this->image );
+ mlt_pool_release( this->alpha );
+ this->image = NULL;
+ this->alpha = NULL;
+
+ pixbuf = mlt_properties_get_data( producer_props, "pixbuf", NULL );
+ }
+
+ // If we have a pixbuf and a valid width
+ if ( pixbuf && width > 0 )
+ {
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+ int interp = GDK_INTERP_BILINEAR;
+
+ if ( strcmp( interps, "nearest" ) == 0 )
+ interp = GDK_INTERP_NEAREST;
+ else if ( strcmp( interps, "tiles" ) == 0 )
+ interp = GDK_INTERP_TILES;
+ else if ( strcmp( interps, "hyper" ) == 0 )
+ interp = GDK_INTERP_HYPER;
+
+// fprintf(stderr,"%s: scaling from %dx%d to %dx%d\n", __FILE__, this->width, this->height, width, height);
+
+ // Note - the original pixbuf is already safe and ready for destruction
+ pixbuf = gdk_pixbuf_scale_simple( pixbuf, width, height, interp );
+
+ // Store width and height
+ this->width = width;
+ this->height = height;
+
+ // Allocate/define image
+ this->image = mlt_pool_alloc( width * ( height + 1 ) * 2 );
+ this->alpha = mlt_pool_alloc( this->width * this->height );
+
+ // Convert the image
+ mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
+ this->width, this->height,
+ gdk_pixbuf_get_rowstride( pixbuf ),
+ this->image, this->alpha );
+
+ // Finished with pixbuf now
+ g_object_unref( pixbuf );
+ }
+
+ // Set width/height
+ mlt_properties_set_int( properties, "width", this->width );
+ mlt_properties_set_int( properties, "height", this->height );
+ mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "real_width" ) );
+ mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "real_height" ) );
+
+ // pass the image data without destructor
+ mlt_properties_set_data( properties, "image", this->image, this->width * ( this->height + 1 ) * 2, NULL, NULL );
+ mlt_properties_set_data( properties, "alpha", this->alpha, this->width * this->height, NULL, NULL );
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // We need to know the size of the image to clone it
+ int image_size = 0;
+ int alpha_size = 0;
+
+ // Alpha channel
+ uint8_t *alpha = NULL;
+
+ *width = mlt_properties_get_int( properties, "rescale_width" );
+ *height = mlt_properties_get_int( properties, "rescale_height" );
+
+ // Refresh the image
+ pthread_mutex_lock( &pango_mutex );
+ refresh_image( frame, *width, *height );
+
+ // Get the image
+ *buffer = mlt_properties_get_data( properties, "image", &image_size );
+ alpha = mlt_properties_get_data( properties, "alpha", &alpha_size );
+
+ // Get width and height
+ *width = mlt_properties_get_int( properties, "width" );
+ *height = mlt_properties_get_int( properties, "height" );
+
+ // Always clone here to allow 'animated' text
+ if ( *buffer != NULL )
+ {
+ // Clone the image and the alpha
+ uint8_t *image_copy = mlt_pool_alloc( image_size );
+ uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+ memcpy( image_copy, *buffer, image_size );
+
+ // Copy or default the alpha
+ if ( alpha != NULL )
+ memcpy( alpha_copy, alpha, alpha_size );
+ else
+ memset( alpha_copy, 255, alpha_size );
+
+ // Now update properties so we free the copy after
+ mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+ // We're going to pass the copy on
+ *buffer = image_copy;
+ }
+ else
+ {
+ // TODO: Review all cases of invalid images
+ *buffer = mlt_pool_alloc( 50 * 50 * 2 );
+ mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
+ *width = 50;
+ *height = 50;
+ }
+
+ pthread_mutex_unlock( &pango_mutex );
+
+ return 0;
+}
+
+static uint8_t *producer_get_alpha_mask( mlt_frame this )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Return the alpha mask
+ return mlt_properties_get_data( properties, "alpha", NULL );
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ producer_pango this = producer->child;
+
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ // Obtain properties of frame and producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Set the producer on the frame properties
+ mlt_properties_set_data( properties, "producer_pango", this, 0, NULL, NULL );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+ mlt_properties_set_position( properties, "pango_position", mlt_producer_frame( producer ) );
+
+ // Refresh the pango image
+ pthread_mutex_lock( &pango_mutex );
+ refresh_image( *frame, 0, 0 );
+ pthread_mutex_unlock( &pango_mutex );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", 1 );
+ mlt_properties_set_double( properties, "aspect_ratio", 1 );
+
+ // Set alpha call back
+ ( *frame )->get_alpha_mask = producer_get_alpha_mask;
+
+ // Stack the get image callback
+ mlt_frame_push_get_image( *frame, producer_get_image );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ producer_pango this = parent->child;
+ mlt_pool_release( this->image );
+ mlt_pool_release( this->alpha );
+ free( this->fgcolor );
+ free( this->bgcolor );
+ free( this->markup );
+ free( this->text );
+ free( this->font );
+ parent->close = NULL;
+ mlt_producer_close( parent );
+ free( this );
+}
+
+static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg )
+{
+ int ww = gdk_pixbuf_get_width( pixbuf );
+ int hh = gdk_pixbuf_get_height( pixbuf );
+ uint8_t *p = gdk_pixbuf_get_pixels( pixbuf );
+ int i, j;
+
+ for ( j = 0; j < hh; j++ )
+ {
+ for ( i = 0; i < ww; i++ )
+ {
+ *p++ = bg.r;
+ *p++ = bg.g;
+ *p++ = bg.b;
+ *p++ = bg.a;
+ }
+ }
+}
+
+static GdkPixbuf *pango_get_pixbuf( const char *markup, const char *text, const char *font, rgba_color fg, rgba_color bg, int pad, int align, int weight, int size )
+{
+ PangoContext *context = pango_ft2_font_map_create_context( fontmap );
+ PangoLayout *layout = pango_layout_new( context );
+ int w, h, x;
+ int i, j;
+ GdkPixbuf *pixbuf = NULL;
+ FT_Bitmap bitmap;
+ uint8_t *src = NULL;
+ uint8_t* dest = NULL;
+ uint8_t *d, *s, a;
+ int stride;
+ PangoFontDescription *desc = pango_font_description_from_string( font );
+
+ pango_ft2_font_map_set_resolution( fontmap, 72, 72 );
+ pango_layout_set_width( layout, -1 ); // set wrapping constraints
+ pango_font_description_set_weight( desc, ( PangoWeight ) weight );
+ if ( size != 0 )
+ pango_font_description_set_absolute_size( desc, PANGO_SCALE * size );
+ pango_layout_set_font_description( layout, desc );
+// pango_layout_set_spacing( layout, space );
+ pango_layout_set_alignment( layout, ( PangoAlignment ) align );
+ if ( markup != NULL && strcmp( markup, "" ) != 0 )
+ {
+ pango_layout_set_markup( layout, markup, strlen( markup ) );
+ }
+ else if ( text != NULL && strcmp( text, "" ) != 0 )
+ {
+ // Replace all ~'s with a line feed (silly convention, but handy)
+ char *copy = strdup( text );
+ while ( strchr( copy, '~' ) )
+ ( *strchr( copy, '~' ) ) = '\n';
+ pango_layout_set_text( layout, copy, strlen( copy ) );
+ free( copy );
+ }
+ else
+ {
+ // Pango doesn't like empty strings
+ pango_layout_set_text( layout, " ", 2 );
+ }
+ pango_layout_get_pixel_size( layout, &w, &h );
+
+ // Interpret size property as an absolute pixel height and compensate for
+ // freetype's "interpretation" of our absolute size request. This gives
+ // precise control over compositing and better quality by reducing scaling
+ // artifacts with composite geometries that constrain the dimensions.
+ // If you do not want this, then put the size in the font property or in
+ // the pango markup.
+ if ( size != 0 )
+ {
+ pango_font_description_set_absolute_size( desc, PANGO_SCALE * size * size/h );
+ pango_layout_set_font_description( layout, desc );
+ pango_layout_get_pixel_size( layout, &w, &h );
+ }
+
+ pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE /* has alpha */, 8, w + 2 * pad, h + 2 * pad );
+ pango_draw_background( pixbuf, bg );
+
+ stride = gdk_pixbuf_get_rowstride( pixbuf );
+
+ bitmap.width = w;
+ bitmap.pitch = 32 * ( ( w + 31 ) / 31 );
+ bitmap.rows = h;
+ bitmap.buffer = mlt_pool_alloc( h * bitmap.pitch );
+ bitmap.num_grays = 256;
+ bitmap.pixel_mode = ft_pixel_mode_grays;
+
+ memset( bitmap.buffer, 0, h * bitmap.pitch );
+
+ pango_ft2_render_layout( &bitmap, layout, 0, 0 );
+
+ src = bitmap.buffer;
+ x = ( gdk_pixbuf_get_width( pixbuf ) - w - 2 * pad ) * align / 2 + pad;
+ dest = gdk_pixbuf_get_pixels( pixbuf ) + 4 * x + pad * stride;
+ j = h;
+
+ while( j -- )
+ {
+ d = dest;
+ s = src;
+ i = w;
+ while( i -- )
+ {
+ a = *s ++;
+ *d++ = ( a * fg.r + ( 255 - a ) * bg.r ) >> 8;
+ *d++ = ( a * fg.g + ( 255 - a ) * bg.g ) >> 8;
+ *d++ = ( a * fg.b + ( 255 - a ) * bg.b ) >> 8;
+ *d++ = ( a * fg.a + ( 255 - a ) * bg.a ) >> 8;
+ }
+ dest += stride;
+ src += bitmap.pitch;
+ }
+ mlt_pool_release( bitmap.buffer );
+ pango_font_description_free( desc );
+ g_object_unref( layout );
+ g_object_unref( context );
+
+ return pixbuf;
+}
--- /dev/null
+/*
+ * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_cache.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+typedef struct producer_pixbuf_s *producer_pixbuf;
+
+struct producer_pixbuf_s
+{
+ struct mlt_producer_s parent;
+
+ // File name list
+ mlt_properties filenames;
+ int count;
+ int image_idx;
+
+ int width;
+ int height;
+ uint8_t *image;
+ uint8_t *alpha;
+ mlt_cache_item image_cache;
+ mlt_cache_item alpha_cache;
+ pthread_mutex_t mutex;
+};
+
+static void load_filenames( producer_pixbuf this, mlt_properties producer_properties );
+static void refresh_image( producer_pixbuf this, mlt_frame frame, int width, int height );
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_pixbuf_init( char *filename )
+{
+ producer_pixbuf this = calloc( sizeof( struct producer_pixbuf_s ), 1 );
+ if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+ {
+ mlt_producer producer = &this->parent;
+
+ // Get the properties interface
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+
+ // Callback registration
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Set the default properties
+ mlt_properties_set( properties, "resource", filename );
+ mlt_properties_set_int( properties, "ttl", 25 );
+ mlt_properties_set_int( properties, "aspect_ratio", 1 );
+ mlt_properties_set_int( properties, "progressive", 1 );
+
+ // Validate the resource
+ if ( filename )
+ load_filenames( this, properties );
+ if ( this->count )
+ {
+ mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+ if ( frame )
+ {
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+ pthread_mutex_init( &this->mutex, NULL );
+ mlt_properties_set_data( frame_properties, "producer_pixbuf", this, 0, NULL, NULL );
+ mlt_frame_set_position( frame, mlt_producer_position( producer ) );
+ mlt_properties_set_position( frame_properties, "pixbuf_position", mlt_producer_position( producer ) );
+ refresh_image( this, frame, 0, 0 );
+ mlt_frame_close( frame );
+ }
+ }
+ if ( this->width == 0 )
+ {
+ producer_close( producer );
+ producer = NULL;
+ }
+ return producer;
+ }
+ free( this );
+ return NULL;
+}
+
+static void load_filenames( producer_pixbuf this, mlt_properties producer_properties )
+{
+ char *filename = mlt_properties_get( producer_properties, "resource" );
+ this->filenames = mlt_properties_new( );
+
+ // Read xml string
+ if ( strstr( filename, "<svg" ) )
+ {
+ // Generate a temporary file for the svg
+ char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
+ int fd = mkstemp( fullname );
+
+ if ( fd > -1 )
+ {
+ // Write the svg into the temp file
+ ssize_t remaining_bytes;
+ char *xml = filename;
+
+ // Strip leading crap
+ while ( xml[0] != '<' )
+ xml++;
+
+ remaining_bytes = strlen( xml );
+ while ( remaining_bytes > 0 )
+ remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
+ close( fd );
+
+ mlt_properties_set( this->filenames, "0", fullname );
+
+ // Teehe - when the producer closes, delete the temp file and the space allo
+ mlt_properties_set_data( producer_properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
+ }
+ }
+ // Obtain filenames
+ else if ( strchr( filename, '%' ) != NULL )
+ {
+ // handle picture sequences
+ int i = mlt_properties_get_int( producer_properties, "begin" );
+ int gap = 0;
+ char full[1024];
+ int keyvalue = 0;
+ char key[ 50 ];
+
+ while ( gap < 100 )
+ {
+ struct stat buf;
+ snprintf( full, 1023, filename, i ++ );
+ if ( stat( full, &buf ) == 0 )
+ {
+ sprintf( key, "%d", keyvalue ++ );
+ mlt_properties_set( this->filenames, key, full );
+ gap = 0;
+ }
+ else
+ {
+ gap ++;
+ }
+ }
+ }
+ else if ( strstr( filename, "/.all." ) != NULL )
+ {
+ char wildcard[ 1024 ];
+ char *dir_name = strdup( filename );
+ char *extension = strrchr( dir_name, '.' );
+
+ *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
+ sprintf( wildcard, "*%s", extension );
+
+ mlt_properties_dir_list( this->filenames, dir_name, wildcard, 1 );
+
+ free( dir_name );
+ }
+ else
+ {
+ mlt_properties_set( this->filenames, "0", filename );
+ }
+
+ this->count = mlt_properties_count( this->filenames );
+}
+
+static void refresh_image( producer_pixbuf this, mlt_frame frame, int width, int height )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer
+ mlt_producer producer = &this->parent;
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Obtain the cache flag and structure
+ int use_cache = mlt_properties_get_int( producer_props, "cache" );
+ mlt_properties cache = mlt_properties_get_data( producer_props, "_cache", NULL );
+ int update_cache = 0;
+
+ // restore GdkPixbuf
+ pthread_mutex_lock( &this->mutex );
+ mlt_cache_item pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
+ GdkPixbuf *pixbuf = mlt_cache_item_data( pixbuf_cache, NULL );
+ GError *error = NULL;
+
+ // restore scaled image
+ this->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
+ this->image = mlt_cache_item_data( this->image_cache, NULL );
+
+ // restore alpha channel
+ this->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
+ this->alpha = mlt_cache_item_data( this->alpha_cache, NULL );
+
+ // Check if user wants us to reload the image
+ if ( mlt_properties_get_int( producer_props, "force_reload" ) )
+ {
+ pixbuf = NULL;
+ this->image = NULL;
+ mlt_properties_set_int( producer_props, "force_reload", 0 );
+ }
+
+ // Get the time to live for each frame
+ double ttl = mlt_properties_get_int( producer_props, "ttl" );
+
+ // Get the original position of this frame
+ mlt_position position = mlt_properties_get_position( properties, "pixbuf_position" );
+ position += mlt_producer_get_in( producer );
+
+ // Image index
+ int image_idx = ( int )floor( ( double )position / ttl ) % this->count;
+
+ // Key for the cache
+ char image_key[ 10 ];
+ sprintf( image_key, "%d", image_idx );
+
+ pthread_mutex_lock( &g_mutex );
+
+ // Check if the frame is already loaded
+ if ( use_cache )
+ {
+ if ( cache == NULL )
+ {
+ cache = mlt_properties_new( );
+ mlt_properties_set_data( producer_props, "_cache", cache, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ }
+
+ mlt_frame cached = mlt_properties_get_data( cache, image_key, NULL );
+
+ if ( cached )
+ {
+ this->image_idx = image_idx;
+ mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+ this->width = mlt_properties_get_int( cached_props, "width" );
+ this->height = mlt_properties_get_int( cached_props, "height" );
+ mlt_properties_set_int( producer_props, "_real_width", mlt_properties_get_int( cached_props, "real_width" ) );
+ mlt_properties_set_int( producer_props, "_real_height", mlt_properties_get_int( cached_props, "real_height" ) );
+ this->image = mlt_properties_get_data( cached_props, "image", NULL );
+ this->alpha = mlt_properties_get_data( cached_props, "alpha", NULL );
+
+ if ( width != 0 && ( width != this->width || height != this->height ) )
+ this->image = NULL;
+ }
+ }
+
+ // optimization for subsequent iterations on single picture
+ if ( width != 0 && ( image_idx != this->image_idx || width != this->width || height != this->height ) )
+ this->image = NULL;
+ if ( image_idx != this->image_idx )
+ pixbuf = NULL;
+ if ( pixbuf == NULL && ( width == 0 || this->image == NULL ) )
+ {
+ this->image = NULL;
+ this->image_idx = image_idx;
+ pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( this->filenames, image_idx ), &error );
+
+ if ( pixbuf )
+ {
+ // Register this pixbuf for destruction and reuse
+ mlt_cache_item_close( pixbuf_cache );
+ mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref );
+ pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
+
+ mlt_events_block( producer_props, NULL );
+ mlt_properties_set_int( producer_props, "_real_width", gdk_pixbuf_get_width( pixbuf ) );
+ mlt_properties_set_int( producer_props, "_real_height", gdk_pixbuf_get_height( pixbuf ) );
+ mlt_events_unblock( producer_props, NULL );
+
+ // Store the width/height of the pixbuf temporarily
+ this->width = gdk_pixbuf_get_width( pixbuf );
+ this->height = gdk_pixbuf_get_height( pixbuf );
+ }
+ }
+
+ // If we have a pixbuf and we need an image
+ if ( pixbuf && width > 0 && this->image == NULL )
+ {
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+ int interp = GDK_INTERP_BILINEAR;
+
+ if ( strcmp( interps, "nearest" ) == 0 )
+ interp = GDK_INTERP_NEAREST;
+ else if ( strcmp( interps, "tiles" ) == 0 )
+ interp = GDK_INTERP_TILES;
+ else if ( strcmp( interps, "hyper" ) == 0 )
+ interp = GDK_INTERP_HYPER;
+
+ // Note - the original pixbuf is already safe and ready for destruction
+ pixbuf = gdk_pixbuf_scale_simple( pixbuf, width, height, interp );
+
+ // Store width and height
+ this->width = width;
+ this->height = height;
+
+ // Allocate/define image
+ this->image = mlt_pool_alloc( width * ( height + 1 ) * 2 );
+ if ( !use_cache )
+ mlt_cache_item_close( this->image_cache );
+ mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", this->image, width * ( height + 1 ) * 2, mlt_pool_release );
+ this->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
+
+ // Extract YUV422 and alpha
+ if ( gdk_pixbuf_get_has_alpha( pixbuf ) )
+ {
+ // Allocate the alpha mask
+ this->alpha = mlt_pool_alloc( this->width * this->height );
+ if ( !use_cache )
+ mlt_cache_item_close( this->alpha_cache );
+ mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", this->alpha, width * height, mlt_pool_release );
+ this->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
+
+ // Convert the image
+ mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
+ this->width, this->height,
+ gdk_pixbuf_get_rowstride( pixbuf ),
+ this->image, this->alpha );
+ }
+ else
+ {
+ // No alpha to extract
+ mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
+ this->width, this->height,
+ gdk_pixbuf_get_rowstride( pixbuf ),
+ this->image );
+ }
+
+ // Finished with pixbuf now
+ g_object_unref( pixbuf );
+
+ // Ensure we update the cache when we need to
+ update_cache = use_cache;
+ }
+
+ // release references no longer needed
+ mlt_cache_item_close( pixbuf_cache );
+ if ( width == 0 )
+ {
+ pthread_mutex_unlock( &this->mutex );
+ mlt_cache_item_close( this->image_cache );
+ mlt_cache_item_close( this->alpha_cache );
+ }
+
+ // Set width/height of frame
+ mlt_properties_set_int( properties, "width", this->width );
+ mlt_properties_set_int( properties, "height", this->height );
+ mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+ mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+
+ if ( update_cache )
+ {
+ mlt_frame cached = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+ mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+ mlt_properties_set_int( cached_props, "width", this->width );
+ mlt_properties_set_int( cached_props, "height", this->height );
+ mlt_properties_set_int( cached_props, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+ mlt_properties_set_int( cached_props, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+ mlt_properties_set_data( cached_props, "image", this->image, this->width * ( this->height + 1 ) * 2, mlt_pool_release, NULL );
+ mlt_properties_set_data( cached_props, "alpha", this->alpha, this->width * this->height, mlt_pool_release, NULL );
+ mlt_properties_set_data( cache, image_key, cached, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ }
+
+ pthread_mutex_unlock( &g_mutex );
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer for this frame
+ producer_pixbuf this = mlt_properties_get_data( properties, "producer_pixbuf", NULL );
+
+ *width = mlt_properties_get_int( properties, "rescale_width" );
+ *height = mlt_properties_get_int( properties, "rescale_height" );
+
+ // Refresh the image
+ refresh_image( this, frame, *width, *height );
+
+ // Get the image size
+ int image_size = this->width * ( this->height + 1 ) * 2;
+ int alpha_size = this->width * this->height;
+
+ // Get width and height (may have changed during the refresh)
+ *width = mlt_properties_get_int( properties, "width" );
+ *height = mlt_properties_get_int( properties, "height" );
+
+ // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
+ // The fault is not in the design of mlt, but in the implementation of the pixbuf producer...
+ if ( this->image )
+ {
+ if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
+ {
+ // Clone the image and the alpha
+ uint8_t *image_copy = mlt_pool_alloc( image_size );
+ uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+ memcpy( image_copy, this->image, image_size );
+
+ // Copy or default the alpha
+ if ( this->alpha != NULL )
+ memcpy( alpha_copy, this->alpha, alpha_size );
+ else
+ memset( alpha_copy, 255, alpha_size );
+
+ // Now update properties so we free the copy after
+ mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+ // We're going to pass the copy on
+ *buffer = image_copy;
+ }
+ else if ( *format == mlt_image_rgb24a )
+ {
+ // Clone the image and the alpha
+ image_size = *width * ( *height + 1 ) * 4;
+ alpha_size = *width * ( *height + 1 );
+ uint8_t *image_copy = mlt_pool_alloc( image_size );
+ uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+ mlt_convert_yuv422_to_rgb24a( this->image, image_copy, (*width)*(*height) );
+
+ // Now update properties so we free the copy after
+ mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+ // We're going to pass the copy on
+ *buffer = image_copy;
+ }
+
+ }
+ else
+ {
+ // TODO: Review all cases of invalid images
+ *buffer = mlt_pool_alloc( 50 * 50 * 2 );
+ mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
+ *width = 50;
+ *height = 50;
+ }
+
+ // Release references and locks
+ pthread_mutex_unlock( &this->mutex );
+ mlt_cache_item_close( this->image_cache );
+ mlt_cache_item_close( this->alpha_cache );
+
+ return 0;
+}
+
+static uint8_t *producer_get_alpha_mask( mlt_frame this )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Return the alpha mask
+ return mlt_properties_get_data( properties, "alpha", NULL );
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Get the real structure for this producer
+ producer_pixbuf this = producer->child;
+
+ // Fetch the producers properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ if ( this->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
+ load_filenames( this, producer_properties );
+
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ if ( *frame != NULL && this->count > 0 )
+ {
+ // Obtain properties of frame and producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Set the producer on the frame properties
+ mlt_properties_set_data( properties, "producer_pixbuf", this, 0, NULL, NULL );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Ensure that we have a way to obtain the position in the get_image
+ mlt_properties_set_position( properties, "pixbuf_position", mlt_producer_position( producer ) );
+
+ // Refresh the image
+ refresh_image( this, *frame, 0, 0 );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
+
+ // Set alpha call back
+ ( *frame )->get_alpha_mask = producer_get_alpha_mask;
+
+ // Push the get_image method
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ producer_pixbuf this = parent->child;
+ pthread_mutex_destroy( &this->mutex );
+ parent->close = NULL;
+ mlt_producer_close( parent );
+ mlt_properties_close( this->filenames );
+ free( this );
+}
--- /dev/null
+/*
+ * scale_line_22_yuv_mmx.S -- scale line in YUY2 format
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+ .file "scale_line_22_yuv_mmx.S"
+ .version "01.01"
+
+.extern printf
+
+gcc2_compiled.:
+.data
+MSG: .ascii "scale_line_22_yuv_mmx: %d %d\n"
+
+.text
+ .align 16
+
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)
+
+.globl pixops_scale_line_22_yuv_mmx
+ .type pixops_scale_line_22_yuv_mmx,@function
+pixops_scale_line_22_yuv_mmx:
+
+#else
+
+.globl _pixops_scale_line_22_yuv_mmx
+_pixops_scale_line_22_yuv_mmx:
+
+#endif
+/*
+ * Arguments
+ *
+ * weights: 8(%ebp)
+ * p (dest): 12(%ebp) %esi
+ * q1 (src0): 16(%ebp)
+ * q2 (src1): 20(%ebp)
+ * xstep: 24(%ebp)
+ * p_end: 28(%ebp)
+ * xinit: 32(%ebp)
+ * dest_x: 36(%ebp)
+ *
+ */
+
+/*
+ * Function call entry
+ */
+ pushl %ebp
+ movl %esp,%ebp
+ subl $28,%esp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+/* Locals:
+ * int x %ebx
+ * int x_scaled -24(%ebp)
+ * int dest_x 36(%ebp)
+ */
+
+/*
+ * Setup
+ */
+/* Initialize variables */
+ movl 36(%ebp),%eax # destx
+ movl %eax,36(%ebp)
+ movl 32(%ebp),%ebx # x
+ movl 12(%ebp),%esi # dest
+
+ cmpl 28(%ebp),%esi # dest == dest_end ?
+ jnb .out
+
+/* For the body of this loop, %mm0, %mm1, %mm2, %mm3 hold the 4 adjoining
+ * points we are interpolating between, as:
+ *
+ * 00VV00Y200UU00Y1
+ */
+
+ pxor %mm4, %mm4
+/*
+ * Load next component values into mm1 (src0) and mm3 (src1)
+ */
+ movl %ebx, %eax # x_scaled
+ sarl $15, %eax
+ andl $0xfffffffe, %eax
+ movl %eax, %edx # x_aligned
+ andl $0xfffffffc, %edx
+
+ movl 16(%ebp), %edi # get src0
+ movl (%edi,%eax), %ecx # get y
+ andl $0x00ff00ff, %ecx # mask off y
+ movl (%edi,%edx), %eax # get uv
+ andl $0xff00ff00, %eax # mask off uv
+ orl %eax, %ecx # composite y, uv
+ movd %ecx, %mm1 # move to mmx1
+ punpcklbw %mm4, %mm1
+
+ movl 20(%ebp), %edi # get src1
+ movl (%edi,%edx), %ecx # get y
+ andl $0x00ff00ff, %ecx # mask off y
+ movl (%edi,%edx), %eax # get uv
+ andl $0xff00ff00, %eax # mask off uv
+ orl %eax, %ecx # composite y, uv
+ movd %ecx, %mm3 # move to mmx3
+ punpcklbw %mm4, %mm3
+
+ jmp .newx
+
+ .p2align 4,,7
+.loop:
+
+/* short *pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y
+ * 16 4 0xf 2 2
+ */
+ movl 8(%ebp), %edi # get weights pointer
+ movl %ebx, %eax
+ andl $0xf000, %eax
+ shrl $7, %eax
+
+/* At this point, %edi holds weights. Load the 4 weights into
+ * %mm4,%mm5,%mm6,%mm7, multiply and accumulate.
+ */
+ movq (%edi,%eax), %mm4
+ pmullw %mm0, %mm4
+ movq 8(%edi,%eax), %mm5
+ pmullw %mm1, %mm5
+ movq 16(%edi,%eax), %mm6
+ pmullw %mm2,%mm6
+ movq 24(%edi,%eax), %mm7
+ pmullw %mm3,%mm7
+
+ paddw %mm4, %mm5
+ paddw %mm6, %mm7
+ paddw %mm5, %mm7
+
+/* %mm7 holds the accumulated sum. Compute (C + 0x80) / 256
+ */
+ pxor %mm4, %mm4
+ movl $0x80808080, %eax
+ movd %eax, %mm6
+ punpcklbw %mm4, %mm6
+ paddw %mm6, %mm7
+ psrlw $8, %mm7
+
+/* Pack into %eax and store result
+ */
+ packuswb %mm7, %mm7
+ movd %mm7, %eax
+
+ movb %al, (%esi) # *dest = y
+
+ movl 36(%ebp), %ecx # get dest_x
+ andl $1, %ecx # select u or v
+ sall $1, %ecx # determine offset
+ addl $1, %ecx # relative to x_aligned
+ sall $3, %ecx # offset * 8 bits/byte
+
+ movd %mm7, %eax
+ shrl %cl, %eax
+ movb %al, 1(%esi) # *dest = uv
+
+ addl $2, %esi # dest += 2
+ cmpl %esi,28(%ebp) # if dest == dest_end
+ je .out # then exit
+
+ addl $1, 36(%ebp) # dest_x++
+
+.newx:
+
+ addl 24(%ebp), %ebx # x += x_step
+/*
+ * Load current component values into mm0 (src0) and mm2 (src1)
+ */
+ movq %mm1, %mm0
+ movq %mm3, %mm2
+
+/*
+ * Load next component values into mm1 (src0) and mm3 (src1)
+ */
+ movl %ebx, %eax # x_scaled
+ sarl $15, %eax
+ andl $0xfffffffe, %eax
+ movl %eax, %edx # x_aligned
+ andl $0xfffffffc, %edx
+
+ movl 16(%ebp), %edi # get src0
+ movl (%edi,%eax), %ecx # get y
+ andl $0x00ff00ff, %ecx # mask off y
+ movl (%edi,%edx), %eax # get uv
+ andl $0xff00ff00, %eax # mask off uv
+ orl %eax, %ecx # composite y, uv
+ movd %ecx, %mm1 # move to mmx1
+ punpcklbw %mm4, %mm1
+
+ movl 20(%ebp), %edi # get src1
+ movl (%edi,%edx), %ecx # get y
+ andl $0x00ff00ff, %ecx # mask off y
+ movl (%edi,%edx), %eax # get uv
+ andl $0xff00ff00, %eax # mask off uv
+ orl %eax, %ecx # composite y, uv
+ movd %ecx, %mm3 # move to mmx3
+ punpcklbw %mm4, %mm3
+
+ jmp .loop
+
+.out:
+ movl %esi,%eax
+ emms
+ leal -40(%ebp),%esp
+ popl %ebx
+ popl %esi
+ popl %edi
+ movl %ebp,%esp
+ popl %ebp
+ ret
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltinigo$(LIBSUF)
+
+OBJS = factory.o \
+ producer_inigo.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_inigo_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_inigo_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( producer_type, "inigo", producer_inigo_init );
+ MLT_REGISTER( producer_type, "inigo_file", producer_inigo_file_init );
+}
--- /dev/null
+/*
+ * producer_inigo.c -- simple inigo test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <framework/mlt.h>
+
+mlt_producer producer_inigo_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv );
+
+mlt_producer producer_inigo_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *file )
+{
+ FILE *input = fopen( file, "r" );
+ char **args = calloc( sizeof( char * ), 1000 );
+ int count = 0;
+ char temp[ 2048 ];
+
+ if ( input != NULL )
+ {
+ while( fgets( temp, 2048, input ) )
+ {
+ temp[ strlen( temp ) - 1 ] = '\0';
+ if ( strcmp( temp, "" ) )
+ args[ count ++ ] = strdup( temp );
+ }
+ }
+
+ mlt_producer result = producer_inigo_init( profile, type, id, args );
+
+ if ( result != NULL )
+ {
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( result );
+ mlt_properties_set( properties, "resource", file );
+ }
+
+ while( count -- )
+ free( args[ count ] );
+ free( args );
+
+ return result;
+}
+
+static void track_service( mlt_field field, void *service, mlt_destructor destructor )
+{
+ mlt_properties properties = mlt_field_properties( field );
+ int registered = mlt_properties_get_int( properties, "registered" );
+ char *key = mlt_properties_get( properties, "registered" );
+ mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
+ mlt_properties_set_int( properties, "registered", ++ registered );
+}
+
+static mlt_producer create_producer( mlt_profile profile, mlt_field field, char *file )
+{
+ mlt_producer result = mlt_factory_producer( profile, "fezzik", file );
+
+ if ( result != NULL )
+ track_service( field, result, ( mlt_destructor )mlt_producer_close );
+
+ return result;
+}
+
+static mlt_filter create_attach( mlt_profile profile, mlt_field field, char *id, int track )
+{
+ char *temp = strdup( id );
+ char *arg = strchr( temp, ':' );
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ mlt_filter filter = mlt_factory_filter( profile, temp, arg );
+ if ( filter != NULL )
+ track_service( field, filter, ( mlt_destructor )mlt_filter_close );
+ free( temp );
+ return filter;
+}
+
+static mlt_filter create_filter( mlt_profile profile, mlt_field field, char *id, int track )
+{
+ char *temp = strdup( id );
+ char *arg = strchr( temp, ':' );
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ mlt_filter filter = mlt_factory_filter( profile, temp, arg );
+ if ( filter != NULL )
+ {
+ mlt_field_plant_filter( field, filter, track );
+ track_service( field, filter, ( mlt_destructor )mlt_filter_close );
+ }
+ free( temp );
+ return filter;
+}
+
+static mlt_transition create_transition( mlt_profile profile, mlt_field field, char *id, int track )
+{
+ char *arg = strchr( id, ':' );
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ mlt_transition transition = mlt_factory_transition( profile, id, arg );
+ if ( transition != NULL )
+ {
+ mlt_field_plant_transition( field, transition, track, track + 1 );
+ track_service( field, transition, ( mlt_destructor )mlt_transition_close );
+ }
+ return transition;
+}
+
+mlt_producer producer_inigo_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv )
+{
+ int i;
+ int track = 0;
+ mlt_producer producer = NULL;
+ mlt_tractor mix = NULL;
+ mlt_playlist playlist = mlt_playlist_init( );
+ mlt_properties group = mlt_properties_new( );
+ mlt_tractor tractor = mlt_tractor_new( );
+ mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
+ mlt_field field = mlt_tractor_field( tractor );
+ mlt_properties field_properties = mlt_field_properties( field );
+ mlt_multitrack multitrack = mlt_tractor_multitrack( tractor );
+ char *title = NULL;
+
+ // Assistance for template construction (allows -track usage to specify the first track)
+ mlt_properties_set_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_inigo_first", 1 );
+
+ // We need to track the number of registered filters
+ mlt_properties_set_int( field_properties, "registered", 0 );
+
+ // Parse the arguments
+ if ( argv )
+ for ( i = 0; argv[ i ] != NULL; i ++ )
+ {
+ if ( !strcmp( argv[ i ], "-group" ) )
+ {
+ if ( mlt_properties_count( group ) != 0 )
+ {
+ mlt_properties_close( group );
+ group = mlt_properties_new( );
+ }
+ if ( group != NULL )
+ properties = group;
+ }
+ else if ( !strcmp( argv[ i ], "-attach" ) ||
+ !strcmp( argv[ i ], "-attach-cut" ) ||
+ !strcmp( argv[ i ], "-attach-track" ) ||
+ !strcmp( argv[ i ], "-attach-clip" ) )
+ {
+ int type = !strcmp( argv[ i ], "-attach" ) ? 0 :
+ !strcmp( argv[ i ], "-attach-cut" ) ? 1 :
+ !strcmp( argv[ i ], "-attach-track" ) ? 2 : 3;
+ mlt_filter filter = create_attach( profile, field, argv[ ++ i ], track );
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_append( playlist, producer );
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ }
+
+ if ( type == 1 || type == 2 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ }
+
+ if ( filter != NULL && mlt_playlist_count( playlist ) > 0 )
+ {
+ if ( type == 0 )
+ mlt_service_attach( ( mlt_service )properties, filter );
+ else if ( type == 1 )
+ mlt_service_attach( ( mlt_service )producer, filter );
+ else if ( type == 2 )
+ mlt_service_attach( ( mlt_service )playlist, filter );
+ else if ( type == 3 )
+ mlt_service_attach( ( mlt_service )mlt_producer_cut_parent( producer ), filter );
+
+ properties = MLT_FILTER_PROPERTIES( filter );
+ mlt_properties_inherit( properties, group );
+ }
+ else if ( filter != NULL )
+ {
+ mlt_service_attach( ( mlt_service )playlist, filter );
+ properties = MLT_FILTER_PROPERTIES( filter );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-repeat" ) )
+ {
+ int repeat = atoi( argv[ ++ i ] );
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( mlt_playlist_count( playlist ) > 0 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_repeat_clip( playlist, mlt_playlist_count( playlist ) - 1, repeat );
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-split" ) )
+ {
+ int split = atoi( argv[ ++ i ] );
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( mlt_playlist_count( playlist ) > 0 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ split = split < 0 ? info.frame_out + split : split;
+ mlt_playlist_split( playlist, mlt_playlist_count( playlist ) - 1, split );
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-swap" ) )
+ {
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( mlt_playlist_count( playlist ) >= 2 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_move( playlist, mlt_playlist_count( playlist ) - 2, mlt_playlist_count( playlist ) - 1 );
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-join" ) )
+ {
+ int clips = atoi( argv[ ++ i ] );
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( mlt_playlist_count( playlist ) > 0 )
+ {
+ mlt_playlist_clip_info info;
+ int clip = clips <= 0 ? 0 : mlt_playlist_count( playlist ) - clips - 1;
+ if ( clip < 0 ) clip = 0;
+ if ( clip >= mlt_playlist_count( playlist ) ) clip = mlt_playlist_count( playlist ) - 2;
+ if ( clips < 0 ) clips = mlt_playlist_count( playlist ) - 1;
+ mlt_playlist_join( playlist, clip, clips, 0 );
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-remove" ) )
+ {
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( mlt_playlist_count( playlist ) > 0 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_remove( playlist, mlt_playlist_count( playlist ) - 1 );
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-mix" ) )
+ {
+ int length = atoi( argv[ ++ i ] );
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( mlt_playlist_count( playlist ) >= 2 )
+ {
+ if ( mlt_playlist_mix( playlist, mlt_playlist_count( playlist ) - 2, length, NULL ) == 0 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ if ( mlt_properties_get_data( ( mlt_properties )info.producer, "mlt_mix", NULL ) == NULL )
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 2 );
+ mix = ( mlt_tractor )mlt_properties_get_data( ( mlt_properties )info.producer, "mlt_mix", NULL );
+ properties = NULL;
+ }
+ else
+ {
+ fprintf( stderr, "Mix failed?\n" );
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Invalid position for a mix...\n" );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-mixer" ) )
+ {
+ if ( mix != NULL )
+ {
+ char *id = strdup( argv[ ++ i ] );
+ char *arg = strchr( id, ':' );
+ mlt_field field = mlt_tractor_field( mix );
+ mlt_transition transition = NULL;
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ transition = mlt_factory_transition( profile, id, arg );
+ if ( transition != NULL )
+ {
+ properties = MLT_TRANSITION_PROPERTIES( transition );
+ mlt_properties_inherit( properties, group );
+ mlt_field_plant_transition( field, transition, 0, 1 );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", mlt_producer_get_out( ( mlt_producer )mix ) );
+ mlt_transition_close( transition );
+ }
+ free( id );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid mixer...\n" );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-filter" ) )
+ {
+ mlt_filter filter = create_filter( profile, field, argv[ ++ i ], track );
+ if ( filter != NULL )
+ {
+ properties = MLT_FILTER_PROPERTIES( filter );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-transition" ) )
+ {
+ mlt_transition transition = create_transition( profile, field, argv[ ++ i ], track - 1 );
+ if ( transition != NULL )
+ {
+ properties = MLT_TRANSITION_PROPERTIES( transition );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-blank" ) )
+ {
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ mlt_playlist_blank( playlist, atof( argv[ ++ i ] ) );
+ }
+ else if ( !strcmp( argv[ i ], "-track" ) ||
+ !strcmp( argv[ i ], "-null-track" ) ||
+ !strcmp( argv[ i ], "-video-track" ) ||
+ !strcmp( argv[ i ], "-audio-track" ) ||
+ !strcmp( argv[ i ], "-hide-track" ) ||
+ !strcmp( argv[ i ], "-hide-video" ) ||
+ !strcmp( argv[ i ], "-hide-audio" ) )
+ {
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ producer = NULL;
+ if ( !mlt_properties_get_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_inigo_first" ) ||
+ mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) ) > 0 )
+ {
+ mlt_multitrack_connect( multitrack, MLT_PLAYLIST_PRODUCER( playlist ), track ++ );
+ track_service( field, playlist, ( mlt_destructor )mlt_playlist_close );
+ playlist = mlt_playlist_init( );
+ }
+ if ( playlist != NULL )
+ {
+ properties = MLT_PLAYLIST_PROPERTIES( playlist );
+ if ( !strcmp( argv[ i ], "-null-track" ) || !strcmp( argv[ i ], "-hide-track" ) )
+ mlt_properties_set_int( properties, "hide", 3 );
+ else if ( !strcmp( argv[ i ], "-audio-track" ) || !strcmp( argv[ i ], "-hide-video" ) )
+ mlt_properties_set_int( properties, "hide", 1 );
+ else if ( !strcmp( argv[ i ], "-video-track" ) || !strcmp( argv[ i ], "-hide-audio" ) )
+ mlt_properties_set_int( properties, "hide", 2 );
+ }
+ }
+ else if ( strchr( argv[ i ], '=' ) && strstr( argv[ i ], "<?xml" ) != argv[ i ] )
+ {
+ mlt_properties_parse( properties, argv[ i ] );
+ }
+ else if ( argv[ i ][ 0 ] != '-' )
+ {
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+ if ( title == NULL && strstr( argv[ i ], "<?xml" ) != argv[ i ] )
+ title = argv[ i ];
+ producer = create_producer( profile, field, argv[ i ] );
+ if ( producer != NULL )
+ {
+ properties = MLT_PRODUCER_PROPERTIES( producer );
+ mlt_properties_inherit( properties, group );
+ }
+ else
+ {
+ fprintf( stderr, "Failed to load \"%s\"\n", argv[ i ] );
+ }
+ }
+ else
+ {
+ int backtrack = 0;
+ if ( !strcmp( argv[ i ], "-serialise" ) ||
+ !strcmp( argv[ i ], "-consumer" ) ||
+ !strcmp( argv[ i ], "-profile" ) )
+ {
+ i += 2;
+ backtrack = 1;
+ }
+
+ while ( argv[ i ] != NULL && strchr( argv[ i ], '=' ) )
+ {
+ i ++;
+ backtrack = 1;
+ }
+ if ( backtrack )
+ i --;
+ }
+ }
+
+ // Connect last producer to playlist
+ if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+ mlt_playlist_append( playlist, producer );
+
+ // Track the last playlist too
+ track_service( field, playlist, ( mlt_destructor )mlt_playlist_close );
+
+ // We must have a playlist to connect
+ if ( !mlt_properties_get_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_inigo_first" ) ||
+ mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) ) > 0 )
+ mlt_multitrack_connect( multitrack, MLT_PLAYLIST_PRODUCER( playlist ), track );
+
+ mlt_producer prod = MLT_TRACTOR_PRODUCER( tractor );
+ mlt_producer_optimise( prod );
+ mlt_properties props = MLT_TRACTOR_PROPERTIES( tractor );
+ mlt_properties_set_data( props, "group", group, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ mlt_properties_set_position( props, "length", mlt_producer_get_out( MLT_MULTITRACK_PRODUCER( multitrack ) ) + 1 );
+ mlt_producer_set_in_and_out( prod, 0, mlt_producer_get_out( MLT_MULTITRACK_PRODUCER( multitrack ) ) );
+ if ( title != NULL )
+ mlt_properties_set( props, "title", strchr( title, '/' ) ? strrchr( title, '/' ) + 1 : title );
+
+ return prod;
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltjackrack$(LIBSUF)
+
+OBJS = factory.o \
+ jack_rack.o \
+ lock_free_fifo.o \
+ plugin.o \
+ plugin_desc.o \
+ plugin_mgr.o \
+ plugin_settings.o \
+ process.o \
+ filter_jackrack.o \
+ filter_ladspa.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags jack`
+CFLAGS += `xml2-config --cflags`
+CFLAGS += `pkg-config glib-2.0 --cflags`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs jack`
+LDFLAGS += `xml2-config --libs`
+LDFLAGS += `pkg-config glib-2.0 --libs`
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ pkg-config jack
+ disable_jack=$?
+
+ which xml2-config > /dev/null 2>&1
+ disable_xml2=$?
+
+ disable_ladspa=1
+ ladspa_prefix=`which listplugins 2> /dev/null`
+ if [ "$ladspa_prefix" != "" ]
+ then
+ ladspa_prefix=`dirname "$ladspa_prefix"`
+ disable_ladspa=`[ -f "$ladspa_prefix/include/ladspa.h" ] && echo 1 || echo 0`
+ fi
+
+ if [ "$disable_jack" = "1" -o "$disable_xml2" = "1" -o "$disable_ladspa" = "1" ]
+ then
+ [ "$disable_jack" = "1" ] && echo "- jackrack not found: disabling"
+ [ "$disable_xml2" = "1" ] && echo "- xml2 not found: disabling jackrack"
+ [ "$disable_ladspa" = "1" ] && echo "- ladspa not found; disabling"
+ touch ../disable-jackrack
+ fi
+ exit 0
+fi
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_jackrack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_ladspa_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "jackrack", filter_jackrack_init );
+ MLT_REGISTER( filter_type, "ladspa", filter_ladspa_init );
+}
--- /dev/null
+/*
+ * filter_jackrack.c -- filter audio through Jack and/or LADSPA plugins
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "jack_rack.h"
+
+#define BUFFER_LEN 204800 * 3
+
+static void initialise_jack_ports( mlt_properties properties )
+{
+ int i;
+ char mlt_name[20], rack_name[30];
+ jack_port_t **port = NULL;
+ jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL );
+ jack_nframes_t jack_buffer_size = jack_get_buffer_size( jack_client );
+
+ // Propogate these for the Jack processing callback
+ int channels = mlt_properties_get_int( properties, "channels" );
+
+ // Start JackRack
+ if ( mlt_properties_get( properties, "src" ) )
+ {
+ snprintf( rack_name, sizeof( rack_name ), "jackrack%d", getpid() );
+ jack_rack_t *jackrack = jack_rack_new( rack_name, mlt_properties_get_int( properties, "channels" ) );
+ jack_rack_open_file( jackrack, mlt_properties_get( properties, "src" ) );
+
+ mlt_properties_set_data( properties, "jackrack", jackrack, 0, NULL, NULL );
+ mlt_properties_set( properties, "_rack_client_name", rack_name );
+ }
+
+ // Allocate buffers and ports
+ jack_ringbuffer_t **output_buffers = mlt_pool_alloc( sizeof( jack_ringbuffer_t *) * channels );
+ jack_ringbuffer_t **input_buffers = mlt_pool_alloc( sizeof( jack_ringbuffer_t *) * channels );
+ jack_port_t **jack_output_ports = mlt_pool_alloc( sizeof(jack_port_t *) * channels );
+ jack_port_t **jack_input_ports = mlt_pool_alloc( sizeof(jack_port_t *) * channels );
+ float **jack_output_buffers = mlt_pool_alloc( sizeof(float *) * jack_buffer_size );
+ float **jack_input_buffers = mlt_pool_alloc( sizeof(float *) * jack_buffer_size );
+
+ // Set properties - released inside filter_close
+ mlt_properties_set_data( properties, "output_buffers", output_buffers, sizeof( jack_ringbuffer_t *) * channels, NULL, NULL );
+ mlt_properties_set_data( properties, "input_buffers", input_buffers, sizeof( jack_ringbuffer_t *) * channels, NULL, NULL );
+ mlt_properties_set_data( properties, "jack_output_ports", jack_output_ports, sizeof( jack_port_t *) * channels, NULL, NULL );
+ mlt_properties_set_data( properties, "jack_input_ports", jack_input_ports, sizeof( jack_port_t *) * channels, NULL, NULL );
+ mlt_properties_set_data( properties, "jack_output_buffers", jack_output_buffers, sizeof( float *) * channels, NULL, NULL );
+ mlt_properties_set_data( properties, "jack_input_buffers", jack_input_buffers, sizeof( float *) * channels, NULL, NULL );
+
+ // Start Jack processing - required before registering ports
+ jack_activate( jack_client );
+
+ // Register Jack ports
+ for ( i = 0; i < channels; i++ )
+ {
+ int in;
+
+ output_buffers[i] = jack_ringbuffer_create( BUFFER_LEN * sizeof(float) );
+ input_buffers[i] = jack_ringbuffer_create( BUFFER_LEN * sizeof(float) );
+ snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+ mlt_properties_set_data( properties, mlt_name, output_buffers[i], BUFFER_LEN * sizeof(float), NULL, NULL );
+ snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+ mlt_properties_set_data( properties, mlt_name, input_buffers[i], BUFFER_LEN * sizeof(float), NULL, NULL );
+
+ for ( in = 0; in < 2; in++ )
+ {
+ snprintf( mlt_name, sizeof( mlt_name ), "%s_%d", in ? "in" : "out", i + 1);
+ port = ( in ? &jack_input_ports[i] : &jack_output_ports[i] );
+
+ *port = jack_port_register( jack_client, mlt_name, JACK_DEFAULT_AUDIO_TYPE,
+ ( in ? JackPortIsInput : JackPortIsOutput ) | JackPortIsTerminal, 0 );
+ }
+ }
+
+ // Establish connections
+ for ( i = 0; i < channels; i++ )
+ {
+ int in;
+ for ( in = 0; in < 2; in++ )
+ {
+ port = ( in ? &jack_input_ports[i] : &jack_output_ports[i] );
+ snprintf( mlt_name, sizeof( mlt_name ), "%s", jack_port_name( *port ) );
+
+ snprintf( rack_name, sizeof( rack_name ), "%s_%d", in ? "in" : "out", i + 1 );
+ if ( mlt_properties_get( properties, "_rack_client_name" ) )
+ snprintf( rack_name, sizeof( rack_name ), "%s:%s_%d", mlt_properties_get( properties, "_rack_client_name" ), in ? "out" : "in", i + 1);
+ else if ( mlt_properties_get( properties, rack_name ) )
+ snprintf( rack_name, sizeof( rack_name ), "%s", mlt_properties_get( properties, rack_name ) );
+ else
+ snprintf( rack_name, sizeof( rack_name ), "%s:%s_%d", mlt_properties_get( properties, "_client_name" ), in ? "out" : "in", i + 1);
+
+ if ( in )
+ {
+ fprintf( stderr, "jack connect %s to %s\n", rack_name, mlt_name );
+ jack_connect( jack_client, rack_name, mlt_name );
+ }
+ else
+ {
+ fprintf( stderr, "jack connect %s to %s\n", mlt_name, rack_name );
+ jack_connect( jack_client, mlt_name, rack_name );
+ }
+ }
+ }
+}
+
+static int jack_process (jack_nframes_t frames, void * data)
+{
+ mlt_filter filter = (mlt_filter) data;
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ int channels = mlt_properties_get_int( properties, "channels" );
+ int frame_size = mlt_properties_get_int( properties, "_samples" ) * sizeof(float);
+ int sync = mlt_properties_get_int( properties, "_sync" );
+ int err = 0;
+ int i;
+ static int total_size = 0;
+
+ jack_ringbuffer_t **output_buffers = mlt_properties_get_data( properties, "output_buffers", NULL );
+ if ( output_buffers == NULL )
+ return 0;
+ jack_ringbuffer_t **input_buffers = mlt_properties_get_data( properties, "input_buffers", NULL );
+ jack_port_t **jack_output_ports = mlt_properties_get_data( properties, "jack_output_ports", NULL );
+ jack_port_t **jack_input_ports = mlt_properties_get_data( properties, "jack_input_ports", NULL );
+ float **jack_output_buffers = mlt_properties_get_data( properties, "jack_output_buffers", NULL );
+ float **jack_input_buffers = mlt_properties_get_data( properties, "jack_input_buffers", NULL );
+ pthread_mutex_t *output_lock = mlt_properties_get_data( properties, "output_lock", NULL );
+ pthread_cond_t *output_ready = mlt_properties_get_data( properties, "output_ready", NULL );
+
+ for ( i = 0; i < channels; i++ )
+ {
+ size_t jack_size = ( frames * sizeof(float) );
+ size_t ring_size;
+
+ // Send audio through out port
+ jack_output_buffers[i] = jack_port_get_buffer( jack_output_ports[i], frames );
+ if ( ! jack_output_buffers[i] )
+ {
+ fprintf( stderr, "%s: no jack buffer for output port %d\n", __FUNCTION__, i );
+ err = 1;
+ break;
+ }
+ ring_size = jack_ringbuffer_read_space( output_buffers[i] );
+ jack_ringbuffer_read( output_buffers[i], ( char * )jack_output_buffers[i], ring_size < jack_size ? ring_size : jack_size );
+
+ // Return audio through in port
+ jack_input_buffers[i] = jack_port_get_buffer( jack_input_ports[i], frames );
+ if ( ! jack_input_buffers[i] )
+ {
+ fprintf( stderr, "%s: no jack buffer for input port %d\n", __FUNCTION__, i );
+ err = 1;
+ break;
+ }
+
+ // Do not start returning audio until we have sent first mlt frame
+ if ( sync && i == 0 && frame_size > 0 )
+ total_size += ring_size;
+ //fprintf(stderr, "sync %d frame_size %d ring_size %d jack_size %d\n", sync, frame_size, ring_size, jack_size );
+
+ if ( ! sync || ( frame_size > 0 && total_size >= frame_size ) )
+ {
+ ring_size = jack_ringbuffer_write_space( input_buffers[i] );
+ jack_ringbuffer_write( input_buffers[i], ( char * )jack_input_buffers[i], ring_size < jack_size ? ring_size : jack_size );
+
+ if ( sync )
+ {
+ // Tell mlt that audio is available
+ pthread_mutex_lock( output_lock);
+ pthread_cond_signal( output_ready );
+ pthread_mutex_unlock( output_lock);
+
+ // Clear sync phase
+ mlt_properties_set_int( properties, "_sync", 0 );
+ }
+ }
+ }
+
+ return err;
+}
+
+
+/** Get the audio.
+*/
+
+static int jackrack_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the filter service
+ mlt_filter filter = mlt_frame_pop_audio( frame );
+
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ int jack_frequency = mlt_properties_get_int( filter_properties, "_sample_rate" );
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, &jack_frequency, channels, samples );
+
+ // TODO: Deal with sample rate differences
+ if ( *frequency != jack_frequency )
+ fprintf( stderr, "mismatching frequencies in filter jackrack\n" );
+ *frequency = jack_frequency;
+
+ // Initialise Jack ports and connections if needed
+ if ( mlt_properties_get_int( filter_properties, "_samples" ) == 0 )
+ mlt_properties_set_int( filter_properties, "_samples", *samples );
+
+ // Get the filter-specific properties
+ jack_ringbuffer_t **output_buffers = mlt_properties_get_data( filter_properties, "output_buffers", NULL );
+ jack_ringbuffer_t **input_buffers = mlt_properties_get_data( filter_properties, "input_buffers", NULL );
+// pthread_mutex_t *output_lock = mlt_properties_get_data( filter_properties, "output_lock", NULL );
+// pthread_cond_t *output_ready = mlt_properties_get_data( filter_properties, "output_ready", NULL );
+
+ // Process the audio
+ int16_t *q = *buffer;
+ float sample[ 2 ][ 10000 ];
+ int i, j;
+// struct timespec tm = { 0, 0 };
+
+ // Convert to floats and write into output ringbuffer
+ if ( jack_ringbuffer_write_space( output_buffers[0] ) >= ( *samples * sizeof(float) ) )
+ {
+ for ( i = 0; i < *samples; i++ )
+ for ( j = 0; j < *channels; j++ )
+ sample[ j ][ i ] = ( float )( *q ++ ) / 32768.0;
+
+ for ( j = 0; j < *channels; j++ )
+ jack_ringbuffer_write( output_buffers[j], ( char * )sample[ j ], *samples * sizeof(float) );
+ }
+
+ // Synchronization phase - wait for signal from Jack process
+ while ( jack_ringbuffer_read_space( input_buffers[ *channels - 1 ] ) < ( *samples * sizeof(float) ) ) ;
+ //pthread_cond_wait( output_ready, output_lock );
+
+ // Read from input ringbuffer and convert from floats
+ if ( jack_ringbuffer_read_space( input_buffers[0] ) >= ( *samples * sizeof(float) ) )
+ {
+ // Initialise to silence, but repeat last frame if available in case of
+ // buffer underrun
+ for ( j = 0; j < *channels; j++ )
+ jack_ringbuffer_read( input_buffers[j], ( char * )sample[ j ], *samples * sizeof(float) );
+
+ q = *buffer;
+ for ( i = 0; i < *samples; i++ )
+ for ( j = 0; j < *channels; j++ )
+ {
+ if ( sample[ j ][ i ] > 1.0 )
+ sample[ j ][ i ] = 1.0;
+ else if ( sample[ j ][ i ] < -1.0 )
+ sample[ j ][ i ] = -1.0;
+
+ if ( sample[ j ][ i ] > 0 )
+ *q ++ = 32767 * sample[ j ][ i ];
+ else
+ *q ++ = 32768 * sample[ j ][ i ];
+ }
+ }
+
+ return 0;
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ mlt_frame_push_audio( frame, this );
+ mlt_frame_push_audio( frame, jackrack_get_audio );
+
+ if ( mlt_properties_get_int( properties, "_sync" ) )
+ initialise_jack_ports( properties );
+ }
+
+ return frame;
+}
+
+
+static void filter_close( mlt_filter this )
+{
+ int i;
+ char mlt_name[20];
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL );
+
+ jack_deactivate( jack_client );
+ jack_client_close( jack_client );
+ for ( i = 0; i < mlt_properties_get_int( properties, "channels" ); i++ )
+ {
+ snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+ jack_ringbuffer_free( mlt_properties_get_data( properties, mlt_name, NULL ) );
+ snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+ jack_ringbuffer_free( mlt_properties_get_data( properties, mlt_name, NULL ) );
+ }
+ mlt_pool_release( mlt_properties_get_data( properties, "output_buffers", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "input_buffers", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "jack_output_ports", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "jack_input_ports", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "jack_output_buffers", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "jack_input_buffers", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "output_lock", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "output_ready", NULL ) );
+
+ jack_rack_t *jackrack = mlt_properties_get_data( properties, "jackrack", NULL );
+ jack_rack_destroy( jackrack );
+
+ this->parent.close = NULL;
+ mlt_service_close( &this->parent );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_jackrack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ char name[14];
+
+ snprintf( name, sizeof( name ), "mlt%d", getpid() );
+ jack_client_t *jack_client = jack_client_new( name );
+ if ( jack_client )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ pthread_mutex_t *output_lock = mlt_pool_alloc( sizeof( pthread_mutex_t ) );
+ pthread_cond_t *output_ready = mlt_pool_alloc( sizeof( pthread_cond_t ) );
+
+ jack_set_process_callback( jack_client, jack_process, this );
+ //TODO: jack_on_shutdown( jack_client, jack_shutdown_cb, this );
+ this->process = filter_process;
+ this->close = filter_close;
+ pthread_mutex_init( output_lock, NULL );
+ pthread_cond_init( output_ready, NULL );
+
+ mlt_properties_set( properties, "src", arg );
+ mlt_properties_set( properties, "_client_name", name );
+ mlt_properties_set_data( properties, "jack_client", jack_client, 0, NULL, NULL );
+ mlt_properties_set_int( properties, "_sample_rate", jack_get_sample_rate( jack_client ) );
+ mlt_properties_set_data( properties, "output_lock", output_lock, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "output_ready", output_ready, 0, NULL, NULL );
+ mlt_properties_set_int( properties, "_sync", 1 );
+ mlt_properties_set_int( properties, "channels", 2 );
+ }
+ }
+ return this;
+}
--- /dev/null
+/*
+ * filter_ladspa.c -- filter audio through LADSPA plugins
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <string.h>
+
+#include "jack_rack.h"
+
+#define BUFFER_LEN 10000
+
+static void initialise_jack_rack( mlt_properties properties, int channels )
+{
+ int i;
+ char mlt_name[20];
+
+ // Propogate these for the Jack processing callback
+ mlt_properties_set_int( properties, "channels", channels );
+
+ // Start JackRack
+ if ( mlt_properties_get( properties, "src" ) )
+ {
+ // Create JackRack without Jack client name so that it only uses LADSPA
+ jack_rack_t *jackrack = jack_rack_new( NULL, channels );
+ mlt_properties_set_data( properties, "jackrack", jackrack, 0, NULL, NULL );
+ jack_rack_open_file( jackrack, mlt_properties_get( properties, "src" ) );
+ }
+
+ // Allocate buffers
+ LADSPA_Data **input_buffers = mlt_pool_alloc( sizeof( LADSPA_Data ) * channels );
+ LADSPA_Data **output_buffers = mlt_pool_alloc( sizeof( LADSPA_Data ) * channels );
+
+ // Set properties - released inside filter_close
+ mlt_properties_set_data( properties, "input_buffers", input_buffers, sizeof( LADSPA_Data *) * channels, NULL, NULL );
+ mlt_properties_set_data( properties, "output_buffers", output_buffers, sizeof( LADSPA_Data *) * channels, NULL, NULL );
+
+ // Register Jack ports
+ for ( i = 0; i < channels; i++ )
+ {
+ input_buffers[i] = mlt_pool_alloc( BUFFER_LEN * sizeof( LADSPA_Data ) );
+ snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+ mlt_properties_set_data( properties, mlt_name, input_buffers[i], BUFFER_LEN * sizeof( LADSPA_Data ), NULL, NULL );
+
+ output_buffers[i] = mlt_pool_alloc( BUFFER_LEN * sizeof( LADSPA_Data ) );
+ snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+ mlt_properties_set_data( properties, mlt_name, output_buffers[i], BUFFER_LEN * sizeof( LADSPA_Data ), NULL, NULL );
+ }
+}
+
+
+/** Get the audio.
+*/
+
+static int ladspa_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the filter service
+ mlt_filter filter = mlt_frame_pop_audio( frame );
+
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+
+ // Initialise LADSPA if needed
+ jack_rack_t *jackrack = mlt_properties_get_data( filter_properties, "jackrack", NULL );
+ if ( jackrack == NULL )
+ {
+ sample_rate = *frequency;
+ initialise_jack_rack( filter_properties, *channels );
+ jackrack = mlt_properties_get_data( filter_properties, "jackrack", NULL );
+ }
+
+ // Get the filter-specific properties
+ LADSPA_Data **input_buffers = mlt_properties_get_data( filter_properties, "input_buffers", NULL );
+ LADSPA_Data **output_buffers = mlt_properties_get_data( filter_properties, "output_buffers", NULL );
+
+ // Process the audio
+ int16_t *q = *buffer;
+ int i, j;
+
+ // Convert to floats and write into output ringbuffer
+ for ( i = 0; i < *samples; i++ )
+ for ( j = 0; j < *channels; j++ )
+ input_buffers[ j ][ i ] = ( float )( *q ++ ) / 32768.0;
+
+ // Do LADSPA processing
+ if ( jackrack && process_ladspa( jackrack->procinfo, *samples, input_buffers, output_buffers) == 0 )
+ {
+ // Read from output buffer and convert from floats
+ q = *buffer;
+ for ( i = 0; i < *samples; i++ )
+ for ( j = 0; j < *channels; j++ )
+ {
+ if ( output_buffers[ j ][ i ] > 1.0 )
+ output_buffers[ j ][ i ] = 1.0;
+ else if ( output_buffers[ j ][ i ] < -1.0 )
+ output_buffers[ j ][ i ] = -1.0;
+
+ if ( output_buffers[ j ][ i ] > 0 )
+ *q ++ = 32767 * output_buffers[ j ][ i ];
+ else
+ *q ++ = 32768 * output_buffers[ j ][ i ];
+ }
+ }
+
+ return 0;
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ {
+ mlt_frame_push_audio( frame, this );
+ mlt_frame_push_audio( frame, ladspa_get_audio );
+ }
+
+ return frame;
+}
+
+
+static void filter_close( mlt_filter this )
+{
+ int i;
+ char mlt_name[20];
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ if ( mlt_properties_get_data( properties, "jackrack", NULL ) != NULL )
+ {
+ for ( i = 0; i < mlt_properties_get_int( properties, "channels" ); i++ )
+ {
+ snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+ mlt_pool_release( mlt_properties_get_data( properties, mlt_name, NULL ) );
+ snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+ mlt_pool_release( mlt_properties_get_data( properties, mlt_name, NULL ) );
+ }
+ mlt_pool_release( mlt_properties_get_data( properties, "output_buffers", NULL ) );
+ mlt_pool_release( mlt_properties_get_data( properties, "input_buffers", NULL ) );
+
+ jack_rack_t *jackrack = mlt_properties_get_data( properties, "jackrack", NULL );
+ jack_rack_destroy( jackrack );
+ }
+ this->parent.close = NULL;
+ mlt_service_close( &this->parent );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_ladspa_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ this->process = filter_process;
+ this->close = filter_close;
+
+ mlt_properties_set( properties, "src", arg );
+ }
+ return this;
+}
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ladspa.h>
+#include <libxml/tree.h>
+
+#include "jack_rack.h"
+#include "lock_free_fifo.h"
+#include "plugin_settings.h"
+
+#ifndef _
+#define _(x) x
+#endif
+#define _x (const xmlChar*)
+#define _s (const char*)
+
+jack_rack_t *
+jack_rack_new (const char * client_name, unsigned long channels)
+{
+ jack_rack_t *rack;
+
+ rack = g_malloc (sizeof (jack_rack_t));
+ rack->saved_plugins = NULL;
+ rack->channels = channels;
+ rack->procinfo = process_info_new (client_name, channels, FALSE, FALSE);
+ if (!rack->procinfo) {
+ g_free (rack);
+ return NULL;
+ }
+ rack->plugin_mgr = plugin_mgr_new ();
+ plugin_mgr_set_plugins (rack->plugin_mgr, channels);
+
+ return rack;
+}
+
+
+void
+jack_rack_destroy (jack_rack_t * jack_rack)
+{
+ process_quit (jack_rack->procinfo);
+ plugin_mgr_destroy (jack_rack->plugin_mgr);
+ process_info_destroy (jack_rack->procinfo);
+ g_slist_free (jack_rack->saved_plugins);
+ g_free (jack_rack);
+}
+
+plugin_t *
+jack_rack_instantiate_plugin (jack_rack_t * jack_rack, plugin_desc_t * desc)
+{
+ plugin_t * plugin;
+
+ /* check whether or not the plugin is RT capable and confirm with the user if it isn't */
+ if (!LADSPA_IS_HARD_RT_CAPABLE(desc->properties)) {
+ fprintf (stderr, "Plugin not RT capable. The plugin '%s' does not describe itself as being capable of real-time operation. You may experience drop outs or jack may even kick us out if you use it.\n",
+ desc->name);
+ }
+
+ /* create the plugin */
+ plugin = plugin_new (desc, jack_rack);
+
+ if (!plugin) {
+ fprintf (stderr, "Error loading file plugin '%s' from file '%s'\n",
+ desc->name, desc->object_file);
+ }
+
+ return plugin;
+}
+
+
+void
+jack_rack_add_saved_plugin (jack_rack_t * jack_rack, saved_plugin_t * saved_plugin)
+{
+ plugin_t * plugin = jack_rack_instantiate_plugin (jack_rack, saved_plugin->settings->desc);
+ if (!plugin)
+ return;
+ jack_rack->saved_plugins = g_slist_append (jack_rack->saved_plugins, saved_plugin);
+ process_add_plugin (jack_rack->procinfo, plugin);
+ jack_rack_add_plugin (jack_rack, plugin);
+}
+
+
+void
+jack_rack_add_plugin (jack_rack_t * jack_rack, plugin_t * plugin)
+{
+ saved_plugin_t * saved_plugin = NULL;
+ GSList * list;
+ unsigned long control, channel;
+ LADSPA_Data value;
+ guint copy;
+
+ /* see if there's any saved settings that match the plugin id */
+ for (list = jack_rack->saved_plugins; list; list = g_slist_next (list))
+ {
+ saved_plugin = list->data;
+
+ if (saved_plugin->settings->desc->id == plugin->desc->id)
+ {
+ /* process the settings! */
+ jack_rack->saved_plugins = g_slist_remove (jack_rack->saved_plugins, saved_plugin);
+ break;
+ }
+ saved_plugin = NULL;
+ }
+
+ /* initialize plugin parameters */
+ plugin->enabled = settings_get_enabled (saved_plugin->settings);
+ plugin->wet_dry_enabled = settings_get_wet_dry_enabled (saved_plugin->settings);
+
+ for (control = 0; control < saved_plugin->settings->desc->control_port_count; control++)
+ for (copy = 0; copy < plugin->copies; copy++)
+ {
+ value = settings_get_control_value (saved_plugin->settings, copy, control);
+ plugin->holders[copy].control_memory[control] = value;
+//printf("setting control value %s (%d) = %f\n", saved_plugin->settings->desc->port_names[control], copy, value);
+// lff_write (plugin->holders[copy].ui_control_fifos + control, &value);
+ }
+ if (plugin->wet_dry_enabled)
+ for (channel = 0; channel < jack_rack->channels; channel++)
+ {
+ value = settings_get_wet_dry_value (saved_plugin->settings, channel);
+ plugin->wet_dry_values[channel] = value;
+//printf("setting wet/dry value %d = %f\n", channel, value);
+// lff_write (plugin->wet_dry_fifos + channel, &value);
+ }
+}
+
+
+static void
+saved_rack_parse_plugin (jack_rack_t * jack_rack, saved_rack_t * saved_rack, saved_plugin_t * saved_plugin,
+ const char * filename, xmlNodePtr plugin)
+{
+ plugin_desc_t * desc;
+ settings_t * settings = NULL;
+ xmlNodePtr node;
+ xmlNodePtr sub_node;
+ xmlChar *content;
+ unsigned long num;
+ unsigned long control = 0;
+
+ for (node = plugin->children; node; node = node->next)
+ {
+ if (xmlStrcmp (node->name, _x("id")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ num = strtoul (_s(content), NULL, 10);
+ xmlFree (content);
+
+ desc = plugin_mgr_get_any_desc (jack_rack->plugin_mgr, num);
+ if (!desc)
+ {
+ fprintf (stderr, _("The file '%s' contains an unknown plugin with ID '%ld'; skipping\n"), filename, num);
+ return;
+ }
+
+ settings = settings_new (desc, saved_rack->channels, saved_rack->sample_rate);
+ }
+ else if (xmlStrcmp (node->name, _x("enabled")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ settings_set_enabled (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (node->name, _x("wet_dry_enabled")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ settings_set_wet_dry_enabled (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (node->name, _x("wet_dry_locked")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ settings_set_wet_dry_locked (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (node->name, _x("wet_dry_values")) == 0)
+ {
+ unsigned long channel = 0;
+
+ for (sub_node = node->children; sub_node; sub_node = sub_node->next)
+ {
+ if (xmlStrcmp (sub_node->name, _x("value")) == 0)
+ {
+ content = xmlNodeGetContent (sub_node);
+ settings_set_wet_dry_value (settings, channel, strtod (_s(content), NULL));
+ xmlFree (content);
+
+ channel++;
+ }
+ }
+ }
+ else if (xmlStrcmp (node->name, _x("lockall")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ settings_set_lock_all (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (node->name, _x("controlrow")) == 0)
+ {
+ gint copy = 0;
+
+ for (sub_node = node->children; sub_node; sub_node = sub_node->next)
+ {
+ if (xmlStrcmp (sub_node->name, _x("lock")) == 0)
+ {
+ content = xmlNodeGetContent (sub_node);
+ settings_set_lock (settings, control, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (sub_node->name, _x("value")) == 0)
+ {
+ content = xmlNodeGetContent (sub_node);
+ settings_set_control_value (settings, copy, control, strtod (_s(content), NULL));
+ xmlFree (content);
+ copy++;
+ }
+ }
+
+ control++;
+ }
+ }
+
+ if (settings)
+ saved_plugin->settings = settings;
+}
+
+static void
+saved_rack_parse_jackrack (jack_rack_t * jack_rack, saved_rack_t * saved_rack, const char * filename, xmlNodePtr jackrack)
+{
+ xmlNodePtr node;
+ xmlChar *content;
+ saved_plugin_t * saved_plugin;
+
+ for (node = jackrack->children; node; node = node->next)
+ {
+ if (xmlStrcmp (node->name, _x("channels")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ saved_rack->channels = strtoul (_s(content), NULL, 10);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (node->name, _x("samplerate")) == 0)
+ {
+ content = xmlNodeGetContent (node);
+ saved_rack->sample_rate = strtoul (_s(content), NULL, 10);
+ xmlFree (content);
+ }
+ else if (xmlStrcmp (node->name, _x("plugin")) == 0)
+ {
+ saved_plugin = g_malloc0 (sizeof (saved_plugin_t));
+ saved_rack->plugins = g_slist_append (saved_rack->plugins, saved_plugin);
+ saved_rack_parse_plugin (jack_rack, saved_rack, saved_plugin, filename, node);
+ }
+ }
+}
+
+static saved_rack_t *
+saved_rack_new (jack_rack_t * jack_rack, const char * filename, xmlDocPtr doc)
+{
+ xmlNodePtr node;
+ saved_rack_t *saved_rack;
+
+ /* create the saved rack */
+ saved_rack = g_malloc (sizeof (saved_rack_t));
+ saved_rack->plugins = NULL;
+ saved_rack->sample_rate = 48000;
+ saved_rack->channels = 2;
+
+ for (node = doc->children; node; node = node->next)
+ {
+ if (xmlStrcmp (node->name, _x("jackrack")) == 0)
+ saved_rack_parse_jackrack (jack_rack, saved_rack, filename, node);
+ }
+
+ return saved_rack;
+}
+
+static void
+saved_rack_destroy (saved_rack_t * saved_rack)
+{
+ GSList * list;
+
+ for (list = saved_rack->plugins; list; list = g_slist_next (list))
+ settings_destroy (((saved_plugin_t *) list->data)->settings);
+ g_slist_free (saved_rack->plugins);
+ g_free (saved_rack);
+}
+
+
+int
+jack_rack_open_file (jack_rack_t * jack_rack, const char * filename)
+{
+ xmlDocPtr doc;
+ saved_rack_t * saved_rack;
+ GSList * list;
+ saved_plugin_t * saved_plugin;
+
+ doc = xmlParseFile (filename);
+ if (!doc)
+ {
+ fprintf (stderr, _("Could not parse file '%s'\n"), filename);
+ return 1;
+ }
+
+ if (xmlStrcmp ( ((xmlDtdPtr)doc->children)->name, _x("jackrack")) != 0)
+ {
+ fprintf (stderr, _("The file '%s' is not a JACK Rack settings file\n"), filename);
+ return 1;
+ }
+
+ saved_rack = saved_rack_new (jack_rack, filename, doc);
+ xmlFreeDoc (doc);
+
+ if (!saved_rack)
+ return 1;
+
+ for (list = saved_rack->plugins; list; list = g_slist_next (list))
+ {
+ saved_plugin = list->data;
+
+ settings_set_sample_rate (saved_plugin->settings, sample_rate);
+
+ jack_rack_add_saved_plugin (jack_rack, saved_plugin);
+ }
+
+ saved_rack_destroy (saved_rack);
+
+ return 0;
+}
+
+
+/* EOF */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_JACK_RACK_H__
+#define __JR_JACK_RACK_H__
+
+#include <glib.h>
+#include <ladspa.h>
+
+#include "plugin.h"
+#include "plugin_mgr.h"
+#include "plugin_settings.h"
+#include "process.h"
+
+typedef struct _saved_plugin saved_plugin_t;
+
+struct _saved_plugin
+{
+ settings_t *settings;
+};
+
+typedef struct _saved_rack saved_rack_t;
+
+struct _saved_rack
+{
+ unsigned long channels;
+ jack_nframes_t sample_rate;
+ GSList * plugins;
+};
+
+typedef struct _jack_rack jack_rack_t;
+
+struct _jack_rack
+{
+ plugin_mgr_t * plugin_mgr;
+ process_info_t * procinfo;
+ unsigned long channels;
+ GSList * saved_plugins;
+};
+
+jack_rack_t * jack_rack_new (const char * client_name, unsigned long channels);
+void jack_rack_destroy (jack_rack_t * jack_rack);
+
+int jack_rack_open_file (jack_rack_t * jack_rack, const char * filename);
+void jack_rack_add_plugin (jack_rack_t * jack_rack, plugin_t * plugin);
+void jack_rack_add_saved_plugin (jack_rack_t * jack_rack, struct _saved_plugin * saved_plugin);
+
+plugin_t * jack_rack_instantiate_plugin (jack_rack_t * jack_rack, plugin_desc_t * desc);
+
+#endif /* __JR_JACK_RACK_H__ */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "lock_free_fifo.h"
+
+/** initialise a lock free fifo */
+
+void
+lff_init (lff_t * lff, unsigned int size, size_t object_size)
+{
+ lff->size = size;
+ lff->object_size = object_size;
+ lff->read_index = 0;
+ lff->write_index = 0;
+ lff->data = g_malloc (object_size * size);
+}
+
+lff_t *
+lff_new (unsigned int size, size_t object_size)
+{
+ lff_t * lff;
+
+ lff = g_malloc (sizeof (lff_t));
+
+ lff_init (lff, size, object_size);
+
+ return lff;
+}
+
+void
+lff_free (lff_t * lff)
+{
+ g_free (lff->data);
+}
+
+void
+lff_destroy (lff_t * lff)
+{
+ lff_free (lff);
+ g_free (lff);
+}
+
+/** read an element from the fifo into data.
+returns 0 on success, non-zero if there were no elements to read */
+int lff_read (lff_t * lff, void * data) {
+ if (lff->read_index == lff->write_index) {
+ return -1;
+ } else {
+ memcpy (data, ((char *)lff->data) + (lff->read_index * lff->object_size),
+ lff->object_size);
+ lff->read_index++;
+ if (lff->read_index >= lff->size) {
+ lff->read_index = 0;
+ }
+ return 0;
+ }
+}
+
+/** write an element from data to the fifo.
+returns 0 on success, non-zero if there was no space */
+int lff_write (lff_t * lff, void * data) {
+ static unsigned int ri;
+
+ /* got to read read_index only once for safety */
+ ri = lff->read_index;
+
+ /* lots of logic for when we're allowed to write to the fifo which basically
+ boils down to "don't write if we're one element behind the read index" */
+ if ((ri > lff->write_index && ri - lff->write_index > 1) ||
+ (lff->write_index >= ri && lff->write_index != lff->size - 1) ||
+ (lff->write_index >= ri && lff->write_index == lff->size - 1 && ri != 0)) {
+
+/* if ((ri > lff->write_index && ri - lff->write_index > 1) ||
+ (lff->write_index >= ri && (lff->write_index != lff->size - 1 || ri != 0))) { */
+
+ memcpy (((char *)lff->data) + (lff->write_index * lff->object_size),
+ data, lff->object_size);
+
+ /* FIXME: is this safe? */
+ lff->write_index++;
+ if (lff->write_index >= lff->size) {
+ lff->write_index = 0;
+ }
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JLH_LOCK_FREE_FIFO_H__
+#define __JLH_LOCK_FREE_FIFO_H__
+
+/** lock free fifo ring buffer structure */
+typedef struct lock_free_fifo {
+ /** Size of the ringbuffer (in elements) */
+ unsigned int size;
+ /** the memory containing the ringbuffer */
+ void * data;
+ /** the size of an element */
+ size_t object_size;
+ /** the current position of the reader */
+ unsigned int read_index;
+ /** the current position of the writer */
+ unsigned int write_index;
+} lff_t;
+
+void lff_init (lff_t * lff, unsigned int size, size_t object_size);
+void lff_free (lff_t * lff);
+
+lff_t * lff_new (unsigned int size, size_t object_size);
+void lff_destroy (lff_t * lock_free_fifo);
+
+int lff_read (lff_t * lock_free_fifo, void * data);
+int lff_write (lff_t * lock_free_fifo, void * data);
+
+
+#endif /* __JLH_LOCK_FREE_FIFO_H__ */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ladspa.h>
+#include <dlfcn.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "plugin.h"
+#include "jack_rack.h"
+#include "process.h"
+
+#define CONTROL_FIFO_SIZE 128
+
+
+
+/* swap over the jack ports in two plugins */
+static void
+plugin_swap_aux_ports (plugin_t * plugin, plugin_t * other)
+{
+ guint copy;
+ jack_port_t ** aux_ports_tmp;
+
+ for (copy = 0; copy < plugin->copies; copy++)
+ {
+ aux_ports_tmp = other->holders[copy].aux_ports;
+ other->holders[copy].aux_ports = plugin->holders[copy].aux_ports;
+ plugin->holders[copy].aux_ports = aux_ports_tmp;
+ }
+}
+
+/** connect up the ladspa instance's input buffers to the previous
+ plugin's audio memory. make sure to check that plugin->prev
+ exists. */
+void
+plugin_connect_input_ports (plugin_t * plugin, LADSPA_Data ** inputs)
+{
+ gint copy;
+ unsigned long channel;
+ unsigned long rack_channel;
+
+ if (!plugin || !inputs)
+ return;
+
+ rack_channel = 0;
+ for (copy = 0; copy < plugin->copies; copy++)
+ {
+ for (channel = 0; channel < plugin->desc->channels; channel++)
+ {
+ plugin->descriptor->
+ connect_port (plugin->holders[copy].instance,
+ plugin->desc->audio_input_port_indicies[channel],
+ inputs[rack_channel]);
+ rack_channel++;
+ }
+ }
+
+ plugin->audio_input_memory = inputs;
+}
+
+/** connect up a plugin's output ports to its own audio_output_memory output memory */
+void
+plugin_connect_output_ports (plugin_t * plugin)
+{
+ gint copy;
+ unsigned long channel;
+ unsigned long rack_channel = 0;
+
+ if (!plugin)
+ return;
+
+
+ for (copy = 0; copy < plugin->copies; copy++)
+ {
+ for (channel = 0; channel < plugin->desc->channels; channel++)
+ {
+ plugin->descriptor->
+ connect_port (plugin->holders[copy].instance,
+ plugin->desc->audio_output_port_indicies[channel],
+ plugin->audio_output_memory[rack_channel]);
+ rack_channel++;
+ }
+ }
+}
+
+void
+process_add_plugin (process_info_t * procinfo, plugin_t * plugin)
+{
+
+ /* sort out list pointers */
+ plugin->next = NULL;
+ plugin->prev = procinfo->chain_end;
+
+ if (procinfo->chain_end)
+ procinfo->chain_end->next = plugin;
+ else
+ procinfo->chain = plugin;
+
+ procinfo->chain_end = plugin;
+
+}
+
+
+/** remove a plugin from the chain */
+plugin_t *
+process_remove_plugin (process_info_t * procinfo, plugin_t *plugin)
+{
+ /* sort out chain pointers */
+ if (plugin->prev)
+ plugin->prev->next = plugin->next;
+ else
+ procinfo->chain = plugin->next;
+
+ if (plugin->next)
+ plugin->next->prev = plugin->prev;
+ else
+ procinfo->chain_end = plugin->prev;
+
+ /* sort out the aux ports */
+ if (procinfo->jack_client && plugin->desc->aux_channels > 0)
+ {
+ plugin_t * other;
+
+ for (other = plugin->next; other; other = other->next)
+ if (other->desc->id == plugin->desc->id)
+ plugin_swap_aux_ports (plugin, other);
+ }
+
+ return plugin;
+}
+
+/** enable/disable a plugin */
+void
+process_ablise_plugin (process_info_t * procinfo, plugin_t *plugin, gboolean enable)
+{
+ plugin->enabled = enable;
+}
+
+/** enable/disable a plugin */
+void
+process_ablise_plugin_wet_dry (process_info_t * procinfo, plugin_t *plugin, gboolean enable)
+{
+ plugin->wet_dry_enabled = enable;
+}
+
+/** move a plugin up or down one place in the chain */
+void
+process_move_plugin (process_info_t * procinfo, plugin_t *plugin, gint up)
+{
+ /* other plugins in the chain */
+ plugin_t *pp = NULL, *p, *n, *nn = NULL;
+
+ /* note that we should never recieve an illogical move request
+ ie, there will always be at least 1 plugin before for an up
+ request or 1 plugin after for a down request */
+
+ /* these are pointers to the plugins surrounding the specified one:
+ { pp, p, plugin, n, nn } which makes things much clearer than
+ tptr, tptr2 etc */
+ p = plugin->prev;
+ if (p) pp = p->prev;
+ n = plugin->next;
+ if (n) nn = n->next;
+
+ if (up)
+ {
+ if (!p)
+ return;
+
+ if (pp)
+ pp->next = plugin;
+ else
+ procinfo->chain = plugin;
+
+ p->next = n;
+ p->prev = plugin;
+
+ plugin->prev = pp;
+ plugin->next = p;
+
+ if (n)
+ n->prev = p;
+ else
+ procinfo->chain_end = p;
+
+ }
+ else
+ {
+ if (!n)
+ return;
+
+ if (p)
+ p->next = n;
+ else
+ procinfo->chain = n;
+
+ n->prev = p;
+ n->next = plugin;
+
+ plugin->prev = n;
+ plugin->next = nn;
+
+ if (nn)
+ nn->prev = plugin;
+ else
+ procinfo->chain_end = plugin;
+ }
+
+ if (procinfo->jack_client && plugin->desc->aux_channels > 0)
+ {
+ plugin_t * other;
+ other = up ? plugin->next : plugin->prev;
+
+ /* swap around the jack ports */
+ if (other->desc->id == plugin->desc->id)
+ plugin_swap_aux_ports (plugin, other);
+ }
+}
+
+/** exchange an existing plugin for a newly created one */
+plugin_t *
+process_change_plugin (process_info_t * procinfo,
+ plugin_t *plugin, plugin_t * new_plugin)
+{
+ new_plugin->next = plugin->next;
+ new_plugin->prev = plugin->prev;
+
+ if (plugin->prev)
+ plugin->prev->next = new_plugin;
+ else
+ procinfo->chain = new_plugin;
+
+ if (plugin->next)
+ plugin->next->prev = new_plugin;
+ else
+ procinfo->chain_end = new_plugin;
+
+ /* sort out the aux ports */
+ if (procinfo->jack_client && plugin->desc->aux_channels > 0)
+ {
+ plugin_t * other;
+
+ for (other = plugin->next; other; other = other->next)
+ if (other->desc->id == plugin->desc->id)
+ plugin_swap_aux_ports (plugin, other);
+ }
+
+ return plugin;
+}
+
+
+/******************************************
+ ************* non RT stuff ***************
+ ******************************************/
+
+
+static int
+plugin_open_plugin (plugin_desc_t * desc,
+ void ** dl_handle_ptr,
+ const LADSPA_Descriptor ** descriptor_ptr)
+{
+ void * dl_handle;
+ const char * dlerr;
+ LADSPA_Descriptor_Function get_descriptor;
+
+ /* open the object file */
+ dl_handle = dlopen (desc->object_file, RTLD_NOW|RTLD_GLOBAL);
+ if (!dl_handle)
+ {
+ fprintf (stderr, "%s: error opening shared object file '%s': %s\n",
+ __FUNCTION__, desc->object_file, dlerror());
+ return 1;
+ }
+
+
+ /* get the get_descriptor function */
+ dlerror (); /* clear the error report */
+
+ get_descriptor = (LADSPA_Descriptor_Function)
+ dlsym (dl_handle, "ladspa_descriptor");
+
+ dlerr = dlerror();
+ if (dlerr)
+ {
+ fprintf (stderr, "%s: error finding descriptor symbol in object file '%s': %s\n",
+ __FUNCTION__, desc->object_file, dlerr);
+ dlclose (dl_handle);
+ return 1;
+ }
+
+ *descriptor_ptr = get_descriptor (desc->index);
+ *dl_handle_ptr = dl_handle;
+
+ return 0;
+}
+
+static int
+plugin_instantiate (const LADSPA_Descriptor * descriptor,
+ unsigned long plugin_index,
+ gint copies,
+ LADSPA_Handle * instances)
+{
+ gint i;
+
+ for (i = 0; i < copies; i++)
+ {
+ instances[i] = descriptor->instantiate (descriptor, sample_rate);
+
+ if (!instances[i])
+ {
+ unsigned long d;
+
+ for (d = 0; d < i; d++)
+ descriptor->cleanup (instances[d]);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+plugin_create_aux_ports (plugin_t * plugin, guint copy, jack_rack_t * jack_rack)
+{
+ plugin_desc_t * desc;
+// plugin_slot_t * slot;
+ unsigned long aux_channel = 1;
+ unsigned long plugin_index = 1;
+ unsigned long i;
+ char port_name[64];
+ char * plugin_name;
+ char * ptr;
+// GList * list;
+ ladspa_holder_t * holder;
+
+ desc = plugin->desc;
+ holder = plugin->holders + copy;
+
+ holder->aux_ports = g_malloc (sizeof (jack_port_t *) * desc->aux_channels);
+
+ /* make the plugin name jack worthy */
+ ptr = plugin_name = g_strndup (plugin->desc->name, 7);
+ while (*ptr != '\0')
+ {
+ if (*ptr == ' ')
+ *ptr = '_';
+ else
+ *ptr = tolower (*ptr);
+
+ ptr++;
+ }
+
+/*
+ for (list = jack_rack->slots; list; list = g_list_next (list))
+ {
+ slot = (plugin_slot_t *) list->data;
+
+ if (slot->plugin->desc->id == plugin->desc->id)
+ plugin_index++;
+ }
+*/
+
+ for (i = 0; i < desc->aux_channels; i++, aux_channel++)
+ {
+ sprintf (port_name, "%s_%ld-%d_%c%ld",
+ plugin_name,
+ plugin_index,
+ copy + 1,
+ desc->aux_are_input ? 'i' : 'o',
+ aux_channel);
+
+ holder->aux_ports[i] =
+ jack_port_register (jack_rack->procinfo->jack_client,
+ port_name,
+ JACK_DEFAULT_AUDIO_TYPE,
+ desc->aux_are_input ? JackPortIsInput : JackPortIsOutput,
+ 0);
+
+ if (!holder->aux_ports[i])
+ {
+ fprintf (stderr, "Could not register jack port '%s'; aborting\n", port_name);
+ abort ();
+ }
+ }
+
+ g_free (plugin_name);
+}
+
+static LADSPA_Data unused_control_port_output;
+
+static void
+plugin_init_holder (plugin_t * plugin,
+ guint copy,
+ LADSPA_Handle instance,
+ jack_rack_t * jack_rack)
+{
+ unsigned long i;
+ plugin_desc_t * desc;
+ ladspa_holder_t * holder;
+
+ desc = plugin->desc;
+ holder = plugin->holders + copy;
+
+ holder->instance = instance;
+
+ if (desc->control_port_count > 0)
+ {
+ holder->ui_control_fifos = g_malloc (sizeof (lff_t) * desc->control_port_count);
+ holder->control_memory = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count);
+ }
+ else
+ {
+ holder->ui_control_fifos = NULL;
+ holder->control_memory = NULL;
+ }
+
+ for (i = 0; i < desc->control_port_count; i++)
+ {
+ lff_init (holder->ui_control_fifos + i, CONTROL_FIFO_SIZE, sizeof (LADSPA_Data));
+ holder->control_memory[i] =
+ plugin_desc_get_default_control_value (desc, desc->control_port_indicies[i], sample_rate);
+
+ plugin->descriptor->
+ connect_port (instance, desc->control_port_indicies[i], holder->control_memory + i);
+ }
+
+ for (i = 0; i < desc->port_count; i++)
+ {
+ if (!LADSPA_IS_PORT_CONTROL (desc->port_descriptors[i]))
+ continue;
+
+ if (LADSPA_IS_PORT_OUTPUT (desc->port_descriptors[i]))
+ plugin->descriptor-> connect_port (instance, i, &unused_control_port_output);
+ }
+
+ if (jack_rack->procinfo->jack_client && plugin->desc->aux_channels > 0)
+ plugin_create_aux_ports (plugin, copy, jack_rack);
+
+ if (plugin->descriptor->activate)
+ plugin->descriptor->activate (instance);
+}
+
+
+plugin_t *
+plugin_new (plugin_desc_t * desc, jack_rack_t * jack_rack)
+{
+ void * dl_handle;
+ const LADSPA_Descriptor * descriptor;
+ LADSPA_Handle * instances;
+ gint copies;
+ unsigned long i;
+ int err;
+ plugin_t * plugin;
+
+ /* open the plugin */
+ err = plugin_open_plugin (desc, &dl_handle, &descriptor);
+ if (err)
+ return NULL;
+
+ /* create the instances */
+ copies = plugin_desc_get_copies (desc, jack_rack->channels);
+ instances = g_malloc (sizeof (LADSPA_Handle) * copies);
+
+ err = plugin_instantiate (descriptor, desc->index, copies, instances);
+ if (err)
+ {
+ g_free (instances);
+ dlclose (dl_handle);
+ return NULL;
+ }
+
+
+ plugin = g_malloc (sizeof (plugin_t));
+
+ plugin->descriptor = descriptor;
+ plugin->dl_handle = dl_handle;
+ plugin->desc = desc;
+ plugin->copies = copies;
+ plugin->enabled = FALSE;
+ plugin->next = NULL;
+ plugin->prev = NULL;
+ plugin->wet_dry_enabled = FALSE;
+ plugin->jack_rack = jack_rack;
+
+ /* create audio memory and wet/dry stuff */
+ plugin->audio_output_memory = g_malloc (sizeof (LADSPA_Data *) * jack_rack->channels);
+ plugin->wet_dry_fifos = g_malloc (sizeof (lff_t) * jack_rack->channels);
+ plugin->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * jack_rack->channels);
+
+ for (i = 0; i < jack_rack->channels; i++)
+ {
+ plugin->audio_output_memory[i] = g_malloc (sizeof (LADSPA_Data) * buffer_size);
+ lff_init (plugin->wet_dry_fifos + i, CONTROL_FIFO_SIZE, sizeof (LADSPA_Data));
+ plugin->wet_dry_values[i] = 1.0;
+ }
+
+ /* create holders and fill them out */
+ plugin->holders = g_malloc (sizeof (ladspa_holder_t) * copies);
+ for (i = 0; i < copies; i++)
+ plugin_init_holder (plugin, i, instances[i], jack_rack);
+
+ return plugin;
+}
+
+
+void
+plugin_destroy (plugin_t * plugin)
+{
+ unsigned long i, j;
+ int err;
+
+ /* destroy holders */
+ for (i = 0; i < plugin->copies; i++)
+ {
+ if (plugin->descriptor->deactivate)
+ plugin->descriptor->deactivate (plugin->holders[i].instance);
+
+/* if (plugin->descriptor->cleanup)
+ plugin->descriptor->cleanup (plugin->holders[i].instance); */
+
+ if (plugin->desc->control_port_count > 0)
+ {
+ for (j = 0; j < plugin->desc->control_port_count; j++)
+ {
+ lff_free (plugin->holders[i].ui_control_fifos + j);
+ }
+ g_free (plugin->holders[i].ui_control_fifos);
+ g_free (plugin->holders[i].control_memory);
+ }
+
+ /* aux ports */
+ if (plugin->jack_rack->procinfo->jack_client && plugin->desc->aux_channels > 0)
+ {
+ for (j = 0; j < plugin->desc->aux_channels; j++)
+ {
+ err = jack_port_unregister (plugin->jack_rack->procinfo->jack_client,
+ plugin->holders[i].aux_ports[j]);
+
+ if (err)
+ fprintf (stderr, "%s: could not unregister jack port\n", __FUNCTION__);
+ }
+
+ g_free (plugin->holders[i].aux_ports);
+ }
+ }
+
+ g_free (plugin->holders);
+
+ for (i = 0; i < plugin->jack_rack->channels; i++)
+ {
+ g_free (plugin->audio_output_memory[i]);
+ lff_free (plugin->wet_dry_fifos + i);
+ }
+
+ g_free (plugin->audio_output_memory);
+ g_free (plugin->wet_dry_fifos);
+ g_free (plugin->wet_dry_values);
+
+ err = dlclose (plugin->dl_handle);
+ if (err)
+ {
+ fprintf (stderr, "%s: error closing shared object '%s': %s\n",
+ __FUNCTION__, plugin->desc->object_file, dlerror ());
+ }
+
+ g_free (plugin);
+}
+
+
+/* EOF */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_H__
+#define __JR_PLUGIN_H__
+
+#include <glib.h>
+#include <ladspa.h>
+#include <jack/jack.h>
+
+#include "process.h"
+#include "plugin_desc.h"
+
+typedef struct _ladspa_holder ladspa_holder_t;
+typedef struct _plugin plugin_t;
+
+struct _ladspa_holder
+{
+ LADSPA_Handle instance;
+ lff_t * ui_control_fifos;
+ LADSPA_Data * control_memory;
+
+ jack_port_t ** aux_ports;
+};
+
+struct _plugin
+{
+ plugin_desc_t * desc;
+ gint enabled;
+
+ gint copies;
+ ladspa_holder_t * holders;
+ LADSPA_Data ** audio_input_memory;
+ LADSPA_Data ** audio_output_memory;
+
+ gboolean wet_dry_enabled;
+ /* 1.0 = all wet, 0.0 = all dry, 0.5 = 50% wet/50% dry */
+ LADSPA_Data * wet_dry_values;
+ lff_t * wet_dry_fifos;
+
+ plugin_t * next;
+ plugin_t * prev;
+
+ const LADSPA_Descriptor * descriptor;
+ void * dl_handle;
+ struct _jack_rack * jack_rack;
+
+};
+
+void process_add_plugin (process_info_t *, plugin_t *plugin);
+plugin_t * process_remove_plugin (process_info_t *, plugin_t *plugin);
+void process_ablise_plugin (process_info_t *, plugin_t *plugin, gboolean able);
+void process_ablise_plugin_wet_dry (process_info_t *, plugin_t *plugin, gboolean enable);
+void process_move_plugin (process_info_t *, plugin_t *plugin, gint up);
+plugin_t * process_change_plugin (process_info_t *, plugin_t *plugin, plugin_t * new_plugin);
+
+struct _jack_rack;
+struct _ui;
+
+plugin_t * plugin_new (plugin_desc_t * plugin_desc, struct _jack_rack * jack_rack);
+void plugin_destroy (plugin_t * plugin);
+
+void plugin_connect_input_ports (plugin_t * plugin, LADSPA_Data ** inputs);
+void plugin_connect_output_ports (plugin_t * plugin);
+
+#endif /* __JR_PLUGIN_H__ */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+
+#include "plugin_desc.h"
+#include "plugin.h"
+
+#define set_string_property(property, value) \
+ \
+ if (property) \
+ g_free (property); \
+ \
+ if (value) \
+ (property) = g_strdup (value); \
+ else \
+ (property) = NULL;
+
+
+void
+plugin_desc_set_ports (plugin_desc_t * pd,
+ unsigned long port_count,
+ const LADSPA_PortDescriptor * port_descriptors,
+ const LADSPA_PortRangeHint * port_range_hints,
+ const char * const * port_names);
+
+
+
+static void
+plugin_desc_init (plugin_desc_t * pd)
+{
+ pd->object_file = NULL;
+ pd->id = 0;
+ pd->name = NULL;
+ pd->properties = 0;
+ pd->channels = 0;
+ pd->port_count = 0;
+ pd->port_descriptors = NULL;
+ pd->port_range_hints = NULL;
+ pd->audio_input_port_indicies = NULL;
+ pd->audio_output_port_indicies = NULL;
+ pd->audio_aux_port_indicies = NULL;
+ pd->control_port_count = 0;
+ pd->control_port_indicies = NULL;
+ pd->aux_channels = 0;
+ pd->aux_are_input = TRUE;
+}
+
+static void
+plugin_desc_free_ports (plugin_desc_t * pd)
+{
+ if (pd->port_count)
+ {
+ g_free (pd->port_descriptors);
+ g_free (pd->port_range_hints);
+ pd->port_descriptors = NULL;
+ pd->port_range_hints = NULL;
+ pd->port_count = 0;
+ }
+}
+
+static void
+plugin_desc_free (plugin_desc_t * pd)
+{
+ plugin_desc_set_object_file (pd, NULL);
+ plugin_desc_set_name (pd, NULL);
+ plugin_desc_free_ports (pd);
+}
+
+plugin_desc_t *
+plugin_desc_new ()
+{
+ plugin_desc_t * pd;
+ pd = g_malloc (sizeof (plugin_desc_t));
+ plugin_desc_init (pd);
+ return pd;
+}
+
+plugin_desc_t *
+plugin_desc_new_with_descriptor (const char * object_file,
+ unsigned long index,
+ const LADSPA_Descriptor * descriptor)
+{
+ plugin_desc_t * pd;
+ pd = plugin_desc_new ();
+
+ plugin_desc_set_object_file (pd, object_file);
+ plugin_desc_set_index (pd, index);
+ plugin_desc_set_id (pd, descriptor->UniqueID);
+ plugin_desc_set_name (pd, descriptor->Name);
+ plugin_desc_set_properties (pd, descriptor->Properties);
+ plugin_desc_set_ports (pd,
+ descriptor->PortCount,
+ descriptor->PortDescriptors,
+ descriptor->PortRangeHints,
+ descriptor->PortNames);
+
+ pd->rt = LADSPA_IS_HARD_RT_CAPABLE(pd->properties) ? TRUE : FALSE;
+
+ return pd;
+}
+
+void
+plugin_desc_destroy (plugin_desc_t * pd)
+{
+ plugin_desc_free (pd);
+ g_free (pd);
+}
+
+void
+plugin_desc_set_object_file (plugin_desc_t * pd, const char * object_file)
+{
+ set_string_property (pd->object_file, object_file);
+}
+
+void
+plugin_desc_set_index (plugin_desc_t * pd, unsigned long index)
+{
+ pd->index = index;
+}
+
+
+void
+plugin_desc_set_id (plugin_desc_t * pd, unsigned long id)
+{
+ pd->id = id;
+}
+
+void
+plugin_desc_set_name (plugin_desc_t * pd, const char * name)
+{
+ set_string_property (pd->name, name);
+}
+
+void
+plugin_desc_set_properties (plugin_desc_t * pd, LADSPA_Properties properties)
+{
+ pd->properties = properties;
+}
+
+static void
+plugin_desc_add_audio_port_index (unsigned long ** indicies,
+ unsigned long * current_port_count,
+ unsigned long index)
+{
+ (*current_port_count)++;
+
+ if (*current_port_count == 0)
+ *indicies = g_malloc (sizeof (unsigned long) * *current_port_count);
+ else
+ *indicies = g_realloc (*indicies, sizeof (unsigned long) * *current_port_count);
+
+ (*indicies)[*current_port_count - 1] = index;
+}
+
+static void
+plugin_desc_set_port_counts (plugin_desc_t * pd)
+{
+ unsigned long i;
+ unsigned long icount = 0;
+ unsigned long ocount = 0;
+
+ for (i = 0; i < pd->port_count; i++)
+ {
+ if (LADSPA_IS_PORT_AUDIO (pd->port_descriptors[i]))
+ {
+ if (LADSPA_IS_PORT_INPUT (pd->port_descriptors[i]))
+ plugin_desc_add_audio_port_index (&pd->audio_input_port_indicies, &icount, i);
+ else
+ plugin_desc_add_audio_port_index (&pd->audio_output_port_indicies, &ocount, i);
+ }
+ else
+ {
+ if (LADSPA_IS_PORT_OUTPUT (pd->port_descriptors[i]))
+ continue;
+
+ pd->control_port_count++;
+ if (pd->control_port_count == 0)
+ pd->control_port_indicies = g_malloc (sizeof (unsigned long) * pd->control_port_count);
+ else
+ pd->control_port_indicies = g_realloc (pd->control_port_indicies,
+ sizeof (unsigned long) * pd->control_port_count);
+
+ pd->control_port_indicies[pd->control_port_count - 1] = i;
+ }
+ }
+
+ if (icount == ocount)
+ pd->channels = icount;
+ else
+ { /* deal with auxilliary ports */
+ unsigned long ** port_indicies;
+ unsigned long port_count;
+ unsigned long i, j;
+
+ if (icount > ocount)
+ {
+ pd->channels = ocount;
+ pd->aux_channels = icount - ocount;
+ pd->aux_are_input = TRUE;
+ port_indicies = &pd->audio_input_port_indicies;
+ port_count = icount;
+ }
+ else
+ {
+ pd->channels = icount;
+ pd->aux_channels = ocount - icount;
+ pd->aux_are_input = FALSE;
+ port_indicies = &pd->audio_output_port_indicies;
+ port_count = ocount;
+ }
+
+ /* allocate indicies */
+ pd->audio_aux_port_indicies = g_malloc (sizeof (unsigned long) * pd->aux_channels);
+
+ /* copy indicies */
+ for (i = pd->channels, j = 0; i < port_count; i++, j++)
+ pd->audio_aux_port_indicies[j] = (*port_indicies)[i];
+
+ /* shrink the main indicies to only have channels indicies */
+ *port_indicies = g_realloc (*port_indicies, sizeof (unsigned long) * pd->channels);
+ }
+}
+
+void
+plugin_desc_set_ports (plugin_desc_t * pd,
+ unsigned long port_count,
+ const LADSPA_PortDescriptor * port_descriptors,
+ const LADSPA_PortRangeHint * port_range_hints,
+ const char * const * port_names)
+{
+ unsigned long i;
+
+ plugin_desc_free_ports (pd);
+
+ if (!port_count)
+ return;
+
+ pd->port_count = port_count;
+ pd->port_descriptors = g_malloc (sizeof (LADSPA_PortDescriptor) * port_count);
+ pd->port_range_hints = g_malloc (sizeof (LADSPA_PortRangeHint) * port_count);
+ pd->port_names = g_malloc (sizeof (char *) * port_count);
+
+ memcpy (pd->port_descriptors, port_descriptors, sizeof (LADSPA_PortDescriptor) * port_count);
+ memcpy (pd->port_range_hints, port_range_hints, sizeof (LADSPA_PortRangeHint) * port_count);
+
+ for (i = 0; i < port_count; i++)
+ pd->port_names[i] = g_strdup (port_names[i]);
+
+ plugin_desc_set_port_counts (pd);
+}
+
+
+LADSPA_Data
+plugin_desc_get_default_control_value (plugin_desc_t * pd, unsigned long port_index, guint32 sample_rate)
+{
+ LADSPA_Data upper, lower;
+ LADSPA_PortRangeHintDescriptor hint_descriptor;
+
+ hint_descriptor = pd->port_range_hints[port_index].HintDescriptor;
+
+ /* set upper and lower, possibly adjusted to the sample rate */
+ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+ upper = pd->port_range_hints[port_index].UpperBound * (LADSPA_Data) sample_rate;
+ lower = pd->port_range_hints[port_index].LowerBound * (LADSPA_Data) sample_rate;
+ } else {
+ upper = pd->port_range_hints[port_index].UpperBound;
+ lower = pd->port_range_hints[port_index].LowerBound;
+ }
+
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor))
+ {
+ if (lower < FLT_EPSILON)
+ lower = FLT_EPSILON;
+ }
+
+
+ if (LADSPA_IS_HINT_HAS_DEFAULT(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint_descriptor)) {
+
+ return lower;
+
+ } else if (LADSPA_IS_HINT_DEFAULT_LOW(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ return exp(log(lower) * 0.75 + log(upper) * 0.25);
+ } else {
+ return lower * 0.75 + upper * 0.25;
+ }
+
+ } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ return exp(log(lower) * 0.5 + log(upper) * 0.5);
+ } else {
+ return lower * 0.5 + upper * 0.5;
+ }
+
+ } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ return exp(log(lower) * 0.25 + log(upper) * 0.75);
+ } else {
+ return lower * 0.25 + upper * 0.75;
+ }
+
+ } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) {
+
+ return upper;
+
+ } else if (LADSPA_IS_HINT_DEFAULT_0(hint_descriptor)) {
+
+ return 0.0;
+
+ } else if (LADSPA_IS_HINT_DEFAULT_1(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+ return (LADSPA_Data) sample_rate;
+ } else {
+ return 1.0;
+ }
+
+ } else if (LADSPA_IS_HINT_DEFAULT_100(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+ return 100.0 * (LADSPA_Data) sample_rate;
+ } else {
+ return 100.0;
+ }
+
+ } else if (LADSPA_IS_HINT_DEFAULT_440(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+ return 440.0 * (LADSPA_Data) sample_rate;
+ } else {
+ return 440.0;
+ }
+
+ }
+
+ } else { /* try and find a reasonable default */
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) {
+ return lower;
+ } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) {
+ return upper;
+ }
+ }
+
+ return 0.0;
+}
+
+LADSPA_Data
+plugin_desc_change_control_value (plugin_desc_t * pd,
+ unsigned long control_index,
+ LADSPA_Data value,
+ guint32 old_sample_rate,
+ guint32 new_sample_rate)
+{
+
+ if (LADSPA_IS_HINT_SAMPLE_RATE (pd->port_range_hints[control_index].HintDescriptor))
+ {
+ LADSPA_Data old_sr, new_sr;
+
+ old_sr = (LADSPA_Data) old_sample_rate;
+ new_sr = (LADSPA_Data) new_sample_rate;
+
+ value /= old_sr;
+ value *= new_sr;
+ }
+
+ return value;
+}
+
+gint
+plugin_desc_get_copies (plugin_desc_t * pd, unsigned long rack_channels)
+{
+ gint copies = 1;
+
+ if (pd->channels > rack_channels)
+ return 0;
+
+ while (pd->channels * copies < rack_channels)
+ copies++;
+
+ if (pd->channels * copies > rack_channels)
+ return 0;
+
+ return copies;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_DESC_H__
+#define __JR_PLUGIN_DESC_H__
+
+#include <ladspa.h>
+#include <glib.h>
+
+typedef struct _plugin_desc plugin_desc_t;
+
+struct _plugin_desc
+{
+ char * object_file;
+ unsigned long index;
+ unsigned long id;
+ char * name;
+ LADSPA_Properties properties;
+ gboolean rt;
+
+ unsigned long channels;
+
+ gboolean aux_are_input;
+ unsigned long aux_channels;
+
+ unsigned long port_count;
+ LADSPA_PortDescriptor * port_descriptors;
+ LADSPA_PortRangeHint * port_range_hints;
+ char ** port_names;
+
+ unsigned long * audio_input_port_indicies;
+ unsigned long * audio_output_port_indicies;
+
+ unsigned long * audio_aux_port_indicies;
+
+ unsigned long control_port_count;
+ unsigned long * control_port_indicies;
+};
+
+plugin_desc_t * plugin_desc_new ();
+plugin_desc_t * plugin_desc_new_with_descriptor (const char * object_file,
+ unsigned long index,
+ const LADSPA_Descriptor * descriptor);
+void plugin_desc_destroy ();
+
+void plugin_desc_set_object_file (plugin_desc_t * pd, const char * object_file);
+void plugin_desc_set_index (plugin_desc_t * pd, unsigned long index);
+void plugin_desc_set_id (plugin_desc_t * pd, unsigned long id);
+void plugin_desc_set_name (plugin_desc_t * pd, const char * name);
+void plugin_desc_set_properties (plugin_desc_t * pd, LADSPA_Properties properties);
+
+struct _plugin * plugin_desc_instantiate (plugin_desc_t * pd);
+
+LADSPA_Data plugin_desc_get_default_control_value (plugin_desc_t * pd, unsigned long port_index, guint32 sample_rate);
+LADSPA_Data plugin_desc_change_control_value (plugin_desc_t *, unsigned long, LADSPA_Data, guint32, guint32);
+
+gint plugin_desc_get_copies (plugin_desc_t * pd, unsigned long rack_channels);
+
+#endif /* __JR_PLUGIN_DESC_H__ */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <math.h>
+#include <strings.h>
+#include <ctype.h>
+#include <ladspa.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "plugin_mgr.h"
+#include "plugin_desc.h"
+
+
+static gboolean
+plugin_is_valid (const LADSPA_Descriptor * descriptor)
+{
+ unsigned long i;
+ unsigned long icount = 0;
+ unsigned long ocount = 0;
+
+ for (i = 0; i < descriptor->PortCount; i++)
+ {
+ if (!LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[i]))
+ continue;
+
+ if (LADSPA_IS_PORT_INPUT (descriptor->PortDescriptors[i]))
+ icount++;
+ else
+ ocount++;
+ }
+
+ if (icount == 0 || ocount == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+plugin_mgr_get_object_file_plugins (plugin_mgr_t * plugin_mgr, const char * filename)
+{
+ const char * dlerr;
+ void * dl_handle;
+ LADSPA_Descriptor_Function get_descriptor;
+ const LADSPA_Descriptor * descriptor;
+ unsigned long plugin_index;
+ plugin_desc_t * desc, * other_desc = NULL;
+ GSList * list;
+ gboolean exists;
+ int err;
+
+ /* open the object file */
+ dl_handle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
+ if (!dl_handle)
+ {
+ fprintf (stderr, "%s: error opening shared object file '%s': %s\n",
+ __FUNCTION__, filename, dlerror());
+ return;
+ }
+
+
+ /* get the get_descriptor function */
+ dlerror (); /* clear the error report */
+
+ get_descriptor = (LADSPA_Descriptor_Function)
+ dlsym (dl_handle, "ladspa_descriptor");
+
+ dlerr = dlerror();
+ if (dlerr) {
+ fprintf (stderr, "%s: error finding ladspa_descriptor symbol in object file '%s': %s\n",
+ __FUNCTION__, filename, dlerr);
+ dlclose (dl_handle);
+ return;
+ }
+
+ plugin_index = 0;
+ while ( (descriptor = get_descriptor (plugin_index)) )
+ {
+ if (!plugin_is_valid (descriptor))
+ {
+ plugin_index++;
+ continue;
+ }
+
+
+ /* check it doesn't already exist */
+ exists = FALSE;
+ for (list = plugin_mgr->all_plugins; list; list = g_slist_next (list))
+ {
+ other_desc = (plugin_desc_t *) list->data;
+
+ if (other_desc->id == descriptor->UniqueID)
+ {
+ exists = TRUE;
+ break;
+ }
+ }
+
+ if (exists)
+ {
+ printf ("Plugin %ld exists in both '%s' and '%s'; using version in '%s'\n",
+ descriptor->UniqueID, other_desc->object_file, filename, other_desc->object_file);
+ plugin_index++;
+ continue;
+ }
+
+
+ desc = plugin_desc_new_with_descriptor (filename, plugin_index, descriptor);
+ plugin_mgr->all_plugins = g_slist_append (plugin_mgr->all_plugins, desc);
+ plugin_index++;
+ plugin_mgr->plugin_count++;
+
+ /* print in the splash screen */
+ /* printf ("Loaded plugin '%s'\n", desc->name); */
+ }
+
+ err = dlclose (dl_handle);
+ if (err)
+ {
+ fprintf (stderr, "%s: error closing object file '%s': %s\n",
+ __FUNCTION__, filename, dlerror ());
+ }
+}
+
+static void
+plugin_mgr_get_dir_plugins (plugin_mgr_t * plugin_mgr, const char * dir)
+{
+ DIR * dir_stream;
+ struct dirent * dir_entry;
+ char * file_name;
+ int err;
+ size_t dirlen;
+
+ dir_stream = opendir (dir);
+ if (!dir_stream)
+ {
+/* fprintf (stderr, "%s: error opening directory '%s': %s\n",
+ __FUNCTION__, dir, strerror (errno)); */
+ return;
+ }
+
+ dirlen = strlen (dir);
+
+ while ( (dir_entry = readdir (dir_stream)) )
+ {
+ struct stat info;
+
+ if (strcmp (dir_entry->d_name, ".") == 0 ||
+ strcmp (dir_entry->d_name, "..") == 0)
+ continue;
+
+ file_name = g_malloc (dirlen + 1 + strlen (dir_entry->d_name) + 1);
+
+ strcpy (file_name, dir);
+ if (file_name[dirlen - 1] == '/')
+ strcpy (file_name + dirlen, dir_entry->d_name);
+ else
+ {
+ file_name[dirlen] = '/';
+ strcpy (file_name + dirlen + 1, dir_entry->d_name);
+ }
+
+ stat (file_name, &info);
+ if (S_ISDIR (info.st_mode))
+ plugin_mgr_get_dir_plugins (plugin_mgr, file_name);
+ else
+ plugin_mgr_get_object_file_plugins (plugin_mgr, file_name);
+
+ g_free (file_name);
+ }
+
+ err = closedir (dir_stream);
+ if (err)
+ fprintf (stderr, "%s: error closing directory '%s': %s\n",
+ __FUNCTION__, dir, strerror (errno));
+}
+
+static void
+plugin_mgr_get_path_plugins (plugin_mgr_t * plugin_mgr)
+{
+ char * ladspa_path, * dir;
+
+ ladspa_path = g_strdup (getenv ("LADSPA_PATH"));
+ if (!ladspa_path)
+ ladspa_path = g_strdup ("/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa");
+
+ dir = strtok (ladspa_path, ":");
+ do
+ plugin_mgr_get_dir_plugins (plugin_mgr, dir);
+ while ((dir = strtok (NULL, ":")));
+
+ g_free (ladspa_path);
+}
+
+static gint
+plugin_mgr_sort (gconstpointer a, gconstpointer b)
+{
+ const plugin_desc_t * da;
+ const plugin_desc_t * db;
+ da = (const plugin_desc_t *) a;
+ db = (const plugin_desc_t *) b;
+
+ return strcasecmp (da->name, db->name);
+}
+
+plugin_mgr_t *
+plugin_mgr_new ()
+{
+ plugin_mgr_t * pm;
+
+ pm = g_malloc (sizeof (plugin_mgr_t));
+ pm->all_plugins = NULL;
+ pm->plugins = NULL;
+ pm->plugin_count = 0;
+
+ plugin_mgr_get_path_plugins (pm);
+
+ if (!pm->all_plugins)
+ {
+ fprintf (stderr, "No LADSPA plugins were found!\n\nCheck your LADSPA_PATH environment variable.\n");
+ abort ();
+ }
+
+ pm->all_plugins = g_slist_sort (pm->all_plugins, plugin_mgr_sort);
+
+ return pm;
+}
+
+void
+plugin_mgr_destroy (plugin_mgr_t * plugin_mgr)
+{
+ GSList * list;
+
+ for (list = plugin_mgr->all_plugins; list; list = g_slist_next (list))
+ plugin_desc_destroy ((plugin_desc_t *) list->data);
+
+ g_slist_free (plugin_mgr->plugins);
+ g_slist_free (plugin_mgr->all_plugins);
+ free (plugin_mgr);
+}
+
+
+void
+plugin_mgr_set_plugins (plugin_mgr_t * plugin_mgr, unsigned long rack_channels)
+{
+ GSList * list;
+ plugin_desc_t * desc;
+
+ /* clear the current plugins */
+ g_slist_free (plugin_mgr->plugins);
+ plugin_mgr->plugins = NULL;
+
+ for (list = plugin_mgr->all_plugins; list; list = g_slist_next (list))
+ {
+ desc = (plugin_desc_t *) list->data;
+
+ if (plugin_desc_get_copies (desc, rack_channels) != 0)
+ plugin_mgr->plugins = g_slist_append (plugin_mgr->plugins, desc);
+ }
+}
+
+static plugin_desc_t *
+plugin_mgr_find_desc (plugin_mgr_t * plugin_mgr, GSList * plugins, unsigned long id)
+{
+ GSList * list;
+ plugin_desc_t * desc;
+
+ for (list = plugins; list; list = g_slist_next (list))
+ {
+ desc = (plugin_desc_t *) list->data;
+
+ if (desc->id == id)
+ return desc;
+ }
+
+ return NULL;
+}
+
+plugin_desc_t *
+plugin_mgr_get_desc (plugin_mgr_t * plugin_mgr, unsigned long id)
+{
+ return plugin_mgr_find_desc (plugin_mgr, plugin_mgr->plugins, id);
+}
+
+plugin_desc_t *
+plugin_mgr_get_any_desc (plugin_mgr_t * plugin_mgr, unsigned long id)
+{
+ return plugin_mgr_find_desc (plugin_mgr, plugin_mgr->all_plugins, id);
+}
+
+
+/* EOF */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_MANAGER_H__
+#define __JR_PLUGIN_MANAGER_H__
+
+#include <glib.h>
+
+#include "plugin_desc.h"
+
+typedef struct _plugin_mgr plugin_mgr_t;
+
+struct _plugin_mgr
+{
+ GSList * all_plugins;
+
+ GSList * plugins;
+ unsigned long plugin_count;
+};
+
+struct _ui;
+
+plugin_mgr_t * plugin_mgr_new ();
+void plugin_mgr_destroy (plugin_mgr_t * plugin_mgr);
+
+void plugin_mgr_set_plugins (plugin_mgr_t * plugin_mgr, unsigned long rack_channels);
+
+plugin_desc_t * plugin_mgr_get_desc (plugin_mgr_t * plugin_mgr, unsigned long id);
+plugin_desc_t * plugin_mgr_get_any_desc (plugin_mgr_t * plugin_mgr, unsigned long id);
+
+#endif /* __JR_PLUGIN_MANAGER_H__ */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <math.h>
+
+#include "plugin_settings.h"
+
+
+static void
+settings_set_to_default (settings_t * settings, guint32 sample_rate)
+{
+ unsigned long control;
+ guint copy;
+ LADSPA_Data value;
+
+ for (control = 0; control < settings->desc->control_port_count; control++)
+ {
+ value = plugin_desc_get_default_control_value (settings->desc, control, sample_rate);
+
+ for (copy = 0; copy < settings->copies; copy++)
+ {
+ settings->control_values[copy][control] = value;
+ }
+
+ settings->locks[control] = TRUE;
+ }
+}
+
+settings_t *
+settings_new (plugin_desc_t * desc, unsigned long channels, guint32 sample_rate)
+{
+ settings_t * settings;
+ unsigned long channel;
+ guint copies;
+
+ settings = g_malloc (sizeof (settings_t));
+ copies = plugin_desc_get_copies (desc, channels);
+
+ settings->sample_rate = sample_rate;
+ settings->desc = desc;
+ settings->copies = copies;
+ settings->channels = channels;
+ settings->lock_all = TRUE;
+ settings->enabled = FALSE;
+ settings->locks = NULL;
+ settings->control_values = NULL;
+ settings->wet_dry_enabled = FALSE;
+ settings->wet_dry_locked = TRUE;
+
+ /* control settings */
+ if (desc->control_port_count > 0)
+ {
+ guint copy;
+
+ settings->locks = g_malloc (sizeof (gboolean) * desc->control_port_count);
+
+ settings->control_values = g_malloc (sizeof (LADSPA_Data *) * copies);
+ for (copy = 0; copy < copies; copy++)
+ {
+ settings->control_values[copy] = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count);
+ }
+
+ settings_set_to_default (settings, sample_rate);
+ }
+
+ /* wet/dry settings */
+ settings->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * channels);
+ for (channel = 0; channel < channels; channel++)
+ settings->wet_dry_values[channel] = 1.0;
+
+ return settings;
+}
+
+settings_t *
+settings_dup (settings_t * other)
+{
+ settings_t * settings;
+ plugin_desc_t * desc;
+ unsigned long channel;
+
+ settings = g_malloc (sizeof (settings_t));
+
+ settings->sample_rate = other->sample_rate;
+ settings->desc = other->desc;
+ settings->copies = settings_get_copies (other);
+ settings->channels = settings_get_channels (other);
+ settings->wet_dry_enabled = settings_get_wet_dry_enabled (other);
+ settings->wet_dry_locked = settings_get_wet_dry_locked (other);
+ settings->lock_all = settings_get_lock_all (other);
+ settings->enabled = settings_get_enabled (other);
+ settings->locks = NULL;
+ settings->control_values = NULL;
+
+ desc = other->desc;
+
+ if (desc->control_port_count > 0)
+ {
+ guint copy;
+ unsigned long control;
+
+ settings->locks = g_malloc (sizeof (gboolean) * desc->control_port_count);
+ for (control = 0; control < desc->control_port_count; control++)
+ settings_set_lock (settings, control, settings_get_lock (other, control));
+
+ settings->control_values = g_malloc (sizeof (LADSPA_Data *) * settings->copies);
+ for (copy = 0; copy < settings->copies; copy++)
+ {
+ settings->control_values[copy] = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count);
+
+ for (control = 0; control < desc->control_port_count; control++)
+ {
+ settings->control_values[copy][control] = settings_get_control_value (other, copy, control);
+ }
+ }
+ }
+
+ settings->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * settings->channels);
+ for (channel = 0; channel < settings->channels; channel++)
+ settings->wet_dry_values[channel] = settings_get_wet_dry_value (other, channel);
+
+ return settings;
+}
+
+void
+settings_destroy (settings_t * settings)
+{
+ if (settings->desc->control_port_count > 0)
+ {
+ guint i;
+ for (i = 0; i < settings->copies; i++)
+ g_free (settings->control_values[i]);
+
+ g_free (settings->control_values);
+ g_free (settings->locks);
+ }
+
+ g_free (settings->wet_dry_values);
+
+ g_free (settings);
+}
+
+static void
+settings_set_copies (settings_t * settings, guint copies)
+{
+ guint copy;
+ guint last_copy;
+ unsigned long control;
+
+ if (copies <= settings->copies)
+ return;
+
+ last_copy = settings->copies - 1;
+
+ settings->control_values = g_realloc (settings->control_values,
+ sizeof (LADSPA_Data *) * copies);
+
+ /* copy over the last settings to the new copies */
+ for (copy = settings->copies; copy < copies; copy++)
+ {
+ for (control = 0; control < settings->desc->control_port_count; control++)
+ {
+ settings->control_values[copy][control] =
+ settings->control_values[last_copy][control];
+ }
+ }
+
+ settings->copies = copies;
+}
+
+static void
+settings_set_channels (settings_t * settings, unsigned long channels)
+{
+ unsigned long channel;
+ LADSPA_Data last_value;
+
+ if (channels <= settings->channels)
+ return;
+
+ settings->wet_dry_values = g_realloc (settings->wet_dry_values, sizeof (LADSPA_Data) * channels);
+
+ last_value = settings->wet_dry_values[settings->channels - 1];
+
+ for (channel = settings->channels; channel < channels; channel++)
+ settings->wet_dry_values[channel] = last_value;
+
+ settings->channels = channels;
+}
+
+void
+settings_set_sample_rate (settings_t * settings, guint32 sample_rate)
+{
+ LADSPA_Data old_sample_rate;
+ LADSPA_Data new_sample_rate;
+
+ g_return_if_fail (settings != NULL);
+
+ if (settings->sample_rate == sample_rate)
+ return;
+
+ if (settings->desc->control_port_count > 0)
+ {
+ unsigned long control;
+ guint copy;
+
+ new_sample_rate = (LADSPA_Data) sample_rate;
+ old_sample_rate = (LADSPA_Data) settings->sample_rate;
+
+ for (control = 0; control < settings->desc->control_port_count; control++)
+ {
+ for (copy = 0; copy < settings->copies; copy++)
+ {
+ if (LADSPA_IS_HINT_SAMPLE_RATE (settings->desc->port_range_hints[control].HintDescriptor))
+ {
+ settings->control_values[copy][control] =
+ (settings->control_values[copy][control] / old_sample_rate) * new_sample_rate;
+ }
+ }
+ }
+ }
+
+ settings->sample_rate = sample_rate;
+}
+
+void
+settings_set_control_value (settings_t * settings, guint copy, unsigned long control_index, LADSPA_Data value)
+{
+ g_return_if_fail (settings != NULL);
+ g_return_if_fail (control_index < settings->desc->control_port_count);
+
+ if (copy >= settings->copies)
+ settings_set_copies (settings, copy + 1);
+
+ settings->control_values[copy][control_index] = value;
+}
+
+void
+settings_set_lock (settings_t * settings, unsigned long control_index, gboolean locked)
+{
+ g_return_if_fail (settings != NULL);
+ g_return_if_fail (control_index < settings->desc->control_port_count);
+
+ settings->locks[control_index] = locked;
+}
+
+void
+settings_set_lock_all (settings_t * settings, gboolean lock_all)
+{
+ g_return_if_fail (settings != NULL);
+
+ settings->lock_all = lock_all;
+}
+
+void
+settings_set_enabled (settings_t * settings, gboolean enabled)
+{
+ g_return_if_fail (settings != NULL);
+
+ settings->enabled = enabled;
+}
+
+void
+settings_set_wet_dry_enabled (settings_t * settings, gboolean enabled)
+{
+ g_return_if_fail (settings != NULL);
+
+ settings->wet_dry_enabled = enabled;
+}
+
+void
+settings_set_wet_dry_locked (settings_t * settings, gboolean locked)
+{
+ g_return_if_fail (settings != NULL);
+
+ settings->wet_dry_locked = locked;
+}
+
+void
+settings_set_wet_dry_value (settings_t * settings, unsigned long channel, LADSPA_Data value)
+{
+ g_return_if_fail (settings != NULL);
+
+ if (channel >= settings->channels)
+ settings_set_channels (settings, channel + 1);
+
+ settings->wet_dry_values[channel] = value;
+}
+
+
+LADSPA_Data
+settings_get_control_value (settings_t * settings, guint copy, unsigned long control_index)
+{
+ g_return_val_if_fail (settings != NULL, NAN);
+ g_return_val_if_fail (control_index < settings->desc->control_port_count, NAN);
+
+ if (copy >= settings->copies)
+ settings_set_copies (settings, copy - 1);
+
+ return settings->control_values[copy][control_index];
+}
+
+gboolean
+settings_get_lock (const settings_t * settings, unsigned long control_index)
+{
+ g_return_val_if_fail (settings != NULL, FALSE);
+
+ return settings->locks[control_index];
+}
+
+gboolean
+settings_get_lock_all (const settings_t * settings)
+{
+ g_return_val_if_fail (settings != NULL, FALSE);
+
+ return settings->lock_all;
+}
+
+gboolean
+settings_get_enabled (const settings_t * settings)
+{
+ g_return_val_if_fail (settings != NULL, FALSE);
+
+ return settings->enabled;
+}
+
+guint
+settings_get_copies (const settings_t * settings)
+{
+ g_return_val_if_fail (settings != NULL, 0);
+
+ return settings->copies;
+}
+
+
+unsigned long
+settings_get_channels (const settings_t * settings)
+{
+ g_return_val_if_fail (settings != NULL, 0);
+
+ return settings->channels;
+}
+
+gboolean
+settings_get_wet_dry_enabled (const settings_t * settings)
+{
+ g_return_val_if_fail (settings != NULL, FALSE);
+
+ return settings->wet_dry_enabled;
+}
+
+gboolean
+settings_get_wet_dry_locked (const settings_t * settings)
+{
+ g_return_val_if_fail (settings != NULL, FALSE);
+
+ return settings->wet_dry_locked;
+}
+
+LADSPA_Data
+settings_get_wet_dry_value (settings_t * settings, unsigned long channel)
+{
+ g_return_val_if_fail (settings != NULL, NAN);
+
+ if (channel >= settings->channels)
+ settings_set_channels (settings, channel + 1);
+
+ return settings->wet_dry_values[channel];
+}
+
+
+
+/* EOF */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_SETTINGS_H__
+#define __JR_PLUGIN_SETTINGS_H__
+
+#include <glib.h>
+#include <ladspa.h>
+
+#include "plugin_mgr.h"
+#include "plugin_desc.h"
+
+typedef struct _settings settings_t;
+
+struct _settings
+{
+ guint32 sample_rate;
+ plugin_desc_t * desc;
+ guint copies;
+ LADSPA_Data ** control_values;
+ gboolean * locks;
+ gboolean lock_all;
+ gboolean enabled;
+ unsigned long channels;
+ gboolean wet_dry_enabled;
+ gboolean wet_dry_locked;
+ LADSPA_Data * wet_dry_values;
+};
+
+settings_t * settings_new (plugin_desc_t * desc, unsigned long channels, guint32 sample_rate);
+settings_t * settings_dup (settings_t * settings);
+void settings_destroy (settings_t * settings);
+
+void settings_set_control_value (settings_t * settings, guint copy, unsigned long control_index, LADSPA_Data value);
+void settings_set_lock (settings_t * settings, unsigned long control_index, gboolean locked);
+void settings_set_lock_all (settings_t * settings, gboolean lock_all);
+void settings_set_enabled (settings_t * settings, gboolean enabled);
+void settings_set_wet_dry_enabled (settings_t * settings, gboolean enabled);
+void settings_set_wet_dry_locked (settings_t * settings, gboolean locked);
+void settings_set_wet_dry_value (settings_t * settings, unsigned long channel, LADSPA_Data value);
+
+LADSPA_Data settings_get_control_value (settings_t * settings, guint copy, unsigned long control_index);
+gboolean settings_get_lock (const settings_t * settings, unsigned long control_index);
+gboolean settings_get_lock_all (const settings_t * settings);
+gboolean settings_get_enabled (const settings_t * settings);
+guint settings_get_copies (const settings_t * settings);
+unsigned long settings_get_channels (const settings_t * settings);
+gboolean settings_get_wet_dry_enabled (const settings_t * settings);
+gboolean settings_get_wet_dry_locked (const settings_t * settings);
+LADSPA_Data settings_get_wet_dry_value (settings_t * settings, unsigned long channel);
+
+void settings_set_sample_rate (settings_t * settings, guint32 sample_rate);
+
+#endif /* __JR_PLUGIN_SETTINGS_H__ */
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <jack/jack.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "process.h"
+#include "lock_free_fifo.h"
+#include "plugin.h"
+#include "jack_rack.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+#define USEC_PER_SEC 1000000
+#define MSEC_PER_SEC 1000
+#define TIME_RUN_SKIP_COUNT 5
+#define MAX_BUFFER_SIZE 4096
+
+jack_nframes_t sample_rate;
+jack_nframes_t buffer_size;
+
+static void
+jack_shutdown_cb (void * data)
+{
+ process_info_t * procinfo = data;
+
+ procinfo->quit = TRUE;
+}
+
+/** process messages for plugins' control ports */
+void process_control_port_messages (process_info_t * procinfo) {
+ plugin_t * plugin;
+ unsigned long control;
+ unsigned long channel;
+ gint copy;
+
+ if (!procinfo->chain) return;
+
+ for (plugin = procinfo->chain; plugin; plugin = plugin->next)
+ {
+ if (plugin->desc->control_port_count > 0)
+ for (control = 0; control < plugin->desc->control_port_count; control++)
+ for (copy = 0; copy < plugin->copies; copy++)
+ {
+ while (lff_read (plugin->holders[copy].ui_control_fifos + control,
+ plugin->holders[copy].control_memory + control) == 0);
+ }
+
+ if (plugin->wet_dry_enabled)
+ for (channel = 0; channel < procinfo->channels; channel++)
+ {
+ while (lff_read (plugin->wet_dry_fifos + channel,
+ plugin->wet_dry_values + channel) == 0);
+ }
+ }
+}
+
+int get_jack_buffers (process_info_t * procinfo, jack_nframes_t frames) {
+ unsigned long channel;
+
+ for (channel = 0; channel < procinfo->channels; channel++)
+ {
+ procinfo->jack_input_buffers[channel] = jack_port_get_buffer (procinfo->jack_input_ports[channel], frames);
+ if (!procinfo->jack_input_buffers[channel])
+ {
+ fprintf (stderr, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel);
+ return 1;
+ }
+
+ procinfo->jack_output_buffers[channel] = jack_port_get_buffer (procinfo->jack_output_ports[channel], frames);
+ if (!procinfo->jack_output_buffers[channel])
+ {
+ fprintf (stderr, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+plugin_t *
+get_first_enabled_plugin (process_info_t * procinfo)
+{
+ plugin_t * first_enabled;
+
+ if (!procinfo->chain) return NULL;
+
+ for (first_enabled = procinfo->chain;
+ first_enabled;
+ first_enabled = first_enabled->next)
+ {
+ if (first_enabled->enabled) return first_enabled;
+ }
+
+ return NULL;
+}
+
+plugin_t *
+get_last_enabled_plugin (process_info_t * procinfo)
+{
+ plugin_t * last_enabled;
+
+ if (!procinfo->chain) return NULL;
+
+ for (last_enabled = procinfo->chain_end;
+ last_enabled;
+ last_enabled = last_enabled->prev)
+ {
+ if (last_enabled->enabled) return last_enabled;
+ }
+
+ return NULL;
+}
+
+void
+connect_chain (process_info_t * procinfo, jack_nframes_t frames)
+{
+ plugin_t * first_enabled, * last_enabled, * plugin;
+ gint copy;
+ unsigned long channel;
+ if (!procinfo->chain) return;
+
+ first_enabled = get_first_enabled_plugin (procinfo);
+ if (!first_enabled) return;
+
+ last_enabled = get_last_enabled_plugin (procinfo);
+
+ /* sort out the aux ports */
+ plugin = first_enabled;
+ do
+ {
+ if (plugin->desc->aux_channels > 0 && plugin->enabled)
+ {
+ if (procinfo->jack_client)
+ {
+ for (copy = 0; copy < plugin->copies; copy++)
+ for (channel = 0; channel < plugin->desc->aux_channels; channel++)
+ plugin->descriptor->
+ connect_port (plugin->holders[copy].instance,
+ plugin->desc->audio_aux_port_indicies[channel],
+ jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames));
+ }
+ else
+ {
+ for (copy = 0; copy < frames; copy++)
+ procinfo->silent_buffer[copy] = 0.0;
+
+ for (copy = 0; copy < plugin->copies; copy++)
+ for (channel = 0; channel < plugin->desc->aux_channels; channel++)
+ plugin->descriptor->
+ connect_port (plugin->holders[copy].instance,
+ plugin->desc->audio_aux_port_indicies[channel],
+ procinfo->silent_buffer);
+ }
+ }
+ }
+ while ( (plugin != last_enabled) && (plugin = plugin->next) );
+
+ /* ensure that all the of the enabled plugins are connected to their memory */
+ plugin_connect_output_ports (first_enabled);
+ if (first_enabled != last_enabled)
+ {
+ plugin_connect_input_ports (last_enabled, last_enabled->prev->audio_output_memory);
+ for (plugin = first_enabled->next; plugin; plugin = plugin->next)
+ {
+ if (plugin->enabled)
+ {
+ plugin_connect_input_ports (plugin, plugin->prev->audio_output_memory);
+ plugin_connect_output_ports (plugin);
+ }
+ }
+ }
+
+ /* input buffers for first plugin */
+ plugin_connect_input_ports (first_enabled, procinfo->jack_input_buffers);
+}
+
+void
+process_chain (process_info_t * procinfo, jack_nframes_t frames)
+{
+ plugin_t * first_enabled;
+ plugin_t * last_enabled = NULL;
+ plugin_t * plugin;
+ unsigned long channel;
+ unsigned long i;
+
+ if (procinfo->jack_client)
+ {
+ LADSPA_Data zero_signal[frames];
+ guint copy;
+
+ /* set the zero signal to zero */
+ for (channel = 0; channel < frames; channel++)
+ zero_signal[channel] = 0.0;
+
+ /* possibly set aux output channels to zero if they're not enabled */
+ for (plugin = procinfo->chain; plugin; plugin = plugin->next)
+ if (!plugin->enabled &&
+ plugin->desc->aux_channels > 0 &&
+ !plugin->desc->aux_are_input)
+ for (copy = 0; copy < plugin->copies; copy++)
+ for (channel = 0; channel < plugin->desc->aux_channels; channel++)
+ memcpy (jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames),
+ zero_signal, sizeof (LADSPA_Data) * frames);
+ }
+
+ first_enabled = get_first_enabled_plugin (procinfo);
+
+ /* no chain; just copy input to output */
+ if (!procinfo->chain || !first_enabled)
+ {
+ unsigned long channel;
+ for (channel = 0; channel < procinfo->channels; channel++)
+ {
+ memcpy (procinfo->jack_output_buffers[channel],
+ procinfo->jack_input_buffers[channel],
+ sizeof(LADSPA_Data) * frames);
+ }
+ return;
+ }
+
+ /* all past here is guaranteed to have at least 1 enabled plugin */
+
+ last_enabled = get_last_enabled_plugin (procinfo);
+
+ for (plugin = first_enabled;
+ plugin;
+ plugin = plugin->next)
+ {
+ if (plugin->enabled)
+ {
+ for (i = 0; i < plugin->copies; i++)
+ plugin->descriptor->run (plugin->holders[i].instance, frames);
+
+ if (plugin->wet_dry_enabled)
+ for (channel = 0; channel < procinfo->channels; channel++)
+ for (i = 0; i < frames; i++)
+ {
+ plugin->audio_output_memory[channel][i] *= plugin->wet_dry_values[channel];
+ plugin->audio_output_memory[channel][i] += plugin->audio_input_memory[channel][i] * (1.0 - plugin->wet_dry_values[channel]);
+ }
+
+ if (plugin == last_enabled)
+ break;
+ }
+ else
+ {
+
+ /* copy the data through */
+ for (i = 0; i < procinfo->channels; i++)
+ memcpy (plugin->audio_output_memory[i],
+ plugin->prev->audio_output_memory[i],
+ sizeof(LADSPA_Data) * frames);
+ }
+ }
+
+ /* copy the last enabled data to the jack ports */
+ for (i = 0; i < procinfo->channels; i++)
+ memcpy (procinfo->jack_output_buffers[i],
+ last_enabled->audio_output_memory[i],
+ sizeof(LADSPA_Data) * frames);
+
+}
+
+int process_ladspa (process_info_t * procinfo, jack_nframes_t frames,
+ LADSPA_Data ** inputs, LADSPA_Data ** outputs) {
+ unsigned long channel;
+
+ if (!procinfo)
+ {
+ fprintf (stderr, "%s: no process_info from jack!\n", __FUNCTION__);
+ return 1;
+ }
+
+ if (procinfo->quit == TRUE)
+ return 1;
+
+ process_control_port_messages (procinfo);
+
+ for (channel = 0; channel < procinfo->channels; channel++)
+ {
+ procinfo->jack_input_buffers[channel] = inputs[channel];
+ if (!procinfo->jack_input_buffers[channel])
+ {
+ fprintf (stderr, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel);
+ return 1;
+ }
+
+ procinfo->jack_output_buffers[channel] = outputs[channel];
+ if (!procinfo->jack_output_buffers[channel])
+ {
+ fprintf (stderr, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel);
+ return 1;
+ }
+ }
+
+ connect_chain (procinfo, frames);
+
+ process_chain (procinfo, frames);
+
+ return 0;
+}
+
+int process_jack (jack_nframes_t frames, void * data) {
+ int err;
+ process_info_t * procinfo;
+
+ procinfo = (process_info_t *) data;
+
+ if (!procinfo)
+ {
+ fprintf (stderr, "%s: no process_info from jack!\n", __FUNCTION__);
+ return 1;
+ }
+
+ if (procinfo->port_count == 0)
+ return 0;
+
+ if (procinfo->quit == TRUE)
+ return 1;
+
+ process_control_port_messages (procinfo);
+
+ err = get_jack_buffers (procinfo, frames);
+ if (err)
+ {
+ fprintf(stderr, "%s: failed to get jack ports, not processing\n", __FUNCTION__);
+ return 0;
+ }
+
+ connect_chain (procinfo, frames);
+
+ process_chain (procinfo, frames);
+
+ return 0;
+}
+
+
+
+/*******************************************
+ ************** non RT stuff ***************
+ *******************************************/
+
+static int
+process_info_connect_jack (process_info_t * procinfo)
+{
+ printf (_("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name);
+
+ procinfo->jack_client = jack_client_new (procinfo->jack_client_name);
+
+ if (!procinfo->jack_client)
+ {
+ fprintf (stderr, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__);
+ return 1;
+ }
+
+ printf (_("Connected to JACK server\n"));
+
+ jack_set_process_callback (procinfo->jack_client, process_jack, procinfo);
+ jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo);
+
+ return 0;
+}
+
+static void
+process_info_connect_port (process_info_t * procinfo,
+ gshort in,
+ unsigned long port_index,
+ const char * port_name)
+{
+ const char ** jack_ports;
+ unsigned long jack_port_index;
+ int err;
+ char * full_port_name;
+
+ jack_ports = jack_get_ports (procinfo->jack_client, NULL, NULL,
+ JackPortIsPhysical | (in ? JackPortIsOutput : JackPortIsInput));
+
+ if (!jack_ports)
+ return;
+
+ for (jack_port_index = 0;
+ jack_ports[jack_port_index] && jack_port_index <= port_index;
+ jack_port_index++)
+ {
+ if (jack_port_index != port_index)
+ continue;
+
+ full_port_name = g_strdup_printf ("%s:%s", procinfo->jack_client_name, port_name);
+
+ printf (_("Connecting ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]);
+
+ err = jack_connect (procinfo->jack_client,
+ in ? jack_ports[jack_port_index] : full_port_name,
+ in ? full_port_name : jack_ports[jack_port_index]);
+
+ if (err)
+ fprintf (stderr, "%s: error connecting ports '%s' and '%s'\n",
+ __FUNCTION__, full_port_name, jack_ports[jack_port_index]);
+ else
+ printf (_("Connected ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]);
+
+ free (full_port_name);
+ }
+
+ free (jack_ports);
+}
+
+int
+process_info_set_port_count (process_info_t * procinfo,
+ unsigned long port_count, gboolean connect_inputs, gboolean connect_outputs)
+{
+ unsigned long i;
+ char * port_name;
+ jack_port_t ** port_ptr;
+ gshort in;
+
+ if (procinfo->port_count >= port_count)
+ return -1;
+
+ if (procinfo->port_count == 0)
+ {
+ procinfo->jack_input_ports = g_malloc (sizeof (jack_port_t *) * port_count);
+ procinfo->jack_output_ports = g_malloc (sizeof (jack_port_t *) * port_count);
+
+ procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count);
+ procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count);
+ }
+ else
+ {
+ procinfo->jack_input_ports = g_realloc (procinfo->jack_input_ports, sizeof (jack_port_t *) * port_count);
+ procinfo->jack_output_ports = g_realloc (procinfo->jack_output_ports, sizeof (jack_port_t *) * port_count);
+
+ procinfo->jack_input_buffers = g_realloc (procinfo->jack_input_buffers, sizeof (LADSPA_Data *) * port_count);
+ procinfo->jack_output_buffers = g_realloc (procinfo->jack_output_buffers, sizeof (LADSPA_Data *) * port_count);
+ }
+
+ for (i = procinfo->port_count; i < port_count; i++)
+ {
+ for (in = 0; in < 2; in++)
+ {
+ port_name = g_strdup_printf ("%s_%ld", in ? "in" : "out", i + 1);
+
+ //printf (_("Creating %s port %s\n"), in ? "input" : "output", port_name);
+
+ port_ptr = (in ? &procinfo->jack_input_ports[i]
+ : &procinfo->jack_output_ports[i]);
+
+ *port_ptr = jack_port_register (procinfo->jack_client,
+ port_name,
+ JACK_DEFAULT_AUDIO_TYPE,
+ in ? JackPortIsInput : JackPortIsOutput,
+ 0);
+
+ if (!*port_ptr)
+ {
+ fprintf (stderr, "%s: could not register port '%s'; aborting\n",
+ __FUNCTION__, port_name);
+ return 1;
+ }
+
+ //printf (_("Created %s port %s\n"), in ? "input" : "output", port_name);
+
+ if ((in && connect_inputs) || (!in && connect_outputs))
+ process_info_connect_port (procinfo, in, i, port_name);
+
+ g_free (port_name);
+ }
+ }
+
+ procinfo->port_count = port_count;
+
+ return 0;
+}
+
+void
+process_info_set_channels (process_info_t * procinfo,
+ unsigned long channels, gboolean connect_inputs, gboolean connect_outputs)
+{
+ process_info_set_port_count (procinfo, channels, connect_inputs, connect_outputs);
+ procinfo->channels = channels;
+}
+
+process_info_t *
+process_info_new (const char * client_name, unsigned long rack_channels,
+ gboolean connect_inputs, gboolean connect_outputs)
+{
+ process_info_t * procinfo;
+ char * jack_client_name;
+ int err;
+
+ procinfo = g_malloc (sizeof (process_info_t));
+
+ procinfo->chain = NULL;
+ procinfo->chain_end = NULL;
+ procinfo->jack_client = NULL;
+ procinfo->port_count = 0;
+ procinfo->jack_input_ports = NULL;
+ procinfo->jack_output_ports = NULL;
+ procinfo->channels = rack_channels;
+ procinfo->quit = FALSE;
+
+ if ( client_name == NULL )
+ {
+ sample_rate = 48000; // should be set externally before calling process_ladspa
+ buffer_size = MAX_BUFFER_SIZE;
+ procinfo->silent_buffer = g_malloc (sizeof (LADSPA_Data) * buffer_size );
+ procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels);
+ procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels);
+
+ return procinfo;
+ }
+
+ /* sort out the client name */
+ procinfo->jack_client_name = jack_client_name = strdup (client_name);
+ for (err = 0; jack_client_name[err] != '\0'; err++)
+ {
+ if (jack_client_name[err] == ' ')
+ jack_client_name[err] = '_';
+ else if (!isalnum (jack_client_name[err]))
+ { /* shift all the chars up one (to remove the non-alphanumeric char) */
+ int i;
+ for (i = err; jack_client_name[i] != '\0'; i++)
+ jack_client_name[i] = jack_client_name[i + 1];
+ }
+ else if (isupper (jack_client_name[err]))
+ jack_client_name[err] = tolower (jack_client_name[err]);
+ }
+
+ err = process_info_connect_jack (procinfo);
+ if (err)
+ {
+/* g_free (procinfo); */
+ return NULL;
+/* abort (); */
+ }
+
+ sample_rate = jack_get_sample_rate (procinfo->jack_client);
+ buffer_size = jack_get_sample_rate (procinfo->jack_client);
+
+ jack_set_process_callback (procinfo->jack_client, process_jack, procinfo);
+ jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo);
+
+ jack_activate (procinfo->jack_client);
+
+ err = process_info_set_port_count (procinfo, rack_channels, connect_inputs, connect_outputs);
+ if (err)
+ return NULL;
+
+ return procinfo;
+}
+
+void
+process_info_destroy (process_info_t * procinfo) {
+ if (procinfo->jack_client)
+ {
+ jack_deactivate (procinfo->jack_client);
+ jack_client_close (procinfo->jack_client);
+ }
+ g_free (procinfo->silent_buffer);
+ g_free (procinfo->jack_input_ports);
+ g_free (procinfo->jack_output_ports);
+ g_free (procinfo->jack_input_buffers);
+ g_free (procinfo->jack_output_buffers);
+ g_free (procinfo);
+}
+
+void process_quit (process_info_t * procinfo) {
+ procinfo->quit = TRUE;
+}
--- /dev/null
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JLH_PROCESS_H__
+#define __JLH_PROCESS_H__
+
+#include <glib.h>
+#include <jack/jack.h>
+#include <ladspa.h>
+
+#include "lock_free_fifo.h"
+
+typedef struct _process_info process_info_t;
+
+/** this is what gets passed to the process() callback and contains all
+ the data the process callback will need */
+struct _process_info {
+
+ /** the plugin instance chain */
+ struct _plugin * chain;
+ struct _plugin * chain_end;
+
+ jack_client_t * jack_client;
+ unsigned long port_count;
+ jack_port_t ** jack_input_ports;
+ jack_port_t ** jack_output_ports;
+
+ unsigned long channels;
+ LADSPA_Data ** jack_input_buffers;
+ LADSPA_Data ** jack_output_buffers;
+ LADSPA_Data * silent_buffer;
+
+ char * jack_client_name;
+ int quit;
+};
+
+extern jack_nframes_t sample_rate;
+extern jack_nframes_t buffer_size;
+
+process_info_t * process_info_new (const char * client_name,
+ unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs);
+void process_info_destroy (process_info_t * procinfo);
+
+void process_info_set_channels (process_info_t * procinfo,
+ unsigned long channels, gboolean connect_inputs, gboolean connect_outputs);
+
+int process_ladspa (process_info_t * procinfo, jack_nframes_t frames,
+ LADSPA_Data ** inputs, LADSPA_Data ** outputs);
+
+int process_jack (jack_nframes_t frames, void * data);
+
+void process_quit (process_info_t * procinfo);
+
+#endif /* __JLH_PROCESS_H__ */
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltkdenlive$(LIBSUF)
+
+OBJS = factory.o \
+ filter_boxblur.o \
+ filter_freeze.o \
+ filter_wave.o \
+ producer_framebuffer.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2007 Jean-Baptiste Mardelle
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_boxblur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_freeze_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_wave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "boxblur", filter_boxblur_init );
+ MLT_REGISTER( filter_type, "freeze", filter_freeze_init );
+ MLT_REGISTER( filter_type, "wave", filter_wave_init );
+ MLT_REGISTER( producer_type, "framebuffer", producer_framebuffer_init );
+}
--- /dev/null
+/*
+ * filter_boxblur.c -- blur filter
+ * Copyright (C) ?-2007 Leny Grisel <leny.grisel@laposte.net>
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@ader.ch>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+static void PreCompute(uint8_t *yuv, int32_t *rgb, int width, int height)
+{
+ register int x, y, z;
+ register int uneven = width % 2;
+ int w = (width - uneven ) / 2;
+ int yy, uu, vv;
+ int r, g, b;
+ int32_t pts[3];
+ for (y=0; y<height; y++)
+ {
+ for (x=0; x<w; x++)
+ {
+ uu = yuv[1];
+ vv = yuv[3];
+ yy = yuv[0];
+ YUV2RGB(yy, uu, vv, r, g, b);
+ pts[0] = r;
+ pts[1] = g;
+ pts[2] = b;
+ for (z = 0; z < 3; z++)
+ {
+ if (x>0) pts[z]+=rgb[-3];
+ if (y>0) pts[z]+=rgb[-(width*3)];
+ if (x>0 && y>0) pts[z]-=rgb[-((width+1)*3)];
+ *rgb++=pts[z];
+ }
+
+ yy = yuv[2];
+ YUV2RGB(yy, uu, vv, r, g, b);
+ pts[0] = r;
+ pts[1] = g;
+ pts[2] = b;
+ for (z = 0; z < 3; z++)
+ {
+ pts[z]+=rgb[-3];
+ if (y>0)
+ {
+ pts[z]+=rgb[-(width*3)];
+ pts[z]-=rgb[-((width+1)*3)];
+ }
+ *rgb++=pts[z];
+ }
+ yuv += 4;
+ }
+ if (uneven)
+ {
+ uu = yuv[1];
+ vv = yuv[3];
+ yy = yuv[0];
+ YUV2RGB(yy, uu, vv, r, g, b);
+ pts[0] = r;
+ pts[1] = g;
+ pts[2] = b;
+ for (z = 0; z < 3; z++)
+ {
+ pts[z]+=rgb[-3];
+ if (y>0)
+ {
+ pts[z]+=rgb[-(width*3)];
+ pts[z]-=rgb[-((width+1)*3)];
+ }
+ *rgb++=pts[z];
+ }
+ yuv += 2;
+ }
+ }
+}
+
+static int32_t GetRGB(int32_t *rgb, unsigned int w, unsigned int h, unsigned int x, int offsetx, unsigned int y, int offsety, unsigned int z)
+{
+ int xtheo = x * 2 + offsetx;
+ int ytheo = y + offsety;
+ if (xtheo < 0) xtheo = 0; else if (xtheo >= w) xtheo = w - 1;
+ if (ytheo < 0) ytheo = 0; else if (ytheo >= h) ytheo = h - 1;
+ return rgb[3*(xtheo+ytheo*w)+z];
+}
+
+static int32_t GetRGB2(int32_t *rgb, unsigned int w, unsigned int h, unsigned int x, int offsetx, unsigned int y, int offsety, unsigned int z)
+{
+ int xtheo = x * 2 + 1 + offsetx;
+ int ytheo = y + offsety;
+ if (xtheo < 0) xtheo = 0; else if (xtheo >= w) xtheo = w - 1;
+ if (ytheo < 0) ytheo = 0; else if (ytheo >= h) ytheo = h - 1;
+ return rgb[3*(xtheo+ytheo*w)+z];
+}
+
+static void DoBoxBlur(uint8_t *yuv, int32_t *rgb, unsigned int width, unsigned int height, unsigned int boxw, unsigned int boxh)
+{
+ register int x, y;
+ int32_t r, g, b;
+ register int uneven = width % 2;
+ register int y0, y1, u0, u1, v0, v1;
+ int w = (width - uneven ) / 2;
+ float mul = 1.f / ((boxw*2) * (boxh*2));
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ r = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 0) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 0) - GetRGB(rgb, width, height, x, -boxw, y, + boxh, 0) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 0);
+ g = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 1) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 1) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 1) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 1);
+ b = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 2) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 2) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 2) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 2);
+ r = (int32_t) (r * mul);
+ g = (int32_t) (g * mul);
+ b = (int32_t) (b * mul);
+ RGB2YUV (r, g, b, y0, u0, v0);
+
+ r = GetRGB2(rgb, width, height, x, +boxw, y, +boxh, 0) + GetRGB2(rgb, width, height, x, -boxw, y, -boxh, 0) - GetRGB2(rgb, width, height, x, -boxw, y, +boxh, 0) - GetRGB2(rgb, width, height, x, +boxw, y, -boxh, 0);
+ g = GetRGB2(rgb, width, height, x, +boxw, y, +boxh, 1) + GetRGB2(rgb, width, height, x, -boxw, y, -boxh, 1) - GetRGB2(rgb, width, height, x, -boxw, y, +boxh, 1) - GetRGB2(rgb, width, height, x, +boxw, y, -boxh, 1);
+ b = GetRGB2(rgb, width, height, x, +boxw, y, +boxh, 2) + GetRGB2(rgb, width, height, x, -boxw, y, -boxh, 2) - GetRGB2(rgb, width, height, x, -boxw, y, +boxh, 2) - GetRGB2(rgb, width, height, x, +boxw, y, -boxh, 2);
+ r = (int32_t) (r * mul);
+ g = (int32_t) (g * mul);
+ b = (int32_t) (b * mul);
+ RGB2YUV (r, g, b, y1, u1, v1);
+ *yuv++ = y0;
+ *yuv++ = (u0+u1) >> 1;
+ *yuv++ = y1;
+ *yuv++ = (v0+v1) >> 1;
+ }
+ if (uneven)
+ {
+ r = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 0) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 0) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 0) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 0);
+ g = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 1) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 1) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 1) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 1);
+ b = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 2) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 2) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 2) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 2);
+ r = (int32_t) (r * mul);
+ g = (int32_t) (g * mul);
+ b = (int32_t) (b * mul);
+ RGB2YUV (r, g, b, y0, u0, v0);
+ *yuv++ = mul * y0;
+ *yuv++ = mul * u0;
+ }
+ }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+ short hori = mlt_properties_get_int(MLT_FRAME_PROPERTIES( this ), "hori" );
+ short vert = mlt_properties_get_int(MLT_FRAME_PROPERTIES( this ), "vert" );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ double factor = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "boxblur" );
+ if (factor != 0) {
+ int h = *height + 1;
+ int32_t *rgb = mlt_pool_alloc (3 * *width * h * sizeof(int32_t));
+ PreCompute (*image, rgb, *width, h);
+ DoBoxBlur (*image, rgb, *width, h, (int) factor*hori, (int) factor*vert);
+ mlt_pool_release (rgb);
+ }
+ }
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the starting blur level
+ double blur = (double) mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "start" );
+ short hori = mlt_properties_get_int(MLT_FILTER_PROPERTIES( this ), "hori" );
+ short vert = mlt_properties_get_int(MLT_FILTER_PROPERTIES( this ), "vert" );
+
+ // If there is an end adjust gain to the range
+ if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL )
+ {
+ // Determine the time position of this frame in the transition duration
+ mlt_position in = mlt_filter_get_in( this );
+ mlt_position out = mlt_filter_get_out( this );
+ mlt_position time = mlt_frame_get_position( frame );
+ double position = (double) ( time - in ) / ( out - in + 1.0 );
+ double end = (double) mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "end" );
+ blur += ( end - blur ) * position;
+ }
+
+ // Push the frame filter
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "boxblur", blur );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "hori", hori );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "vert", vert );
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_boxblur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "10" : arg);
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "hori", arg == NULL ? "1" : arg);
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "vert", arg == NULL ? "1" : arg);
+ }
+ return this;
+}
+
+
+
--- /dev/null
+/*
+ * filter_freeze.c -- simple frame freezing filter
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@kdenlive.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_service.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_property.h>
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the image
+ mlt_filter filter = mlt_frame_pop_service( this );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+ mlt_frame freeze_frame = NULL;;
+ int freeze_before = mlt_properties_get_int( properties, "freeze_before" );
+ int freeze_after = mlt_properties_get_int( properties, "freeze_after" );
+ mlt_position pos = mlt_properties_get_position( properties, "frame" );
+ mlt_position currentpos = mlt_properties_get_position( properties, "_seek_frame" );
+
+ int do_freeze = 0;
+ if (freeze_before == 0 && freeze_after == 0) {
+ do_freeze = 1;
+ } else if (freeze_before != 0 && pos > currentpos) {
+ do_freeze = 1;
+ } else if (freeze_after != 0 && pos < currentpos) {
+ do_freeze = 1;
+ }
+
+ if (do_freeze == 1) {
+ freeze_frame = mlt_properties_get_data( properties, "freeze_frame", NULL );
+
+ if( freeze_frame == NULL || mlt_properties_get_position( properties, "_frame" ) != pos )
+ {
+ // freeze_frame has not been fetched yet, so fetch it and cache it.
+ mlt_producer producer = mlt_frame_get_original_producer(this);
+ mlt_producer_seek( producer, pos );
+
+ // Get the frame
+ mlt_service_get_frame( mlt_producer_service(producer), &freeze_frame, 0 );
+
+ mlt_properties props = MLT_FRAME_PROPERTIES( this );
+ mlt_properties freeze_properties = MLT_FRAME_PROPERTIES( freeze_frame );
+ mlt_properties_set_double( freeze_properties, "consumer_aspect_ratio", mlt_properties_get_double( props, "consumer_aspect_ratio" ) );
+ mlt_properties_set( freeze_properties, "rescale.interp", mlt_properties_get( props, "rescale.interp" ) );
+ mlt_properties_set_double( freeze_properties, "aspect_ratio", mlt_frame_get_aspect_ratio( this ) );
+ mlt_properties_set_int( freeze_properties, "progressive", mlt_properties_get_int( props, "progressive" ) );
+
+ mlt_properties_set_data( properties, "freeze_frame", freeze_frame, 0, NULL, NULL );
+ mlt_properties_set_position( properties, "_frame", pos );
+ }
+ int error = mlt_frame_get_image( freeze_frame, image, format, width, height, 1 );
+ return error;
+ }
+
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ // Push the filter on to the stack
+ mlt_frame_push_service( frame, this );
+
+ // Determine the time position of this frame
+ mlt_properties_set_position( MLT_FILTER_PROPERTIES( this ), "_seek_frame", mlt_frame_get_position( frame ) - mlt_filter_get_in( this ) );
+
+ // Push the frame filter
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_freeze_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ // Set the frame which will be chosen for freeze
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "frame", "0" );
+
+ // If freeze_after = 1, only frames after the "frame" value will be frozen
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "freeze_after", "0" );
+
+ // If freeze_before = 1, only frames after the "frame" value will be frozen
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "freeze_before", "0" );
+ }
+ return this;
+}
+
+
+
--- /dev/null
+/*
+ * wave.c -- wave filter
+ * Copyright (C) ?-2007 Leny Grisel <leny.grisel@laposte.net>
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@ader.ch>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+// this is a utility function used by DoWave below
+static uint8_t getPoint(uint8_t *src, int w, int h, int x, int y, int z)
+{
+ if (x<0) x+=-((-x)%w)+w; else if (x>=w) x=x%w;
+ if (y<0) y+=-((-y)%h)+h; else if (y>=h) y=y%h;
+ return src[(x+y*w)*4+z];
+}
+
+// the main meat of the algorithm lies here
+static void DoWave(uint8_t *src, int src_w, int src_h, uint8_t *dst, mlt_position position, int speed, int factor, int deformX, int deformY)
+{
+ register int x, y;
+ int decalY, decalX, z;
+ float amplitude, phase, pulsation;
+ register int uneven = src_w % 2;
+ int w = (src_w - uneven ) / 2;
+ amplitude = factor;
+ pulsation = 0.5 / factor; // smaller means bigger period
+ phase = position * pulsation * speed / 10; // smaller means longer
+ for (y=0;y<src_h;y++) {
+ decalX = deformX ? sin(pulsation * y + phase) * amplitude : 0;
+ for (x=0;x<w;x++) {
+ decalY = deformY ? sin(pulsation * x * 2 + phase) * amplitude : 0;
+ for (z=0; z<4; z++)
+ *dst++ = getPoint(src, w, src_h, (x+decalX), (y+decalY), z);
+ }
+ if (uneven) {
+ decalY = sin(pulsation * x * 2 + phase) * amplitude;
+ for (z=0; z<2; z++)
+ *dst++ = getPoint(src, w, src_h, (x+decalX), (y+decalY), z);
+ }
+ }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+ mlt_position position = mlt_frame_get_position( this );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ double factor = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "wave" );
+ int speed = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "speed" );
+ int deformX = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "deformX" );
+ int deformY = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "deformY" );
+ if (factor != 0) {
+ int image_size = *width * (*height + 1) * 2;
+ uint8_t *dest = mlt_pool_alloc (image_size);
+ DoWave(*image, *width, (*height + 1), dest, position, speed, factor, deformX, deformY);
+ memcpy(*image, dest, image_size);
+ mlt_pool_release(dest);
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the starting wave level
+ double wave = mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "start" );
+ int speed = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "speed" );
+ int deformX = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "deformX" );
+ int deformY = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "deformY" );
+
+ // If there is an end adjust gain to the range
+ if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL )
+ {
+ // Determine the time position of this frame in the transition duration
+ mlt_position in = mlt_filter_get_in( this );
+ mlt_position out = mlt_filter_get_out( this );
+ mlt_position time = mlt_frame_get_position( frame );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "end" ) );
+ wave += ( end - wave ) * position;
+ }
+
+ // Push the frame filter
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "wave", wave );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "speed", speed );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "deformX", deformX );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "deformY", deformY );
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_wave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "10" : arg);
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "speed", arg == NULL ? "5" : arg);
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "deformX", arg == NULL ? "1" : arg);
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "deformY", arg == NULL ? "1" : arg);
+ }
+ return this;
+}
+
+
+
--- /dev/null
+/*
+ * producer_framebuffer.c -- create subspeed frames
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@ader.ch>
+ * Author: Jean-Baptiste Mardelle, based on the code of motion_est by Zachary Drew
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <assert.h>
+
+// Forward references.
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+
+/** Image stack(able) method
+*/
+
+static int framebuffer_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ // Get the filter object and properties
+ mlt_producer producer = mlt_frame_pop_service( this );
+ mlt_frame first_frame = mlt_frame_pop_service( this );
+
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Frame properties objects
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this );
+ mlt_properties first_frame_properties = MLT_FRAME_PROPERTIES( first_frame );
+
+ *width = mlt_properties_get_int( frame_properties, "width" );
+ *height = mlt_properties_get_int( frame_properties, "height" );
+
+ int size;
+ switch ( *format )
+ {
+ case mlt_image_yuv420p:
+ size = *width * 3 * ( *height + 1 ) / 2;
+ break;
+ case mlt_image_rgb24:
+ size = *width * ( *height + 1 ) * 3;
+ break;
+ default:
+ *format = mlt_image_yuv422;
+ size = *width * ( *height + 1 ) * 2;
+ break;
+ }
+
+ uint8_t *output = mlt_properties_get_data( producer_properties, "output_buffer", NULL );
+
+ if( output == NULL )
+ {
+ output = mlt_pool_alloc( size );
+
+ // Let someone else clean up
+ mlt_properties_set_data( producer_properties, "output_buffer", output, size, mlt_pool_release, NULL );
+ }
+
+ uint8_t *first_image = mlt_properties_get_data( first_frame_properties, "image", NULL );
+
+ // which frames are buffered?
+
+ int error = 0;
+
+ if( first_image == NULL )
+ {
+ mlt_properties props = MLT_FRAME_PROPERTIES( this );
+ mlt_properties test_properties = MLT_FRAME_PROPERTIES( first_frame );
+ mlt_properties_set_double( test_properties, "consumer_aspect_ratio", mlt_properties_get_double( props, "consumer_aspect_ratio" ) );
+ mlt_properties_set( test_properties, "rescale.interp", mlt_properties_get( props, "rescale.interp" ) );
+
+ error = mlt_frame_get_image( first_frame, &first_image, format, width, height, writable );
+
+ if( error != 0 ) {
+ fprintf(stderr, "first_image == NULL get image died\n");
+ return error;
+ }
+ }
+
+ // Start with a base image
+ memcpy( output, first_image, size );
+
+ *image = output;
+ mlt_properties_set_data( frame_properties, "image", output, size, NULL, NULL );
+
+ // Make sure that no further scaling is done
+ mlt_properties_set( frame_properties, "rescale.interps", "none" );
+ mlt_properties_set( frame_properties, "scale", "off" );
+
+ mlt_frame_close( first_frame );
+
+ return 0;
+}
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ // Construct a new frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ if( frame != NULL )
+ {
+ mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL );
+
+ mlt_position first_position = (first_frame != NULL) ? mlt_frame_get_position( first_frame ) : -1;
+
+ // Get the real producer
+ mlt_producer real_producer = mlt_properties_get_data( properties, "producer", NULL );
+
+ // get properties
+ int strobe = mlt_properties_get_int( properties, "strobe");
+ int freeze = mlt_properties_get_int( properties, "freeze");
+ int freeze_after = mlt_properties_get_int( properties, "freeze_after");
+ int freeze_before = mlt_properties_get_int( properties, "freeze_before");
+
+ mlt_position need_first;
+
+ if (!freeze || freeze_after || freeze_before) {
+ double prod_speed = mlt_properties_get_double( properties, "_speed");
+ double actual_position = prod_speed * (double) mlt_producer_position( this );
+
+ if (mlt_properties_get_int( properties, "reverse")) actual_position = mlt_producer_get_playtime(this) - actual_position;
+
+ if (strobe < 2)
+ {
+ need_first = floor( actual_position );
+ }
+ else
+ {
+ // Strobe effect wanted, calculate frame position
+ need_first = floor( actual_position );
+ need_first -= need_first%strobe;
+ }
+ if (freeze)
+ {
+ if (freeze_after && need_first > freeze) need_first = freeze;
+ else if (freeze_before && need_first < freeze) need_first = freeze;
+ }
+ }
+ else need_first = freeze;
+
+ if( need_first != first_position )
+ {
+ mlt_frame_close( first_frame );
+ first_position = -1;
+ first_frame = NULL;
+ }
+
+ if( first_frame == NULL )
+ {
+ // Seek the producer to the correct place
+ mlt_producer_seek( real_producer, need_first );
+
+ // Get the frame
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index );
+ }
+
+ // Make sure things are in their place
+ mlt_properties_set_data( properties, "first_frame", first_frame, 0, NULL, NULL );
+
+ // Stack the producer and producer's get image
+ mlt_frame_push_service( *frame, first_frame );
+ mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( first_frame ) );
+
+ mlt_frame_push_service( *frame, this );
+ mlt_frame_push_service( *frame, framebuffer_get_image );
+
+
+ // Give the returned frame temporal identity
+ mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+ }
+
+ return 0;
+}
+
+
+mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ if ( !arg ) return NULL;
+ mlt_producer this = NULL;
+ this = calloc( 1, sizeof( struct mlt_producer_s ) );
+ mlt_producer_init( this, NULL );
+
+ // Wrap fezzik
+ mlt_producer real_producer;
+
+ // Check if a speed was specified.
+ /**
+
+ * Speed must be appended to the filename with '?'. To play your video at 50%:
+ inigo framebuffer:my_video.mpg?0.5
+
+ * Stroboscope effect can be obtained by adding a stobe=x parameter, where
+ x is the number of frames that will be ignored.
+
+ * You can play the movie backwards by adding reverse=1
+
+ * You can freeze the clip at a determined position by adding freeze=frame_pos
+ add freeze_after=1 to freeze only paste position or freeze_before to freeze before it
+
+ **/
+
+ double speed = 0.0;
+ char *props = strdup( arg );
+ char *ptr = strrchr( props, '?' );
+
+ if ( ptr )
+ {
+ speed = atof( ptr + 1 );
+ if ( speed != 0.0 )
+ // If speed was valid, then strip it and the delimiter.
+ // Otherwise, an invalid speed probably means this '?' was not a delimiter.
+ *ptr = '\0';
+ }
+
+ real_producer = mlt_factory_producer( profile, "fezzik", props );
+ free( props );
+
+ if (speed == 0.0) speed = 1.0;
+
+ if ( this != NULL && real_producer != NULL)
+ {
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Fezzik normalised it for us already
+ mlt_properties_set_int( properties, "fezzik_normalised", 1);
+ mlt_properties_set( properties, "resource", arg);
+
+ // Store the producer and fitler
+ mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+
+ // Grab some stuff from the real_producer
+ mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "length, width,height" );
+
+ if ( speed < 0 )
+ {
+ speed = -speed;
+ mlt_properties_set_int( properties, "reverse", 1 );
+ }
+
+ if ( speed != 1.0 )
+ {
+ double real_length = ( (double) mlt_producer_get_length( real_producer ) ) / speed;
+ mlt_properties_set_position( properties, "length", real_length );
+ }
+ mlt_properties_set_position( properties, "out", mlt_producer_get_length( this ) - 1 );
+
+ // Since we control the seeking, prevent it from seeking on its own
+ mlt_producer_set_speed( real_producer, 0 );
+ mlt_producer_set_speed( this, speed );
+
+ // Override the get_frame method
+ this->get_frame = producer_get_frame;
+ }
+ else
+ {
+ if ( this )
+ mlt_producer_close( this );
+ if ( real_producer )
+ mlt_producer_close( real_producer );
+
+ this = NULL;
+ }
+ return this;
+}
--- /dev/null
+include ../../../config.mak
+include config.mak
+
+TARGET = ../libmltkino.so
+
+OBJS = factory.o producer_kino.o
+CPPOBJS = kino_wrapper.o avi.o error.o filehandler.o riff.o
+
+CFLAGS += -I../../
+CXXFLAGS += $(CFLAGS) -Wno-deprecated
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lstdc++
+
+ifdef HAVE_LIBQUICKTIME
+CFLAGS += `pkg-config --cflags libquicktime`
+CXXFLAGS += `pkg-config --cflags libquicktime`
+LDFLAGS += `pkg-config --libs libquicktime`
+endif
+
+ifdef HAVE_LIBDV
+CFLAGS += `pkg-config --cflags libdv`
+LDFLAGS += `pkg-config --libs libdv`
+endif
+
+
+SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cc)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS) $(CPPOBJS)
+ $(CC) -shared -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend config.h config.mak
+
+clean:
+ rm -f $(OBJS) $(TARGET) $(CPPOBJS)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+* avi.cc library for AVI file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU 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.
+*/
+
+#include "config.h"
+
+// C++ includes
+
+#include <string>
+#include <iostream>
+#include <iomanip>
+
+using std::cout;
+using std::hex;
+using std::dec;
+using std::setw;
+using std::setfill;
+using std::endl;
+
+// C includes
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+// local includes
+
+#include "error.h"
+#include "riff.h"
+#include "avi.h"
+
+#define PADDING_SIZE (512)
+#define PADDING_1GB (0x40000000)
+#define IX00_INDEX_SIZE (4028)
+
+#define AVIF_HASINDEX 0x00000010
+#define AVIF_MUSTUSEINDEX 0x00000020
+#define AVIF_TRUSTCKTYPE 0x00000800
+#define AVIF_ISINTERLEAVED 0x00000100
+#define AVIF_WASCAPTUREFILE 0x00010000
+#define AVIF_COPYRIGHTED 0x00020000
+
+
+//static char g_zeroes[ PADDING_SIZE ];
+
+/** The constructor
+
+ \todo mainHdr not initialized
+ \todo add checking for NULL pointers
+
+*/
+
+AVIFile::AVIFile() : RIFFFile(),
+ idx1( NULL ), file_list( -1 ), riff_list( -1 ),
+ hdrl_list( -1 ), avih_chunk( -1 ), movi_list( -1 ), junk_chunk( -1 ), idx1_chunk( -1 ),
+ index_type( -1 ), current_ix00( -1 ), odml_list( -1 ), dmlh_chunk( -1 ), isUpdateIdx1( true )
+{
+ // cerr << "0x" << hex << (long)this << dec << " AVIFile::AVIFile() : RIFFFile(), ..." << endl;
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ indx[ i ] = new AVISuperIndex;
+ memset( indx[ i ], 0, sizeof( AVISuperIndex ) );
+ ix[ i ] = new AVIStdIndex;
+ memset( ix[ i ], 0, sizeof( AVIStdIndex ) );
+ indx_chunk[ i ] = -1;
+ ix_chunk[ i ] = -1;
+ strl_list[ i ] = -1;
+ strh_chunk[ i ] = -1;
+ strf_chunk[ i ] = -1;
+ }
+ idx1 = new AVISimpleIndex;
+ memset( idx1, 0, sizeof( AVISimpleIndex ) );
+}
+
+
+/** The copy constructor
+
+ \todo add checking for NULL pointers
+
+*/
+
+AVIFile::AVIFile( const AVIFile& avi ) : RIFFFile( avi )
+{
+ // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile::AVIFile(const AVIFile& avi) : RIFFFile(avi)" << endl;
+
+ mainHdr = avi.mainHdr;
+ idx1 = new AVISimpleIndex;
+ *idx1 = *avi.idx1;
+ file_list = avi.file_list;
+ riff_list = avi.riff_list;
+ hdrl_list = avi.hdrl_list;
+ avih_chunk = avi.avih_chunk;
+ movi_list = avi.movi_list;
+ junk_chunk = avi.junk_chunk;
+ idx1_chunk = avi.idx1_chunk;
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ indx[ i ] = new AVISuperIndex;
+ *indx[ i ] = *avi.indx[ i ];
+ ix[ i ] = new AVIStdIndex;
+ *ix[ i ] = *avi.ix[ i ];
+ indx_chunk[ i ] = avi.indx_chunk[ i ];
+ ix_chunk[ i ] = avi.ix_chunk[ i ];
+ strl_list[ i ] = avi.strl_list[ i ];
+ strh_chunk[ i ] = avi.strh_chunk[ i ];
+ strf_chunk[ i ] = avi.strf_chunk[ i ];
+ }
+
+ index_type = avi.index_type;
+ current_ix00 = avi.current_ix00;
+
+ for ( int i = 0; i < 62; ++i )
+ dmlh[ i ] = avi.dmlh[ i ];
+
+ isUpdateIdx1 = avi.isUpdateIdx1;
+
+}
+
+
+/** The assignment operator
+
+*/
+
+AVIFile& AVIFile::operator=( const AVIFile& avi )
+{
+ // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile& AVIFile::operator=(const AVIFile& avi)" << endl;
+
+ if ( this != &avi )
+ {
+ RIFFFile::operator=( avi );
+ mainHdr = avi.mainHdr;
+ *idx1 = *avi.idx1;
+ file_list = avi.file_list;
+ riff_list = avi.riff_list;
+ hdrl_list = avi.hdrl_list;
+ avih_chunk = avi.avih_chunk;
+ movi_list = avi.movi_list;
+ junk_chunk = avi.junk_chunk;
+ idx1_chunk = avi.idx1_chunk;
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ *indx[ i ] = *avi.indx[ i ];
+ *ix[ i ] = *avi.ix[ i ];
+ indx_chunk[ i ] = avi.indx_chunk[ i ];
+ ix_chunk[ i ] = avi.ix_chunk[ i ];
+ strl_list[ i ] = avi.strl_list[ i ];
+ strh_chunk[ i ] = avi.strh_chunk[ i ];
+ strf_chunk[ i ] = avi.strf_chunk[ i ];
+ }
+
+ index_type = avi.index_type;
+ current_ix00 = avi.current_ix00;
+
+ for ( int i = 0; i < 62; ++i )
+ dmlh[ i ] = avi.dmlh[ i ];
+
+ isUpdateIdx1 = avi.isUpdateIdx1;
+ }
+ return *this;
+}
+
+
+/** The destructor
+
+*/
+
+AVIFile::~AVIFile()
+{
+ // cerr << "0x" << hex << (long)this << dec << " AVIFile::~AVIFile()" << endl;
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ delete ix[ i ];
+ delete indx[ i ];
+ }
+ delete idx1;
+}
+
+/** Initialize the AVI structure to its initial state, either for PAL or NTSC format
+
+ Initialize the AVIFile attributes: mainHdr, indx, ix00, idx1
+
+ \todo consolidate AVIFile::Init, AVI1File::Init, AVI2File::Init. They are somewhat redundant.
+ \param format pass AVI_PAL or AVI_NTSC
+ \param sampleFrequency the sample frequency of the audio content
+ \param indexType pass AVI_SMALL_INDEX or AVI_LARGE_INDEX
+
+*/
+
+void AVIFile::Init( int format, int sampleFrequency, int indexType )
+{
+ int i, j;
+
+ assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
+
+ index_type = indexType;
+
+ switch ( format )
+ {
+ case AVI_PAL:
+ mainHdr.dwMicroSecPerFrame = 40000;
+ mainHdr.dwSuggestedBufferSize = 144008;
+ break;
+
+ case AVI_NTSC:
+ mainHdr.dwMicroSecPerFrame = 33366;
+ mainHdr.dwSuggestedBufferSize = 120008;
+ break;
+
+ default: /* no default allowed */
+ assert( 0 );
+ break;
+ }
+
+ /* Initialize the 'avih' chunk */
+
+ mainHdr.dwMaxBytesPerSec = 3600000 + sampleFrequency * 4;
+ mainHdr.dwPaddingGranularity = PADDING_SIZE;
+ mainHdr.dwFlags = AVIF_TRUSTCKTYPE;
+ if ( indexType & AVI_SMALL_INDEX )
+ mainHdr.dwFlags |= AVIF_HASINDEX;
+ mainHdr.dwTotalFrames = 0;
+ mainHdr.dwInitialFrames = 0;
+ mainHdr.dwStreams = 1;
+ mainHdr.dwWidth = 0;
+ mainHdr.dwHeight = 0;
+ mainHdr.dwReserved[ 0 ] = 0;
+ mainHdr.dwReserved[ 1 ] = 0;
+ mainHdr.dwReserved[ 2 ] = 0;
+ mainHdr.dwReserved[ 3 ] = 0;
+
+ /* Initialize the 'idx1' chunk */
+
+ for ( int i = 0; i < 8000; ++i )
+ {
+ idx1->aIndex[ i ].dwChunkId = 0;
+ idx1->aIndex[ i ].dwFlags = 0;
+ idx1->aIndex[ i ].dwOffset = 0;
+ idx1->aIndex[ i ].dwSize = 0;
+ }
+ idx1->nEntriesInUse = 0;
+
+ /* Initialize the 'indx' chunk */
+
+ for ( i = 0; i < 2; ++i )
+ {
+ indx[ i ] ->wLongsPerEntry = 4;
+ indx[ i ] ->bIndexSubType = 0;
+ indx[ i ] ->bIndexType = KINO_AVI_INDEX_OF_INDEXES;
+ indx[ i ] ->nEntriesInUse = 0;
+ indx[ i ] ->dwReserved[ 0 ] = 0;
+ indx[ i ] ->dwReserved[ 1 ] = 0;
+ indx[ i ] ->dwReserved[ 2 ] = 0;
+ for ( j = 0; j < 2014; ++j )
+ {
+ indx[ i ] ->aIndex[ j ].qwOffset = 0;
+ indx[ i ] ->aIndex[ j ].dwSize = 0;
+ indx[ i ] ->aIndex[ j ].dwDuration = 0;
+ }
+ }
+
+ /* The ix00 and ix01 chunk will be added dynamically in avi_write_frame
+ as needed */
+
+ /* Initialize the 'dmlh' chunk. I have no clue what this means
+ though */
+
+ for ( i = 0; i < 62; ++i )
+ dmlh[ i ] = 0;
+ //dmlh[0] = -1; /* frame count + 1? */
+
+}
+
+
+/** Find position and size of a given frame in the file
+
+ Depending on which index is available, search one of them to
+ find position and frame size
+
+ \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
+ \todo all index related operations should be isolated
+ \param offset the file offset to the start of the frame
+ \param size the size of the frame
+ \param frameNum the number of the frame we wish to find
+ \return 0 if the frame could be found, -1 otherwise
+*/
+
+int AVIFile::GetDVFrameInfo( off_t &offset, int &size, int frameNum )
+{
+ switch ( index_type )
+ {
+ case AVI_LARGE_INDEX:
+
+ /* find relevant index in indx0 */
+
+ int i;
+
+ for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
+ ;
+
+ if ( i != current_ix00 )
+ {
+ fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
+ current_ix00 = i;
+ }
+
+ if ( frameNum < ix[ 0 ] ->nEntriesInUse )
+ {
+ offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
+ size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
+ return 0;
+ }
+ else
+ return -1;
+ break;
+
+ case AVI_SMALL_INDEX:
+ int index = -1;
+ int frameNumIndex = 0;
+ for ( int i = 0; i < idx1->nEntriesInUse; ++i )
+ {
+ FOURCC chunkID1 = make_fourcc( "00dc" );
+ FOURCC chunkID2 = make_fourcc( "00db" );
+ if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
+ idx1->aIndex[ i ].dwChunkId == chunkID2 )
+ {
+ if ( frameNumIndex == frameNum )
+ {
+ index = i;
+ break;
+ }
+ ++frameNumIndex;
+ }
+ }
+ if ( index != -1 )
+ {
+ // compatibility check for broken dvgrab dv2 format
+ if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
+ {
+ offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
+ }
+ else
+ {
+ // new, correct dv2 format
+ offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
+ }
+ size = idx1->aIndex[ index ].dwSize;
+ return 0;
+ }
+ else
+ return -1;
+ break;
+ }
+ return -1;
+}
+
+/** Find position and size of a given frame in the file
+
+ Depending on which index is available, search one of them to
+ find position and frame size
+
+ \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
+ \todo all index related operations should be isolated
+ \param offset the file offset to the start of the frame
+ \param size the size of the frame
+ \param frameNum the number of the frame we wish to find
+ \param chunkID the ID of the type of chunk we want
+ \return 0 if the frame could be found, -1 otherwise
+*/
+
+int AVIFile::GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID )
+{
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ int i;
+
+ for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
+ ;
+
+ if ( i != current_ix00 )
+ {
+ fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
+ current_ix00 = i;
+ }
+
+ if ( frameNum < ix[ 0 ] ->nEntriesInUse )
+ {
+ if ( ( FOURCC ) ix[ 0 ] ->dwChunkId == chunkID )
+ {
+ offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
+ size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
+ return 0;
+ }
+ }
+ }
+ if ( index_type & AVI_SMALL_INDEX )
+ {
+ int index = -1;
+ int frameNumIndex = 0;
+ for ( int i = 0; i < idx1->nEntriesInUse; ++i )
+ {
+ if ( idx1->aIndex[ i ].dwChunkId == chunkID )
+ {
+ if ( frameNumIndex == frameNum )
+ {
+ index = i;
+ break;
+ }
+ ++frameNumIndex;
+ }
+ }
+ if ( index != -1 )
+ {
+ // compatibility check for broken dvgrab dv2 format
+ if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
+ {
+ offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
+ }
+ else
+ {
+ // new, correct dv2 format
+ offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
+ }
+ size = idx1->aIndex[ index ].dwSize;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/** Read in a frame
+
+ \todo we actually don't need the frame here, we could use just a void pointer
+ \param frame a reference to the frame object that will receive the frame data
+ \param frameNum the frame number to read
+ \return 0 if the frame could be read, -1 otherwise
+*/
+
+int AVIFile::GetDVFrame( uint8_t *data, int frameNum )
+{
+ off_t offset;
+ int size;
+
+ if ( GetDVFrameInfo( offset, size, frameNum ) != 0 || size < 0 )
+ return -1;
+ pthread_mutex_lock( &file_mutex );
+ fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, data, size ) );
+ pthread_mutex_unlock( &file_mutex );
+
+ return 0;
+}
+
+/** Read in a frame
+
+ \param data a pointer to the audio buffer
+ \param frameNum the frame number to read
+ \param chunkID the ID of the type of chunk we want
+ \return the size the of the frame data, 0 if could not be read
+*/
+
+int AVIFile::getFrame( void *data, int frameNum, FOURCC chunkID )
+{
+ off_t offset;
+ int size;
+
+ if ( GetFrameInfo( offset, size, frameNum, chunkID ) != 0 )
+ return 0;
+ fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, data, size ) );
+
+ return size;
+}
+
+int AVIFile::GetTotalFrames() const
+{
+ return mainHdr.dwTotalFrames;
+}
+
+
+/** prints out a directory entry in text form
+
+ Every subclass of RIFFFile is supposed to override this function
+ and to implement it for the entry types it knows about. For all
+ other entry types it should call its parent::PrintDirectoryData.
+
+ \todo use 64 bit routines
+ \param entry the entry to print
+*/
+
+void AVIFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
+{
+ static FOURCC lastStreamType = make_fourcc( " " );
+
+ if ( entry.type == make_fourcc( "avih" ) )
+ {
+
+ int i;
+ MainAVIHeader main_avi_header;
+
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &main_avi_header, sizeof( MainAVIHeader ) ) );
+
+ cout << " dwMicroSecPerFrame: " << ( int ) main_avi_header.dwMicroSecPerFrame << endl
+ << " dwMaxBytesPerSec: " << ( int ) main_avi_header.dwMaxBytesPerSec << endl
+ << " dwPaddingGranularity: " << ( int ) main_avi_header.dwPaddingGranularity << endl
+ << " dwFlags: " << ( int ) main_avi_header.dwFlags << endl
+ << " dwTotalFrames: " << ( int ) main_avi_header.dwTotalFrames << endl
+ << " dwInitialFrames: " << ( int ) main_avi_header.dwInitialFrames << endl
+ << " dwStreams: " << ( int ) main_avi_header.dwStreams << endl
+ << " dwSuggestedBufferSize: " << ( int ) main_avi_header.dwSuggestedBufferSize << endl
+ << " dwWidth: " << ( int ) main_avi_header.dwWidth << endl
+ << " dwHeight: " << ( int ) main_avi_header.dwHeight << endl;
+ for ( i = 0; i < 4; ++i )
+ cout << " dwReserved[" << i << "]: " << ( int ) main_avi_header.dwReserved[ i ] << endl;
+
+ }
+ else if ( entry.type == make_fourcc( "strh" ) )
+ {
+
+ AVIStreamHeader avi_stream_header;
+
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &avi_stream_header, sizeof( AVIStreamHeader ) ) );
+
+ lastStreamType = avi_stream_header.fccType;
+
+ cout << " fccType: '"
+ << ((char *)&avi_stream_header.fccType)[0]
+ << ((char *)&avi_stream_header.fccType)[1]
+ << ((char *)&avi_stream_header.fccType)[2]
+ << ((char *)&avi_stream_header.fccType)[3]
+ << '\'' << endl
+ << " fccHandler: '"
+ << ((char *)&avi_stream_header.fccHandler)[0]
+ << ((char *)&avi_stream_header.fccHandler)[1]
+ << ((char *)&avi_stream_header.fccHandler)[2]
+ << ((char *)&avi_stream_header.fccHandler)[3]
+ << '\'' << endl
+ << " dwFlags: " << ( int ) avi_stream_header.dwFlags << endl
+ << " wPriority: " << ( int ) avi_stream_header.wPriority << endl
+ << " wLanguage: " << ( int ) avi_stream_header.wLanguage << endl
+ << " dwInitialFrames: " << ( int ) avi_stream_header.dwInitialFrames << endl
+ << " dwScale: " << ( int ) avi_stream_header.dwScale << endl
+ << " dwRate: " << ( int ) avi_stream_header.dwRate << endl
+ << " dwLength: " << ( int ) avi_stream_header.dwLength << endl
+ << " dwQuality: " << ( int ) avi_stream_header.dwQuality << endl
+ << " dwSampleSize: " << ( int ) avi_stream_header.dwSampleSize << endl;
+
+ }
+ else if ( entry.type == make_fourcc( "indx" ) )
+ {
+
+ int i;
+ AVISuperIndex avi_super_index;
+
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &avi_super_index, sizeof( AVISuperIndex ) ) );
+
+ cout << " wLongsPerEntry: " << ( int ) avi_super_index.wLongsPerEntry
+ << endl
+ << " bIndexSubType: " << ( int ) avi_super_index.bIndexSubType << endl
+ << " bIndexType: " << ( int ) avi_super_index.bIndexType << endl
+ << " nEntriesInUse: " << ( int ) avi_super_index.nEntriesInUse << endl
+ << " dwChunkId: '"
+ << ((char *)&avi_super_index.dwChunkId)[0]
+ << ((char *)&avi_super_index.dwChunkId)[1]
+ << ((char *)&avi_super_index.dwChunkId)[2]
+ << ((char *)&avi_super_index.dwChunkId)[3]
+ << '\'' << endl
+ << " dwReserved[0]: " << ( int ) avi_super_index.dwReserved[ 0 ] << endl
+ << " dwReserved[1]: " << ( int ) avi_super_index.dwReserved[ 1 ] << endl
+ << " dwReserved[2]: " << ( int ) avi_super_index.dwReserved[ 2 ] << endl;
+ for ( i = 0; i < avi_super_index.nEntriesInUse; ++i )
+ {
+ cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
+ << ": qwOffset : 0x" << setw( 12 ) << setfill( '0' ) << hex << avi_super_index.aIndex[ i ].qwOffset << endl
+ << " dwSize : 0x" << setw( 8 ) << avi_super_index.aIndex[ i ].dwSize << endl
+ << " dwDuration : " << dec << avi_super_index.aIndex[ i ].dwDuration << endl;
+ }
+ }
+ else if ( entry.type == make_fourcc( "strf" ) )
+ {
+ if ( lastStreamType == make_fourcc( "auds" ) )
+ {
+ WAVEFORMATEX waveformatex;
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &waveformatex, sizeof( WAVEFORMATEX ) ) );
+ cout << " waveformatex.wFormatTag : " << waveformatex.wFormatTag << endl;
+ cout << " waveformatex.nChannels : " << waveformatex.nChannels << endl;
+ cout << " waveformatex.nSamplesPerSec : " << waveformatex.nSamplesPerSec << endl;
+ cout << " waveformatex.nAvgBytesPerSec: " << waveformatex.nAvgBytesPerSec << endl;
+ cout << " waveformatex.nBlockAlign : " << waveformatex.nBlockAlign << endl;
+ cout << " waveformatex.wBitsPerSample : " << waveformatex.wBitsPerSample << endl;
+ cout << " waveformatex.cbSize : " << waveformatex.cbSize << endl;
+ }
+ else if ( lastStreamType == make_fourcc( "vids" ) )
+ {
+ BITMAPINFOHEADER bitmapinfo;
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &bitmapinfo, sizeof( BITMAPINFOHEADER ) ) );
+ cout << " bitmapinfo.biSize : " << bitmapinfo.biSize << endl;
+ cout << " bitmapinfo.biWidth : " << bitmapinfo.biWidth << endl;
+ cout << " bitmapinfo.biHeight : " << bitmapinfo.biHeight << endl;
+ cout << " bitmapinfo.biPlanes : " << bitmapinfo.biPlanes << endl;
+ cout << " bitmapinfo.biBitCount : " << bitmapinfo.biBitCount << endl;
+ cout << " bitmapinfo.biCompression : " << bitmapinfo.biCompression << endl;
+ cout << " bitmapinfo.biSizeImage : " << bitmapinfo.biSizeImage << endl;
+ cout << " bitmapinfo.biXPelsPerMeter: " << bitmapinfo.biXPelsPerMeter << endl;
+ cout << " bitmapinfo.biYPelsPerMeter: " << bitmapinfo.biYPelsPerMeter << endl;
+ cout << " bitmapinfo.biClrUsed : " << bitmapinfo.biClrUsed << endl;
+ cout << " bitmapinfo.biClrImportant : " << bitmapinfo.biClrImportant << endl;
+ }
+ else if ( lastStreamType == make_fourcc( "iavs" ) )
+ {
+ DVINFO dvinfo;
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &dvinfo, sizeof( DVINFO ) ) );
+ cout << " dvinfo.dwDVAAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc << endl;
+ cout << " dvinfo.dwDVAAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl << endl;
+ cout << " dvinfo.dwDVAAuxSrc1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc1 << endl;
+ cout << " dvinfo.dwDVAAuxCtl1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl1 << endl;
+ cout << " dvinfo.dwDVVAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxSrc << endl;
+ cout << " dvinfo.dwDVVAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxCtl << endl;
+ }
+ }
+
+ /* This is the Standard Index. It is an array of offsets and
+ sizes relative to some start offset. */
+
+ else if ( ( entry.type == make_fourcc( "ix00" ) ) || ( entry.type == make_fourcc( "ix01" ) ) )
+ {
+
+ int i;
+ AVIStdIndex avi_std_index;
+
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, &avi_std_index, sizeof( AVIStdIndex ) ) );
+
+ cout << " wLongsPerEntry: " << ( int ) avi_std_index.wLongsPerEntry
+ << endl
+ << " bIndexSubType: " << ( int ) avi_std_index.bIndexSubType << endl
+ << " bIndexType: " << ( int ) avi_std_index.bIndexType << endl
+ << " nEntriesInUse: " << ( int ) avi_std_index.nEntriesInUse << endl
+ << " dwChunkId: '"
+ << ((char *)&avi_std_index.dwChunkId)[0]
+ << ((char *)&avi_std_index.dwChunkId)[1]
+ << ((char *)&avi_std_index.dwChunkId)[2]
+ << ((char *)&avi_std_index.dwChunkId)[3]
+ << '\'' << endl
+ << " qwBaseOffset: 0x" << setw( 12 ) << hex << avi_std_index.qwBaseOffset << endl
+ << " dwReserved: " << dec << ( int ) avi_std_index.dwReserved << endl;
+ for ( i = 0; i < avi_std_index.nEntriesInUse; ++i )
+ {
+ cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
+ << ": dwOffset : 0x" << setw( 8 ) << setfill( '0' ) << hex << avi_std_index.aIndex[ i ].dwOffset
+ << " (0x" << setw( 12 ) << avi_std_index.qwBaseOffset + avi_std_index.aIndex[ i ].dwOffset << ')' << endl
+ << " dwSize : 0x" << setw( 8 ) << avi_std_index.aIndex[ i ].dwSize << dec << endl;
+ }
+
+ }
+ else if ( entry.type == make_fourcc( "idx1" ) )
+ {
+
+ int i;
+ int numEntries = entry.length / sizeof( int ) / 4;
+ DWORD *idx1 = new DWORD[ numEntries * 4 ];
+ // FOURCC movi_list = FindDirectoryEntry(make_fourcc("movi"));
+
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, idx1, entry.length ) );
+
+ for ( i = 0; i < numEntries; ++i )
+ {
+
+ cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": dwChunkId : '"
+ << ((char *)&idx1[ i * 4 + 0 ])[0]
+ << ((char *)&idx1[ i * 4 + 0 ])[1]
+ << ((char *)&idx1[ i * 4 + 0 ])[2]
+ << ((char *)&idx1[ i * 4 + 0 ])[3]
+ << '\'' << endl
+ << " dwType : 0x" << setw( 8 ) << hex << idx1[ i * 4 + 1 ] << endl
+ << " dwOffset : 0x" << setw( 8 ) << idx1[ i * 4 + 2 ] << endl
+ // << " (0x" << setw(8) << idx1[i * 4 + 2] + GetDirectoryEntry(movi_list).offset << ')' << endl
+ << " dwSize : 0x" << setw( 8 ) << idx1[ i * 4 + 3 ] << dec << endl;
+ }
+
+ delete[] idx1;
+ }
+ else if ( entry.type == make_fourcc( "dmlh" ) )
+ {
+ int i;
+ int numEntries = entry.length / sizeof( int );
+ DWORD *dmlh = new DWORD[ numEntries ];
+
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, dmlh, entry.length ) );
+
+ for ( i = 0; i < numEntries; ++i )
+ {
+ cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": "
+ << " dwTotalFrames: 0x" << setw( 8 ) << hex << dmlh[ i ]
+ << " (" << dec << dmlh[ i ] << ")" << endl;
+ }
+ delete[] dmlh;
+ }
+}
+
+
+/** If this is not a movi list, read its contents
+
+*/
+
+void AVIFile::ParseList( int parent )
+{
+ FOURCC type;
+ FOURCC name;
+ DWORD length;
+ int list;
+ off_t pos;
+ off_t listEnd;
+
+ /* Read in the chunk header (type and length). */
+ fail_neg( read( fd, &type, sizeof( type ) ) );
+ fail_neg( read( fd, &length, sizeof( length ) ) );
+ if ( length & 1 )
+ length++;
+
+ /* The contents of the list starts here. Obtain its offset. The list
+ name (4 bytes) is already part of the contents). */
+
+ pos = lseek( fd, 0, SEEK_CUR );
+ fail_if( pos == ( off_t ) - 1 );
+ fail_neg( read( fd, &name, sizeof( name ) ) );
+
+ /* if we encounter a movi list, do not read it. It takes too much time
+ and we don't need it anyway. */
+
+ if ( name != make_fourcc( "movi" ) )
+ {
+ // if (1) {
+
+ /* Add an entry for this list. */
+ list = AddDirectoryEntry( type, name, sizeof( name ), parent );
+
+ /* Read in any chunks contained in this list. This list is the
+ parent for all chunks it contains. */
+
+ listEnd = pos + length;
+ while ( pos < listEnd )
+ {
+ ParseChunk( list );
+ pos = lseek( fd, 0, SEEK_CUR );
+ fail_if( pos == ( off_t ) - 1 );
+ }
+ }
+ else
+ {
+ /* Add an entry for this list. */
+
+ movi_list = AddDirectoryEntry( type, name, length, parent );
+
+ pos = lseek( fd, length - 4, SEEK_CUR );
+ fail_if( pos == ( off_t ) - 1 );
+ }
+}
+
+
+void AVIFile::ParseRIFF()
+{
+ RIFFFile::ParseRIFF();
+
+ avih_chunk = FindDirectoryEntry( make_fourcc( "avih" ) );
+ if ( avih_chunk != -1 )
+ ReadChunk( avih_chunk, ( void* ) & mainHdr, sizeof( MainAVIHeader ) );
+}
+
+
+void AVIFile::ReadIndex()
+{
+ indx_chunk[ 0 ] = FindDirectoryEntry( make_fourcc( "indx" ) );
+ if ( indx_chunk[ 0 ] != -1 )
+ {
+ ReadChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ], sizeof( AVISuperIndex ) );
+ index_type = AVI_LARGE_INDEX;
+
+ /* recalc number of frames from each index */
+ mainHdr.dwTotalFrames = 0;
+ for ( int i = 0;
+ i < indx[ 0 ] ->nEntriesInUse;
+ mainHdr.dwTotalFrames += indx[ 0 ] ->aIndex[ i++ ].dwDuration )
+ ;
+ return ;
+ }
+ idx1_chunk = FindDirectoryEntry( make_fourcc( "idx1" ) );
+ if ( idx1_chunk != -1 )
+ {
+ ReadChunk( idx1_chunk, ( void* ) idx1, sizeof( AVISuperIndex ) );
+ idx1->nEntriesInUse = GetDirectoryEntry( idx1_chunk ).length / 16;
+ index_type = AVI_SMALL_INDEX;
+
+ /* recalc number of frames from the simple index */
+ int frameNumIndex = 0;
+ FOURCC chunkID1 = make_fourcc( "00dc" );
+ FOURCC chunkID2 = make_fourcc( "00db" );
+ for ( int i = 0; i < idx1->nEntriesInUse; ++i )
+ {
+ if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
+ idx1->aIndex[ i ].dwChunkId == chunkID2 )
+ {
+ ++frameNumIndex;
+ }
+ }
+ mainHdr.dwTotalFrames = frameNumIndex;
+ return ;
+ }
+}
+
+
+void AVIFile::FlushIndx( int stream )
+{
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+ int i;
+
+ /* Write out the previous index. When this function is
+ entered for the first time, there is no index to
+ write. Note: this may be an expensive operation
+ because of a time consuming seek to the former file
+ position. */
+
+ if ( ix_chunk[ stream ] != -1 )
+ WriteChunk( ix_chunk[ stream ], ix[ stream ] );
+
+ /* make a new ix chunk. */
+
+ if ( stream == 0 )
+ type = make_fourcc( "ix00" );
+ else
+ type = make_fourcc( "ix01" );
+ ix_chunk[ stream ] = AddDirectoryEntry( type, 0, sizeof( AVIStdIndex ), movi_list );
+ GetDirectoryEntry( ix_chunk[ stream ], type, name, length, offset, parent );
+
+ /* fill out all required fields. The offsets in the
+ array are relative to qwBaseOffset, so fill in the
+ offset to the next free location in the file
+ there. */
+
+ ix[ stream ] ->wLongsPerEntry = 2;
+ ix[ stream ] ->bIndexSubType = 0;
+ ix[ stream ] ->bIndexType = KINO_AVI_INDEX_OF_CHUNKS;
+ ix[ stream ] ->nEntriesInUse = 0;
+ ix[ stream ] ->dwChunkId = indx[ stream ] ->dwChunkId;
+ ix[ stream ] ->qwBaseOffset = offset + length;
+ ix[ stream ] ->dwReserved = 0;
+
+ for ( i = 0; i < IX00_INDEX_SIZE; ++i )
+ {
+ ix[ stream ] ->aIndex[ i ].dwOffset = 0;
+ ix[ stream ] ->aIndex[ i ].dwSize = 0;
+ }
+
+ /* add a reference to this new index in our super
+ index. */
+
+ i = indx[ stream ] ->nEntriesInUse++;
+ indx[ stream ] ->aIndex[ i ].qwOffset = offset - RIFF_HEADERSIZE;
+ indx[ stream ] ->aIndex[ i ].dwSize = length + RIFF_HEADERSIZE;
+ indx[ stream ] ->aIndex[ i ].dwDuration = 0;
+}
+
+
+void AVIFile::UpdateIndx( int stream, int chunk, int duration )
+{
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+ int i;
+
+ /* update the appropiate entry in the super index. It reflects
+ the number of frames in the referenced index. */
+
+ i = indx[ stream ] ->nEntriesInUse - 1;
+ indx[ stream ] ->aIndex[ i ].dwDuration += duration;
+
+ /* update the standard index. Calculate the file position of
+ the new frame. */
+
+ GetDirectoryEntry( chunk, type, name, length, offset, parent );
+
+ indx[ stream ] ->dwChunkId = type;
+ i = ix[ stream ] ->nEntriesInUse++;
+ ix[ stream ] ->aIndex[ i ].dwOffset = offset - ix[ stream ] ->qwBaseOffset;
+ ix[ stream ] ->aIndex[ i ].dwSize = length;
+}
+
+
+void AVIFile::UpdateIdx1( int chunk, int flags )
+{
+ if ( idx1->nEntriesInUse < 20000 )
+ {
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+
+ GetDirectoryEntry( chunk, type, name, length, offset, parent );
+
+ idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = type;
+ idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = flags;
+ idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = offset - GetDirectoryEntry( movi_list ).offset - RIFF_HEADERSIZE;
+ idx1->aIndex[ idx1->nEntriesInUse ].dwSize = length;
+ idx1->nEntriesInUse++;
+ }
+}
+
+bool AVIFile::verifyStreamFormat( FOURCC type )
+{
+ int i, j = 0;
+ AVIStreamHeader avi_stream_header;
+ BITMAPINFOHEADER bih;
+ FOURCC strh = make_fourcc( "strh" );
+ FOURCC strf = make_fourcc( "strf" );
+
+ while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
+ {
+ ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
+ if ( avi_stream_header.fccHandler == type )
+ return true;
+ }
+ j = 0;
+ while ( ( i = FindDirectoryEntry( strf, j++ ) ) != -1 )
+ {
+ ReadChunk( i, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
+ if ( ( FOURCC ) bih.biCompression == type )
+ return true;
+ }
+
+ return false;
+}
+
+bool AVIFile::verifyStream( FOURCC type )
+{
+ int i, j = 0;
+ AVIStreamHeader avi_stream_header;
+ FOURCC strh = make_fourcc( "strh" );
+
+ while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
+ {
+ ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
+ if ( avi_stream_header.fccType == type )
+ return true;
+ }
+ return false;
+}
+
+bool AVIFile::isOpenDML( void )
+{
+ int i, j = 0;
+ FOURCC dmlh = make_fourcc( "dmlh" );
+
+ while ( ( i = FindDirectoryEntry( dmlh, j++ ) ) != -1 )
+ {
+ return true;
+ }
+ return false;
+}
+
+AVI1File::AVI1File() : AVIFile()
+{}
+
+
+AVI1File::~AVI1File()
+{}
+
+
+/* Initialize the AVI structure to its initial state, either for PAL
+ or NTSC format */
+
+void AVI1File::Init( int format, int sampleFrequency, int indexType )
+{
+ int num_blocks;
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+
+ assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
+
+ AVIFile::Init( format, sampleFrequency, indexType );
+
+ switch ( format )
+ {
+ case AVI_PAL:
+ mainHdr.dwWidth = 720;
+ mainHdr.dwHeight = 576;
+
+ streamHdr[ 0 ].dwScale = 1;
+ streamHdr[ 0 ].dwRate = 25;
+ streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
+
+ /* initialize the 'strf' chunk */
+
+ /* Meaning of the DV stream format chunk per Microsoft
+ dwDVAAuxSrc
+ Specifies the Audio Auxiliary Data Source Pack for the first audio block
+ (first 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of
+ a frame. A DIF sequence is a data block that contains 150 DIF blocks. A DIF block consists
+ of 80 bytes. The Audio Auxiliary Data Source Pack is defined in section D.7.1 of Part 2,
+ Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+ Consumer-use Digital VCRs.
+ dwDVAAuxCtl
+ Specifies the Audio Auxiliary Data Source Control Pack for the first audio block of a
+ frame. The Audio Auxiliary Data Control Pack is defined in section D.7.2 of Part 2,
+ Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+ Consumer-use Digital VCRs.
+ dwDVAAuxSrc1
+ Specifies the Audio Auxiliary Data Source Pack for the second audio block
+ (second 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of a frame.
+ dwDVAAuxCtl1
+ Specifies the Audio Auxiliary Data Source Control Pack for the second audio block of a frame.
+ dwDVVAuxSrc
+ Specifies the Video Auxiliary Data Source Pack as defined in section D.8.1 of Part 2,
+ Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+ Consumer-use Digital VCRs.
+ dwDVVAuxCtl
+ Specifies the Video Auxiliary Data Source Control Pack as defined in section D.8.2 of Part 2,
+ Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+ Consumer-use Digital VCRs.
+ dwDVReserved[2]
+ Reserved. Set this array to zero.
+ */
+
+ dvinfo.dwDVAAuxSrc = 0xd1e030d0;
+ dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
+ dvinfo.dwDVAAuxSrc1 = 0xd1e03fd0;
+ dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
+ dvinfo.dwDVVAuxSrc = 0xff20ffff;
+ dvinfo.dwDVVAuxCtl = 0xfffdc83f;
+ dvinfo.dwDVReserved[ 0 ] = 0;
+ dvinfo.dwDVReserved[ 1 ] = 0;
+ break;
+
+ case AVI_NTSC:
+ mainHdr.dwWidth = 720;
+ mainHdr.dwHeight = 480;
+
+ streamHdr[ 0 ].dwScale = 1001;
+ streamHdr[ 0 ].dwRate = 30000;
+ streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
+
+ /* initialize the 'strf' chunk */
+ dvinfo.dwDVAAuxSrc = 0xc0c000c0;
+ dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
+ dvinfo.dwDVAAuxSrc1 = 0xc0c001c0;
+ dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
+ dvinfo.dwDVVAuxSrc = 0xff80ffff;
+ dvinfo.dwDVVAuxCtl = 0xfffcc83f;
+ dvinfo.dwDVReserved[ 0 ] = 0;
+ dvinfo.dwDVReserved[ 1 ] = 0;
+ break;
+
+ default: /* no default allowed */
+ assert( 0 );
+ break;
+ }
+
+ indx[ 0 ] ->dwChunkId = make_fourcc( "00__" );
+
+ /* Initialize the 'strh' chunk */
+
+ streamHdr[ 0 ].fccType = make_fourcc( "iavs" );
+ streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
+ streamHdr[ 0 ].dwFlags = 0;
+ streamHdr[ 0 ].wPriority = 0;
+ streamHdr[ 0 ].wLanguage = 0;
+ streamHdr[ 0 ].dwInitialFrames = 0;
+ streamHdr[ 0 ].dwStart = 0;
+ streamHdr[ 0 ].dwLength = 0;
+ streamHdr[ 0 ].dwQuality = 0;
+ streamHdr[ 0 ].dwSampleSize = 0;
+ streamHdr[ 0 ].rcFrame.top = 0;
+ streamHdr[ 0 ].rcFrame.bottom = 0;
+ streamHdr[ 0 ].rcFrame.left = 0;
+ streamHdr[ 0 ].rcFrame.right = 0;
+
+ /* This is a simple directory structure setup. For details see the
+ "OpenDML AVI File Format Extensions" document.
+
+ An AVI file contains basically two types of objects, a
+ "chunk" and a "list" object. The list object contains any
+ number of chunks. Since a list is also a chunk, it is
+ possible to create a hierarchical "list of lists"
+ structure.
+
+ Every AVI file starts with a "RIFF" object, which is a list
+ of several other required objects. The actual DV data is
+ contained in a "movi" list, each frame is in its own chunk.
+
+ Old AVI files (pre OpenDML V. 1.02) contain only one RIFF
+ chunk of less than 1 GByte size per file. The current
+ format which allow for almost arbitrary sizes can contain
+ several RIFF chunks of less than 1 GByte size. Old software
+ however would only deal with the first RIFF chunk.
+
+ Note that the first entry (FILE) isn�t actually part
+ of the AVI file. I use this (pseudo-) directory entry to
+ keep track of the RIFF chunks and their positions in the
+ AVI file.
+ */
+
+ /* Create the container directory entry */
+
+ file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
+
+ /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
+
+ riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
+ hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
+ avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
+ strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
+ strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
+ strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( dvinfo ), strl_list[ 0 ] );
+ if ( index_type & AVI_LARGE_INDEX )
+ indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
+
+ odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
+ dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
+
+ /* align movi list to block */
+ GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
+ num_blocks = length / PADDING_SIZE + 1;
+ length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5?
+ junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+
+ movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+
+ /* The ix00 chunk will be added dynamically to the movi_list in avi_write_frame
+ as needed */
+
+ ix_chunk[ 0 ] = -1;
+}
+
+
+/* Write a DV video frame. This is somewhat complex... */
+
+#if 0
+bool AVI1File::WriteFrame( const Frame &frame )
+{
+ int frame_chunk;
+ int junk_chunk;
+ int num_blocks;
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+
+ /* exit if no large index and 1GB reached */
+ if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
+ return false;
+
+ /* Check if we need a new ix00 Standard Index. It has a
+ capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
+ number, we need a new index. The new ix00 chunk is also
+ part of the movi list. */
+
+ if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
+ FlushIndx( 0 );
+
+ /* Write the DV frame data.
+
+ Make a new 00__ chunk for the new frame, write out the
+ frame. */
+
+ frame_chunk = AddDirectoryEntry( make_fourcc( "00__" ), 0, frame.GetFrameSize(), movi_list );
+ if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
+ {
+ GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
+ ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
+ }
+ WriteChunk( frame_chunk, frame.data );
+ // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
+ // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
+ // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
+ // WriteChunk(junk_chunk, g_zeroes);
+
+ if ( index_type & AVI_LARGE_INDEX )
+ UpdateIndx( 0, frame_chunk, 1 );
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ UpdateIdx1( frame_chunk, 0x10 );
+
+ /* update some variables with the new frame count. */
+
+ if ( isUpdateIdx1 )
+ ++mainHdr.dwTotalFrames;
+ ++streamHdr[ 0 ].dwLength;
+ ++dmlh[ 0 ];
+
+ /* Find out if the current riff list is close to 1 GByte in
+ size. If so, start a new (extended) RIFF. The only allowed
+ item in the new RIFF chunk is a movi list (with video
+ frames and indexes as usual). */
+
+ GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+ if ( length > 0x3f000000 )
+ {
+ /* write idx1 only once and before end of first GB */
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ {
+ int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+ WriteChunk( idx1_chunk, ( void* ) idx1 );
+ }
+ isUpdateIdx1 = false;
+
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ /* pad out to 1GB */
+ //GetDirectoryEntry(riff_list, type, name, length, offset, parent);
+ //junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, PADDING_1GB - length - 5 * RIFF_HEADERSIZE, riff_list);
+ //WriteChunk(junk_chunk, g_zeroes);
+
+ /* padding for alignment */
+ GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+ num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
+ length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
+ if ( length > 0 )
+ {
+ junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+ WriteChunk( junk_chunk, g_zeroes );
+ }
+
+ riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
+ movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+ }
+ }
+ return true;
+}
+#endif
+
+void AVI1File::WriteRIFF()
+{
+
+ WriteChunk( avih_chunk, ( void* ) & mainHdr );
+ WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
+ WriteChunk( strf_chunk[ 0 ], ( void* ) & dvinfo );
+ WriteChunk( dmlh_chunk, ( void* ) & dmlh );
+
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
+ WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
+ }
+
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ {
+ int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+ WriteChunk( idx1_chunk, ( void* ) idx1 );
+ }
+
+ RIFFFile::WriteRIFF();
+}
+
+
+AVI2File::AVI2File() : AVIFile()
+{}
+
+
+AVI2File::~AVI2File()
+{}
+
+
+/* Initialize the AVI structure to its initial state, either for PAL
+ or NTSC format */
+
+void AVI2File::Init( int format, int sampleFrequency, int indexType )
+{
+ int num_blocks;
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+
+ assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
+
+ AVIFile::Init( format, sampleFrequency, indexType );
+
+ switch ( format )
+ {
+
+ case AVI_PAL:
+ mainHdr.dwStreams = 2;
+ mainHdr.dwWidth = 720;
+ mainHdr.dwHeight = 576;
+
+ /* Initialize the 'strh' chunk */
+
+ streamHdr[ 0 ].fccType = make_fourcc( "vids" );
+ streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
+ streamHdr[ 0 ].dwFlags = 0;
+ streamHdr[ 0 ].wPriority = 0;
+ streamHdr[ 0 ].wLanguage = 0;
+ streamHdr[ 0 ].dwInitialFrames = 0;
+ streamHdr[ 0 ].dwScale = 1;
+ streamHdr[ 0 ].dwRate = 25;
+ streamHdr[ 0 ].dwStart = 0;
+ streamHdr[ 0 ].dwLength = 0;
+ streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
+ streamHdr[ 0 ].dwQuality = -1;
+ streamHdr[ 0 ].dwSampleSize = 0;
+ streamHdr[ 0 ].rcFrame.top = 0;
+ streamHdr[ 0 ].rcFrame.bottom = 0;
+ streamHdr[ 0 ].rcFrame.left = 0;
+ streamHdr[ 0 ].rcFrame.right = 0;
+
+ bitmapinfo.biSize = sizeof( bitmapinfo );
+ bitmapinfo.biWidth = 720;
+ bitmapinfo.biHeight = 576;
+ bitmapinfo.biPlanes = 1;
+ bitmapinfo.biBitCount = 24;
+ bitmapinfo.biCompression = make_fourcc( "dvsd" );
+ bitmapinfo.biSizeImage = 144000;
+ bitmapinfo.biXPelsPerMeter = 0;
+ bitmapinfo.biYPelsPerMeter = 0;
+ bitmapinfo.biClrUsed = 0;
+ bitmapinfo.biClrImportant = 0;
+
+ streamHdr[ 1 ].fccType = make_fourcc( "auds" );
+ streamHdr[ 1 ].fccHandler = 0;
+ streamHdr[ 1 ].dwFlags = 0;
+ streamHdr[ 1 ].wPriority = 0;
+ streamHdr[ 1 ].wLanguage = 0;
+ streamHdr[ 1 ].dwInitialFrames = 0;
+ streamHdr[ 1 ].dwScale = 2 * 2;
+ streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
+ streamHdr[ 1 ].dwStart = 0;
+ streamHdr[ 1 ].dwLength = 0;
+ streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
+ streamHdr[ 1 ].dwQuality = -1;
+ streamHdr[ 1 ].dwSampleSize = 2 * 2;
+ streamHdr[ 1 ].rcFrame.top = 0;
+ streamHdr[ 1 ].rcFrame.bottom = 0;
+ streamHdr[ 1 ].rcFrame.left = 0;
+ streamHdr[ 1 ].rcFrame.right = 0;
+
+ break;
+
+ case AVI_NTSC:
+ mainHdr.dwTotalFrames = 0;
+ mainHdr.dwStreams = 2;
+ mainHdr.dwWidth = 720;
+ mainHdr.dwHeight = 480;
+
+ /* Initialize the 'strh' chunk */
+
+ streamHdr[ 0 ].fccType = make_fourcc( "vids" );
+ streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
+ streamHdr[ 0 ].dwFlags = 0;
+ streamHdr[ 0 ].wPriority = 0;
+ streamHdr[ 0 ].wLanguage = 0;
+ streamHdr[ 0 ].dwInitialFrames = 0;
+ streamHdr[ 0 ].dwScale = 1001;
+ streamHdr[ 0 ].dwRate = 30000;
+ streamHdr[ 0 ].dwStart = 0;
+ streamHdr[ 0 ].dwLength = 0;
+ streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
+ streamHdr[ 0 ].dwQuality = -1;
+ streamHdr[ 0 ].dwSampleSize = 0;
+ streamHdr[ 0 ].rcFrame.top = 0;
+ streamHdr[ 0 ].rcFrame.bottom = 0;
+ streamHdr[ 0 ].rcFrame.left = 0;
+ streamHdr[ 0 ].rcFrame.right = 0;
+
+ bitmapinfo.biSize = sizeof( bitmapinfo );
+ bitmapinfo.biWidth = 720;
+ bitmapinfo.biHeight = 480;
+ bitmapinfo.biPlanes = 1;
+ bitmapinfo.biBitCount = 24;
+ bitmapinfo.biCompression = make_fourcc( "dvsd" );
+ bitmapinfo.biSizeImage = 120000;
+ bitmapinfo.biXPelsPerMeter = 0;
+ bitmapinfo.biYPelsPerMeter = 0;
+ bitmapinfo.biClrUsed = 0;
+ bitmapinfo.biClrImportant = 0;
+
+ streamHdr[ 1 ].fccType = make_fourcc( "auds" );
+ streamHdr[ 1 ].fccHandler = 0;
+ streamHdr[ 1 ].dwFlags = 0;
+ streamHdr[ 1 ].wPriority = 0;
+ streamHdr[ 1 ].wLanguage = 0;
+ streamHdr[ 1 ].dwInitialFrames = 1;
+ streamHdr[ 1 ].dwScale = 2 * 2;
+ streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
+ streamHdr[ 1 ].dwStart = 0;
+ streamHdr[ 1 ].dwLength = 0;
+ streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
+ streamHdr[ 1 ].dwQuality = 0;
+ streamHdr[ 1 ].dwSampleSize = 2 * 2;
+ streamHdr[ 1 ].rcFrame.top = 0;
+ streamHdr[ 1 ].rcFrame.bottom = 0;
+ streamHdr[ 1 ].rcFrame.left = 0;
+ streamHdr[ 1 ].rcFrame.right = 0;
+
+ break;
+ }
+ waveformatex.wFormatTag = 1;
+ waveformatex.nChannels = 2;
+ waveformatex.nSamplesPerSec = sampleFrequency;
+ waveformatex.nAvgBytesPerSec = sampleFrequency * 2 * 2;
+ waveformatex.nBlockAlign = 4;
+ waveformatex.wBitsPerSample = 16;
+ waveformatex.cbSize = 0;
+
+ file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
+
+ /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
+
+ riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
+ hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
+ avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
+
+ strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
+ strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
+ strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( BITMAPINFOHEADER ), strl_list[ 0 ] );
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
+ ix_chunk[ 0 ] = -1;
+ indx[ 0 ] ->dwChunkId = make_fourcc( "00dc" );
+ }
+
+ strl_list[ 1 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
+ strh_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 1 ] );
+ strf_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( WAVEFORMATEX ) - 2, strl_list[ 1 ] );
+ junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, 2, strl_list[ 1 ] );
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ indx_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 1 ] );
+ ix_chunk[ 1 ] = -1;
+ indx[ 1 ] ->dwChunkId = make_fourcc( "01wb" );
+
+ odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
+ dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
+ }
+
+ /* align movi list to block */
+ GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
+ num_blocks = length / PADDING_SIZE + 1;
+ length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5 headers?
+ junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+
+ movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+
+ idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = make_fourcc( "7Fxx" );
+ idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = 0;
+ idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = 0;
+ idx1->aIndex[ idx1->nEntriesInUse ].dwSize = 0;
+ idx1->nEntriesInUse++;
+}
+
+
+void AVI2File::WriteRIFF()
+{
+ WriteChunk( avih_chunk, ( void* ) & mainHdr );
+ WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
+ WriteChunk( strf_chunk[ 0 ], ( void* ) & bitmapinfo );
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ WriteChunk( dmlh_chunk, ( void* ) & dmlh );
+ WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
+ WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
+ }
+ WriteChunk( strh_chunk[ 1 ], ( void* ) & streamHdr[ 1 ] );
+ WriteChunk( strf_chunk[ 1 ], ( void* ) & waveformatex );
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ WriteChunk( indx_chunk[ 1 ], ( void* ) indx[ 1 ] );
+ WriteChunk( ix_chunk[ 1 ], ( void* ) ix[ 1 ] );
+ }
+
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ {
+ int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+ WriteChunk( idx1_chunk, ( void* ) idx1 );
+ }
+ RIFFFile::WriteRIFF();
+}
+
+
+/** Write a DV video frame
+
+ \param frame the frame to write
+*/
+
+#if 0
+bool AVI2File::WriteFrame( const Frame &frame )
+{
+ int audio_chunk;
+ int frame_chunk;
+ int junk_chunk;
+ char soundbuf[ 20000 ];
+ int audio_size;
+ int num_blocks;
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+
+ /* exit if no large index and 1GB reached */
+ if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
+ return false;
+
+ /* Check if we need a new ix00 Standard Index. It has a
+ capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
+ number, we need a new index. The new ix00 chunk is also
+ part of the movi list. */
+
+ if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
+ {
+ FlushIndx( 0 );
+ FlushIndx( 1 );
+ }
+
+ /* Write audio data if we have it */
+
+ audio_size = frame.ExtractAudio( soundbuf );
+ if ( audio_size > 0 )
+ {
+ audio_chunk = AddDirectoryEntry( make_fourcc( "01wb" ), 0, audio_size, movi_list );
+ if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
+ {
+ GetDirectoryEntry( audio_chunk, type, name, length, offset, parent );
+ ix[ 1 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
+ }
+ WriteChunk( audio_chunk, soundbuf );
+ // num_blocks = (audio_size + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
+ // length = num_blocks * PADDING_SIZE - audio_size - 2 * RIFF_HEADERSIZE;
+ // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
+ // WriteChunk(junk_chunk, g_zeroes);
+ if ( index_type & AVI_LARGE_INDEX )
+ UpdateIndx( 1, audio_chunk, audio_size / waveformatex.nChannels / 2 );
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ UpdateIdx1( audio_chunk, 0x00 );
+ streamHdr[ 1 ].dwLength += audio_size / waveformatex.nChannels / 2;
+
+ }
+
+ /* Write video data */
+
+ frame_chunk = AddDirectoryEntry( make_fourcc( "00dc" ), 0, frame.GetFrameSize(), movi_list );
+ if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
+ {
+ GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
+ ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
+ }
+ WriteChunk( frame_chunk, frame.data );
+ // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
+ // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
+ // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
+ // WriteChunk(junk_chunk, g_zeroes);
+ if ( index_type & AVI_LARGE_INDEX )
+ UpdateIndx( 0, frame_chunk, 1 );
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ UpdateIdx1( frame_chunk, 0x10 );
+
+ /* update some variables with the new frame count. */
+
+ if ( isUpdateIdx1 )
+ ++mainHdr.dwTotalFrames;
+ ++streamHdr[ 0 ].dwLength;
+ ++dmlh[ 0 ];
+
+ /* Find out if the current riff list is close to 1 GByte in
+ size. If so, start a new (extended) RIFF. The only allowed
+ item in the new RIFF chunk is a movi list (with video
+ frames and indexes as usual). */
+
+ GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+ if ( length > 0x3f000000 )
+ {
+
+ /* write idx1 only once and before end of first GB */
+ if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+ {
+ int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+ WriteChunk( idx1_chunk, ( void* ) idx1 );
+ }
+ isUpdateIdx1 = false;
+
+ if ( index_type & AVI_LARGE_INDEX )
+ {
+ /* padding for alignment */
+ GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+ num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
+ length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
+ if ( length > 0 )
+ {
+ junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+ WriteChunk( junk_chunk, g_zeroes );
+ }
+
+ riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
+ movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+ }
+ }
+ return true;
+}
+#endif
+
+void AVI1File::setDVINFO( DVINFO &info )
+{
+ // do not do this until debugged audio against DirectShow
+ return ;
+
+ dvinfo.dwDVAAuxSrc = info.dwDVAAuxSrc;
+ dvinfo.dwDVAAuxCtl = info.dwDVAAuxCtl;
+ dvinfo.dwDVAAuxSrc1 = info.dwDVAAuxSrc1;
+ dvinfo.dwDVAAuxCtl1 = info.dwDVAAuxCtl1;
+ dvinfo.dwDVVAuxSrc = info.dwDVVAuxSrc;
+ dvinfo.dwDVVAuxCtl = info.dwDVVAuxCtl;
+}
+
+
+void AVI2File::setDVINFO( DVINFO &info )
+{}
+
+void AVIFile::setFccHandler( FOURCC type, FOURCC handler )
+{
+ for ( int i = 0; i < mainHdr.dwStreams; i++ )
+ {
+ if ( streamHdr[ i ].fccType == type )
+ {
+ int k, j = 0;
+ FOURCC strf = make_fourcc( "strf" );
+ BITMAPINFOHEADER bih;
+
+ streamHdr[ i ].fccHandler = handler;
+
+ while ( ( k = FindDirectoryEntry( strf, j++ ) ) != -1 )
+ {
+ ReadChunk( k, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
+ bih.biCompression = handler;
+ }
+ }
+ }
+}
+
+bool AVIFile::getStreamFormat( void* data, FOURCC type )
+{
+ int i, j = 0;
+ FOURCC strh = make_fourcc( "strh" );
+ FOURCC strf = make_fourcc( "strf" );
+ AVIStreamHeader avi_stream_header;
+ bool result = false;
+
+ while ( ( result == false ) && ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
+ {
+ ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
+ if ( avi_stream_header.fccType == type )
+ {
+ FOURCC chunkID;
+ int size;
+
+ pthread_mutex_lock( &file_mutex );
+ fail_neg( read( fd, &chunkID, sizeof( FOURCC ) ) );
+ if ( chunkID == strf )
+ {
+ fail_neg( read( fd, &size, sizeof( int ) ) );
+ fail_neg( read( fd, data, size ) );
+ result = true;
+ }
+ pthread_mutex_unlock( &file_mutex );
+ }
+ }
+ return result;
+}
--- /dev/null
+/*
+* avi.h library for AVI file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU 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.
+*
+* Tag: $Name$
+*
+* Change log:
+*
+* $Log$
+* Revision 1.4 2005/07/25 07:21:39 lilo_booter
+* + fixes for opendml dv avi
+*
+* Revision 1.3 2005/06/21 20:59:39 lilo_booter
+* src/framework/mlt_consumer.c src/framework/mlt_consumer.h
+* + Added a general profile handling for size, aspect ratio and display ratio
+*
+* src/framework/mlt_producer.c
+* + Correction to aspect ratio properties
+*
+* src/inigo/inigo.c
+* + Minimalist support for sdl_preview (still not very good)
+*
+* src/modules/avformat/consumer_avformat.c
+* + Takes consumer profile into account
+*
+* src/modules/core/filter_resize.c
+* + Corrections for synthesised producers and aspect ratio (inherits from consumer)
+*
+* src/modules/core/producer_colour.c
+* src/modules/core/producer_noise.c
+* src/modules/gtk2/producer_pango.c
+* + Ensures that resize picks up consumer aspect ratio
+*
+* src/modules/dv/consumer_libdv.c
+* + Honour wide screen output
+*
+* src/modules/gtk2/producer_pixbuf.c
+* + Correction for 1:1 aspect ratio
+*
+* src/modules/kino/Makefile
+* src/modules/kino/avi.cc
+* src/modules/kino/avi.h
+* src/modules/kino/configure
+* src/modules/kino/filehandler.cc
+* + Attempt to allow mov dv files to provide audio
+*
+* src/modules/sdl/consumer_sdl.c
+* src/modules/sdl/consumer_sdl_preview.c
+* src/modules/sdl/consumer_sdl_still.c
+* + Takes consumer profile into account
+*
+* Revision 1.2 2005/04/15 14:37:03 lilo_booter
+* Minor correction
+*
+* Revision 1.1 2005/04/15 14:28:26 lilo_booter
+* Initial version
+*
+* Revision 1.16 2005/04/01 23:43:10 ddennedy
+* apply endian fixes from Daniel Kobras
+*
+* Revision 1.15 2004/10/11 01:37:11 ddennedy
+* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
+*
+* Revision 1.14 2003/11/25 23:00:52 ddennedy
+* cleanup and a few bugfixes
+*
+* Revision 1.13 2003/10/21 16:34:32 ddennedy
+* GNOME2 port phase 1: initial checkin
+*
+* Revision 1.11.2.5 2003/07/24 14:13:57 ddennedy
+* support for distinct audio stream in type2 AVI and Quicktime; support for more DV FOURCCs
+*
+* Revision 1.11.2.4 2003/06/10 23:53:36 ddennedy
+* Daniel Kobras' WriteFrame error handling and automatic OpenDML, bugfixes in scene list updates, export AV/C Record
+*
+* Revision 1.11.2.3 2003/02/20 21:59:57 ddennedy
+* bugfixes to capture and AVI
+*
+* Revision 1.11.2.2 2003/01/13 05:15:31 ddennedy
+* added More Info panel and supporting methods
+*
+* Revision 1.11.2.1 2002/11/25 04:48:31 ddennedy
+* bugfix to report errors when loading files
+*
+* Revision 1.11 2002/10/08 07:46:41 ddennedy
+* AVI bugfixes, compatibility, optimization, warn bad file in capture and export dv file, allow no mplex
+*
+* Revision 1.10 2002/05/17 08:04:25 ddennedy
+* revert const-ness of Frame references in Frame, FileHandler, and AVI classes
+*
+* Revision 1.9 2002/05/15 04:39:35 ddennedy
+* bugfixes to dv2 AVI write, audio export, Xv init
+*
+* Revision 1.8 2002/04/29 05:09:22 ddennedy
+* raw dv file support, Frame::ExtractAudio uses libdv, audioScrub prefs
+*
+* Revision 1.7 2002/04/09 06:53:42 ddennedy
+* cleanup, new libdv 0.9.5, large AVI, dnd storyboard
+*
+* Revision 1.7 2002/03/25 21:34:25 arne
+* Support for large (64 bit) files mostly completed
+*
+* Revision 1.6 2002/03/10 13:29:41 arne
+* more changes for 64 bit access
+*
+* Revision 1.5 2002/03/09 17:59:28 arne
+* moved index routines to AVIFile
+*
+* Revision 1.4 2002/03/09 10:26:26 arne
+* improved constructors and assignment operator
+*
+* Revision 1.3 2002/03/09 08:55:57 arne
+* moved a few variables to AVIFile
+*
+* Revision 1.2 2002/03/04 19:22:43 arne
+* updated to latest Kino avi code
+*
+* Revision 1.1.1.1 2002/03/03 19:08:08 arne
+* import of version 1.01
+*
+*/
+
+/** Common AVI declarations
+
+ Some of this comes from the public domain AVI specification, which
+ explains the microsoft-style definitions.
+
+ \file avi.h
+*/
+
+#ifndef _AVI_H
+#define _AVI_H 1
+
+#include <stdint.h>
+#include "riff.h"
+
+#define PACKED(x) __attribute__((packed)) x
+
+#define AVI_SMALL_INDEX (0x01)
+#define AVI_LARGE_INDEX (0x02)
+#define KINO_AVI_INDEX_OF_INDEXES (0x00)
+#define KINO_AVI_INDEX_OF_CHUNKS (0x01)
+#define AVI_INDEX_2FIELD (0x01)
+
+enum { AVI_PAL, AVI_NTSC, AVI_AUDIO_48KHZ, AVI_AUDIO_44KHZ, AVI_AUDIO_32KHZ };
+
+/** Declarations of the main AVI file header
+
+ The contents of this struct goes into the 'avih' chunk. */
+
+typedef struct
+{
+ /// frame display rate (or 0L)
+ DWORD dwMicroSecPerFrame;
+
+ /// max. transfer rate
+ DWORD dwMaxBytesPerSec;
+
+ /// pad to multiples of this size, normally 2K
+ DWORD dwPaddingGranularity;
+
+ /// the ever-present flags
+ DWORD dwFlags;
+
+ /// # frames in file
+ DWORD dwTotalFrames;
+ DWORD dwInitialFrames;
+ DWORD dwStreams;
+ DWORD dwSuggestedBufferSize;
+
+ DWORD dwWidth;
+ DWORD dwHeight;
+
+ DWORD dwReserved[ 4 ];
+}
+PACKED(MainAVIHeader);
+
+typedef struct
+{
+ WORD top, bottom, left, right;
+}
+PACKED(RECT);
+
+/** Declaration of a stream header
+
+ The contents of this struct goes into the 'strh' header. */
+
+typedef struct
+{
+ FOURCC fccType;
+ FOURCC fccHandler;
+ DWORD dwFlags; /* Contains AVITF_* flags */
+ WORD wPriority;
+ WORD wLanguage;
+ DWORD dwInitialFrames;
+ DWORD dwScale;
+ DWORD dwRate; /* dwRate / dwScale == samples/second */
+ DWORD dwStart;
+ DWORD dwLength; /* In units above... */
+ DWORD dwSuggestedBufferSize;
+ DWORD dwQuality;
+ DWORD dwSampleSize;
+ RECT rcFrame;
+}
+PACKED(AVIStreamHeader);
+
+typedef struct
+{
+ DWORD dwDVAAuxSrc;
+ DWORD dwDVAAuxCtl;
+ DWORD dwDVAAuxSrc1;
+ DWORD dwDVAAuxCtl1;
+ DWORD dwDVVAuxSrc;
+ DWORD dwDVVAuxCtl;
+ DWORD dwDVReserved[ 2 ];
+}
+PACKED(DVINFO);
+
+typedef struct
+{
+ DWORD biSize;
+ LONG biWidth;
+ LONG biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+ DWORD biCompression;
+ DWORD biSizeImage;
+ LONG biXPelsPerMeter;
+ LONG biYPelsPerMeter;
+ DWORD biClrUsed;
+ DWORD biClrImportant;
+ char dummy[ 1040 ];
+}
+PACKED(BITMAPINFOHEADER);
+
+typedef struct
+{
+ WORD wFormatTag;
+ WORD nChannels;
+ DWORD nSamplesPerSec;
+ DWORD nAvgBytesPerSec;
+ WORD nBlockAlign;
+ WORD wBitsPerSample;
+ WORD cbSize;
+ WORD dummy;
+}
+PACKED(WAVEFORMATEX);
+
+typedef struct
+{
+ WORD wLongsPerEntry;
+ BYTE bIndexSubType;
+ BYTE bIndexType;
+ DWORD nEntriesInUse;
+ FOURCC dwChunkId;
+ DWORD dwReserved[ 3 ];
+ struct avisuperindex_entry
+ {
+ QUADWORD qwOffset;
+ DWORD dwSize;
+ DWORD dwDuration;
+ }
+ aIndex[ 3198 ];
+}
+PACKED(AVISuperIndex);
+
+typedef struct
+{
+ WORD wLongsPerEntry;
+ BYTE bIndexSubType;
+ BYTE bIndexType;
+ DWORD nEntriesInUse;
+ FOURCC dwChunkId;
+ QUADWORD qwBaseOffset;
+ DWORD dwReserved;
+ struct avifieldindex_entry
+ {
+ DWORD dwOffset;
+ DWORD dwSize;
+ }
+ aIndex[ 17895 ];
+}
+PACKED(AVIStdIndex);
+
+typedef struct
+{
+ struct avisimpleindex_entry
+ {
+ FOURCC dwChunkId;
+ DWORD dwFlags;
+ DWORD dwOffset;
+ DWORD dwSize;
+ }
+ aIndex[ 20000 ];
+ DWORD nEntriesInUse;
+}
+PACKED(AVISimpleIndex);
+
+typedef struct
+{
+ DWORD dirEntryType;
+ DWORD dirEntryName;
+ DWORD dirEntryLength;
+ size_t dirEntryOffset;
+ int dirEntryWrittenFlag;
+ int dirEntryParentList;
+}
+AviDirEntry;
+
+
+/** base class for all AVI type files
+
+ It contains methods and members which are the same in all AVI type files regardless of the particular compression, number
+ of streams etc.
+
+ The AVIFile class also contains methods for handling several indexes to the video frame content. */
+
+class AVIFile : public RIFFFile
+{
+public:
+ AVIFile();
+ AVIFile( const AVIFile& );
+ virtual ~AVIFile();
+ virtual AVIFile& operator=( const AVIFile& );
+
+ virtual void Init( int format, int sampleFrequency, int indexType );
+ virtual int GetDVFrameInfo( off_t &offset, int &size, int frameNum );
+ virtual int GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID );
+ virtual int GetDVFrame( uint8_t *data, int frameNum );
+ virtual int getFrame( void *data, int frameNum, FOURCC chunkID );
+ virtual int GetTotalFrames() const;
+ virtual void PrintDirectoryEntryData( const RIFFDirEntry &entry ) const;
+ //virtual bool WriteFrame( const Frame &frame ) { return false; }
+ virtual void ParseList( int parent );
+ virtual void ParseRIFF( void );
+ virtual void ReadIndex( void );
+ virtual void WriteRIFF( void )
+ { }
+ virtual void FlushIndx( int stream );
+ virtual void UpdateIndx( int stream, int chunk, int duration );
+ virtual void UpdateIdx1( int chunk, int flags );
+ virtual bool verifyStreamFormat( FOURCC type );
+ virtual bool verifyStream( FOURCC type );
+ virtual bool isOpenDML( void );
+ virtual void setDVINFO( DVINFO& )
+ { }
+ virtual void setFccHandler( FOURCC type, FOURCC handler );
+ virtual bool getStreamFormat( void* data, FOURCC type );
+
+protected:
+ MainAVIHeader mainHdr;
+ AVISimpleIndex *idx1;
+ int file_list;
+ int riff_list;
+ int hdrl_list;
+ int avih_chunk;
+ int movi_list;
+ int junk_chunk;
+ int idx1_chunk;
+
+ AVIStreamHeader streamHdr[ 2 ];
+ AVISuperIndex *indx[ 2 ];
+ AVIStdIndex *ix[ 2 ];
+ int indx_chunk[ 2 ];
+ int ix_chunk[ 2 ];
+ int strl_list[ 2 ];
+ int strh_chunk[ 2 ];
+ int strf_chunk[ 2 ];
+
+ int index_type;
+ int current_ix00;
+
+ DWORD dmlh[ 62 ];
+ int odml_list;
+ int dmlh_chunk;
+ bool isUpdateIdx1;
+
+};
+
+
+/** writing Type 1 DV AVIs
+
+*/
+
+class AVI1File : public AVIFile
+{
+public:
+ AVI1File();
+ virtual ~AVI1File();
+
+ virtual void Init( int format, int sampleFrequency, int indexType );
+ //virtual bool WriteFrame( const Frame &frame );
+ virtual void WriteRIFF( void );
+ virtual void setDVINFO( DVINFO& );
+
+private:
+ DVINFO dvinfo;
+
+ AVI1File( const AVI1File& );
+ AVI1File& operator=( const AVI1File& );
+};
+
+
+/** writing Type 2 (separate audio data) DV AVIs
+
+This file type contains both audio and video tracks. It is therefore more compatible
+to certain Windows programs, which expect any AVI having both audio and video tracks.
+The video tracks contain the raw DV data (as in type 1) and the extracted audio tracks.
+
+Note that because the DV data contains audio information anyway, this means duplication
+of data and a slight increase of file size.
+
+*/
+
+class AVI2File : public AVIFile
+{
+public:
+ AVI2File();
+ virtual ~AVI2File();
+
+ virtual void Init( int format, int sampleFrequency, int indexType );
+ //virtual bool WriteFrame( const Frame &frame );
+ virtual void WriteRIFF( void );
+ virtual void setDVINFO( DVINFO& );
+
+private:
+ BITMAPINFOHEADER bitmapinfo;
+ WAVEFORMATEX waveformatex;
+
+ AVI2File( const AVI2File& );
+ AVI2File& operator=( const AVI2File& );
+};
+#endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ if [ "$targetos" = "Darwin" ]
+ then
+ echo "- does not build on Darwin: disabling"
+ touch ../disable-kino
+ exit 0
+ fi
+
+ # Entirely optional...
+ pkg-config libquicktime 2> /dev/null
+ lqt_disabled=$?
+
+ pkg-config libdv 2> /dev/null
+ libdv_disabled=$?
+
+ echo > config.h
+ [ "$lqt_disabled" = "0" ] && echo "#define HAVE_LIBQUICKTIME" >> config.h
+ [ "$libdv_disabled" = "0" ] && echo "#define HAVE_LIBDV" >> config.h
+ echo > config.mak
+ [ "$lqt_disabled" = "0" ] && echo "HAVE_LIBQUICKTIME=1" >> config.mak
+ [ "$libdv_disabled" = "0" ] && echo "HAVE_LIBDV=1" >> config.mak
+
+ [ "$lqt_disabled" != "0" ] && echo "- libquicktime not found: only enabling dv avi support"
+ [ "$libdv_disabled" != "0" -a "$lqt_disabled" = "0" ] && echo "- libdv not found: mov dv may not have audio"
+
+ exit 0
+fi
--- /dev/null
+/* <endian_types.h>
+ *
+ * Quick hack to handle endianness and word length issues.
+ * Defines _le, _be, and _ne variants to standard ISO types
+ * like int32_t, that are stored in little-endian, big-endian,
+ * and native-endian byteorder in memory, respectively.
+ * Caveat: int32_le_t and friends cannot be used in vararg
+ * functions like printf() without an explicit cast.
+ *
+ * Copyright (c) 2003-2005 Daniel Kobras <kobras@debian.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ENDIAN_TYPES_H
+#define _ENDIAN_TYPES_H
+
+/* Needed for BYTE_ORDER and BIG/LITTLE_ENDIAN macros. */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE
+#ifndef __FreeBSD__
+# include <endian.h>
+#else
+# include <sys/endian.h>
+#endif /* __FreeBSD__ */
+# undef _BSD_SOURCE
+#else
+#ifndef __FreeBSD__
+# include <endian.h>
+#else
+# include <sys/endian.h>
+#endif /* __FreeBSD__ */
+#endif
+
+#include <sys/types.h>
+#ifndef __FreeBSD__
+#include <byteswap.h>
+#else
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif /* __FreeBSD__ */
+
+static inline int8_t bswap(const int8_t& x)
+{
+ return x;
+}
+
+static inline u_int8_t bswap(const u_int8_t& x)
+{
+ return x;
+}
+
+static inline int16_t bswap(const int16_t& x)
+{
+ return bswap_16(x);
+}
+
+static inline u_int16_t bswap(const u_int16_t& x)
+{
+ return bswap_16(x);
+}
+
+static inline int32_t bswap(const int32_t& x)
+{
+ return bswap_32(x);
+}
+
+static inline u_int32_t bswap(const u_int32_t& x)
+{
+ return bswap_32(x);
+}
+
+static inline int64_t bswap(const int64_t& x)
+{
+ return bswap_64(x);
+}
+
+static inline u_int64_t bswap(const u_int64_t& x)
+{
+ return bswap_64(x);
+}
+
+#define le_to_cpu cpu_to_le
+#define be_to_cpu cpu_to_be
+
+template <class T> static inline T cpu_to_le(const T& x)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return x;
+#else
+ return bswap(x);
+#endif
+}
+
+template <class T> static inline T cpu_to_be(const T& x)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return bswap(x);
+#else
+ return x;
+#endif
+}
+
+template <class T> class le_t {
+ T m;
+ T read() const {
+ return le_to_cpu(m);
+ };
+ void write(const T& n) {
+ m = cpu_to_le(n);
+ };
+public:
+ le_t(void) {
+ m = 0;
+ };
+ le_t(const T& o) {
+ write(o);
+ };
+ operator T() const {
+ return read();
+ };
+ le_t<T> operator++() {
+ write(read() + 1);
+ return *this;
+ };
+ le_t<T> operator++(int) {
+ write(read() + 1);
+ return *this;
+ };
+ le_t<T> operator--() {
+ write(read() - 1);
+ return *this;
+ };
+ le_t<T> operator--(int) {
+ write(read() - 1);
+ return *this;
+ };
+ le_t<T>& operator+=(const T& t) {
+ write(read() + t);
+ return *this;
+ };
+ le_t<T>& operator-=(const T& t) {
+ write(read() - t);
+ return *this;
+ };
+ le_t<T>& operator&=(const le_t<T>& t) {
+ m &= t.m;
+ return *this;
+ };
+ le_t<T>& operator|=(const le_t<T>& t) {
+ m |= t.m;
+ return *this;
+ };
+} __attribute__((packed));
+
+/* Just copy-and-pasted from le_t. Too lazy to do it right. */
+
+template <class T> class be_t {
+ T m;
+ T read() const {
+ return be_to_cpu(m);
+ };
+ void write(const T& n) {
+ m = cpu_to_be(n);
+ };
+public:
+ be_t(void) {
+ m = 0;
+ };
+ be_t(const T& o) {
+ write(o);
+ };
+ operator T() const {
+ return read();
+ };
+ be_t<T> operator++() {
+ write(read() + 1);
+ return *this;
+ };
+ be_t<T> operator++(int) {
+ write(read() + 1);
+ return *this;
+ };
+ be_t<T> operator--() {
+ write(read() - 1);
+ return *this;
+ };
+ be_t<T> operator--(int) {
+ write(read() - 1);
+ return *this;
+ };
+ be_t<T>& operator+=(const T& t) {
+ write(read() + t);
+ return *this;
+ };
+ be_t<T>& operator-=(const T& t) {
+ write(read() - t);
+ return *this;
+ };
+ be_t<T>& operator&=(const be_t<T>& t) {
+ m &= t.m;
+ return *this;
+ };
+ be_t<T>& operator|=(const be_t<T>& t) {
+ m |= t.m;
+ return *this;
+ };
+} __attribute__((packed));
+
+/* Define types of native endianness similar to the little and big endian
+ * versions below. Not really necessary but useful occasionally to emphasize
+ * endianness of data.
+ */
+
+typedef int8_t int8_ne_t;
+typedef int16_t int16_ne_t;
+typedef int32_t int32_ne_t;
+typedef int64_t int64_ne_t;
+typedef u_int8_t u_int8_ne_t;
+typedef u_int16_t u_int16_ne_t;
+typedef u_int32_t u_int32_ne_t;
+typedef u_int64_t u_int64_ne_t;
+
+
+/* The classes work on their native endianness as well, but obviously
+ * introduce some overhead. Use the faster typedefs to native types
+ * therefore, unless you're debugging.
+ */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+typedef int8_ne_t int8_le_t;
+typedef int16_ne_t int16_le_t;
+typedef int32_ne_t int32_le_t;
+typedef int64_ne_t int64_le_t;
+typedef u_int8_ne_t u_int8_le_t;
+typedef u_int16_ne_t u_int16_le_t;
+typedef u_int32_ne_t u_int32_le_t;
+typedef u_int64_ne_t u_int64_le_t;
+typedef int8_t int8_be_t;
+typedef be_t<int16_t> int16_be_t;
+typedef be_t<int32_t> int32_be_t;
+typedef be_t<int64_t> int64_be_t;
+typedef u_int8_t u_int8_be_t;
+typedef be_t<u_int16_t> u_int16_be_t;
+typedef be_t<u_int32_t> u_int32_be_t;
+typedef be_t<u_int64_t> u_int64_be_t;
+#else
+typedef int8_ne_t int8_be_t;
+typedef int16_ne_t int16_be_t;
+typedef int32_ne_t int32_be_t;
+typedef int64_ne_t int64_be_t;
+typedef u_int8_ne_t u_int8_be_t;
+typedef u_int16_ne_t u_int16_be_t;
+typedef u_int32_ne_t u_int32_be_t;
+typedef u_int64_ne_t u_int64_be_t;
+typedef int8_t int8_le_t;
+typedef le_t<int16_t> int16_le_t;
+typedef le_t<int32_t> int32_le_t;
+typedef le_t<int64_t> int64_le_t;
+typedef u_int8_t u_int8_le_t;
+typedef le_t<u_int16_t> u_int16_le_t;
+typedef le_t<u_int32_t> u_int32_le_t;
+typedef le_t<u_int64_t> u_int64_le_t;
+#endif
+
+#endif
--- /dev/null
+/*
+* error.cc Error handling
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// C++ includes
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+using std::ostringstream;
+using std::string;
+using std::endl;
+using std::ends;
+using std::cerr;
+
+// C includes
+
+#include <errno.h>
+#include <string.h>
+
+// local includes
+
+#include "error.h"
+
+void real_fail_neg( int eval, const char *eval_str, const char *func, const char *file, int line )
+{
+ if ( eval < 0 )
+ {
+ string exc;
+ ostringstream sb;
+
+ sb << file << ":" << line << ": In function \"" << func << "\": \"" << eval_str << "\" evaluated to " << eval;
+ if ( errno != 0 )
+ sb << endl << file << ":" << line << ": errno: " << errno << " (" << strerror( errno ) << ")";
+ sb << ends;
+ exc = sb.str();
+ cerr << exc << endl;
+ throw exc;
+ }
+}
+
+
+/** error handler for NULL result codes
+
+ Whenever this is called with a NULL argument, it will throw an
+ exception. Typically used with functions like malloc() and new().
+
+*/
+
+void real_fail_null( const void *eval, const char *eval_str, const char *func, const char *file, int line )
+{
+ if ( eval == NULL )
+ {
+
+ string exc;
+ ostringstream sb;
+
+ sb << file << ":" << line << ": In function \"" << func << "\": " << eval_str << " is NULL" << ends;
+ exc = sb.str();
+ cerr << exc << endl;
+ throw exc;
+ }
+}
+
+
+void real_fail_if( bool eval, const char *eval_str, const char *func, const char *file, int line )
+{
+ if ( eval == true )
+ {
+
+ string exc;
+ ostringstream sb;
+
+ sb << file << ":" << line << ": In function \"" << func << "\": condition \"" << eval_str << "\" is true";
+ if ( errno != 0 )
+ sb << endl << file << ":" << line << ": errno: " << errno << " (" << strerror( errno ) << ")";
+ sb << ends;
+ exc = sb.str();
+ cerr << exc << endl;
+ throw exc;
+ }
+}
--- /dev/null
+/*
+* error.h Error handling
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _ERROR_H
+#define _ERROR_H 1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /*
+ * Should check for gcc/g++ and version > 2.6 I suppose
+ */
+#ifndef __ASSERT_FUNCTION
+# define __ASSERT_FUNCTION __PRETTY_FUNCTION__
+#endif
+
+#define fail_neg(eval) real_fail_neg (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__)
+#define fail_null(eval) real_fail_null (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__)
+#define fail_if(eval) real_fail_if (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__)
+
+ void real_fail_neg ( int eval, const char * eval_str, const char * func, const char * file, int line );
+ void real_fail_null ( const void * eval, const char * eval_str, const char * func, const char * file, int line );
+ void real_fail_if ( bool eval, const char * eval_str, const char * func, const char * file, int line );
+
+ extern void sigpipe_clear( );
+ extern int sigpipe_get( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_kino_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( producer_type, "kino", producer_kino_init );
+}
--- /dev/null
+/*
+* filehandler.cc -- saving DV data into different file formats
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU 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.
+*/
+
+#include "config.h"
+
+extern "C" {
+#include <framework/mlt_frame.h>
+}
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+using std::cerr;
+using std::endl;
+using std::ostringstream;
+using std::setw;
+using std::setfill;
+
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+
+// libdv header files
+#ifdef HAVE_LIBDV
+#include <libdv/dv.h>
+#endif
+
+#include "filehandler.h"
+#include "error.h"
+#include "riff.h"
+#include "avi.h"
+
+FileTracker *FileTracker::instance = NULL;
+
+FileTracker::FileTracker( ) : mode( CAPTURE_MOVIE_APPEND )
+{
+ cerr << ">> Constructing File Capture tracker" << endl;
+}
+
+FileTracker::~FileTracker( )
+{
+ cerr << ">> Destroying File Capture tracker" << endl;
+}
+
+FileTracker &FileTracker::GetInstance( )
+{
+ if ( instance == NULL )
+ instance = new FileTracker();
+
+ return *instance;
+}
+
+void FileTracker::SetMode( FileCaptureMode mode )
+{
+ this->mode = mode;
+}
+
+FileCaptureMode FileTracker::GetMode( )
+{
+ return this->mode;
+}
+
+char *FileTracker::Get( int index )
+{
+ return list[ index ];
+}
+
+void FileTracker::Add( const char *file )
+{
+ if ( this->mode != CAPTURE_IGNORE )
+ {
+ cerr << ">>>> Registering " << file << " with the tracker" << endl;
+ list.push_back( strdup( file ) );
+ }
+}
+
+unsigned int FileTracker::Size( )
+{
+ return list.size();
+}
+
+void FileTracker::Clear( )
+{
+ while ( Size() > 0 )
+ {
+ free( list[ Size() - 1 ] );
+ list.pop_back( );
+ }
+ this->mode = CAPTURE_MOVIE_APPEND;
+}
+
+FileHandler::FileHandler() : done( false ), autoSplit( false ), maxFrameCount( 999999 ),
+ framesWritten( 0 ), filename( "" )
+{
+ /* empty body */
+}
+
+
+FileHandler::~FileHandler()
+{
+ /* empty body */
+}
+
+
+bool FileHandler::GetAutoSplit() const
+{
+ return autoSplit;
+}
+
+
+bool FileHandler::GetTimeStamp() const
+{
+ return timeStamp;
+}
+
+
+string FileHandler::GetBaseName() const
+{
+ return base;
+}
+
+
+string FileHandler::GetExtension() const
+{
+ return extension;
+}
+
+
+int FileHandler::GetMaxFrameCount() const
+{
+ return maxFrameCount;
+}
+
+off_t FileHandler::GetMaxFileSize() const
+{
+ return maxFileSize;
+}
+
+string FileHandler::GetFilename() const
+{
+ return filename;
+}
+
+
+void FileHandler::SetAutoSplit( bool flag )
+{
+ autoSplit = flag;
+}
+
+
+void FileHandler::SetTimeStamp( bool flag )
+{
+ timeStamp = flag;
+}
+
+
+void FileHandler::SetBaseName( const string& s )
+{
+ base = s;
+}
+
+
+void FileHandler::SetMaxFrameCount( int count )
+{
+ assert( count >= 0 );
+ maxFrameCount = count;
+}
+
+
+void FileHandler::SetEveryNthFrame( int every )
+{
+ assert ( every > 0 );
+
+ everyNthFrame = every;
+}
+
+
+void FileHandler::SetMaxFileSize( off_t size )
+{
+ assert ( size >= 0 );
+ maxFileSize = size;
+}
+
+
+#if 0
+void FileHandler::SetSampleFrame( const Frame& sample )
+{
+ /* empty body */
+}
+#endif
+
+bool FileHandler::Done()
+{
+ return done;
+}
+
+#if 0
+bool FileHandler::WriteFrame( const Frame& frame )
+{
+ static TimeCode prevTimeCode;
+ TimeCode timeCode;
+
+ /* If the user wants autosplit, start a new file if a
+ new recording is detected. */
+ prevTimeCode.sec = -1;
+ frame.GetTimeCode( timeCode );
+ int time_diff = timeCode.sec - prevTimeCode.sec;
+ bool discontinuity = prevTimeCode.sec != -1 && ( time_diff > 1 || ( time_diff < 0 && time_diff > -59 ) );
+ if ( FileIsOpen() && GetAutoSplit() == true && ( frame.IsNewRecording() || discontinuity ) )
+ {
+ Close();
+ }
+
+ if ( FileIsOpen() == false )
+ {
+
+ string filename;
+ static int counter = 0;
+
+ if ( GetTimeStamp() == true )
+ {
+ ostringstream sb, sb2;
+ struct tm date;
+ string recDate;
+
+ if ( ! frame.GetRecordingDate( date ) )
+ {
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday( &tv, &tz );
+ localtime_r( static_cast< const time_t * >( &tv.tv_sec ), &date );
+ }
+ sb << setfill( '0' )
+ << setw( 4 ) << date.tm_year + 1900 << '.'
+ << setw( 2 ) << date.tm_mon + 1 << '.'
+ << setw( 2 ) << date.tm_mday << '_'
+ << setw( 2 ) << date.tm_hour << '-'
+ << setw( 2 ) << date.tm_min << '-'
+ << setw( 2 ) << date.tm_sec;
+ recDate = sb.str();
+ sb2 << GetBaseName() << recDate << GetExtension();
+ filename = sb2.str();
+ cerr << ">>> Trying " << filename << endl;
+ }
+ else
+ {
+ struct stat stats;
+ do
+ {
+ ostringstream sb;
+ sb << GetBaseName() << setfill( '0' ) << setw( 3 ) << ++ counter << GetExtension();
+ filename = sb.str();
+ cerr << ">>> Trying " << filename << endl;
+ }
+ while ( stat( filename.c_str(), &stats ) == 0 );
+ }
+
+ SetSampleFrame( frame );
+ if ( Create( filename ) == false )
+ {
+ cerr << ">>> Error creating file!" << endl;
+ return false;
+ }
+ framesWritten = 0;
+ framesToSkip = 0;
+ }
+
+ /* write frame */
+
+ if ( framesToSkip == 0 )
+ {
+ if ( 0 > Write( frame ) )
+ {
+ cerr << ">>> Error writing frame!" << endl;
+ return false;
+ }
+ framesToSkip = everyNthFrame;
+ ++framesWritten;
+ }
+ framesToSkip--;
+
+ /* If the frame count is exceeded, close the current file.
+ If the autosplit flag is set, a new file will be created in the next iteration.
+ If the flag is not set, we are done. */
+
+ if ( ( GetMaxFrameCount() > 0 ) &&
+ ( framesWritten >= GetMaxFrameCount() ) )
+ {
+ Close();
+ done = !GetAutoSplit();
+ }
+
+ /* If the file size could be exceeded by another frame, close the current file.
+ If the autosplit flag is set, a new file will be created on the next iteration.
+ If the flag is not set, we are done. */
+ /* not exact, but should be good enough to prevent going over. */
+ if ( FileIsOpen() )
+ {
+ AudioInfo info;
+ frame.GetAudioInfo( info );
+ if ( ( GetFileSize() > 0 ) && ( GetMaxFileSize() > 0 ) &&
+ ( GetFileSize() + frame.GetFrameSize() + info.samples * 4 + 12 )
+ >= GetMaxFileSize() )
+ { // 12 = sizeof chunk metadata
+ Close();
+ done = !GetAutoSplit();
+ }
+ }
+ prevTimeCode.sec = timeCode.sec;
+ return true;
+}
+#endif
+
+RawHandler::RawHandler() : fd( -1 )
+{
+ extension = ".dv";
+}
+
+
+RawHandler::~RawHandler()
+{
+ Close();
+}
+
+
+bool RawHandler::FileIsOpen()
+{
+ return fd != -1;
+}
+
+
+bool RawHandler::Create( const string& filename )
+{
+ fd = open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 );
+ if ( fd != -1 )
+ {
+ FileTracker::GetInstance().Add( filename.c_str() );
+ this->filename = filename;
+ }
+ return ( fd != -1 );
+}
+
+
+#if 0
+int RawHandler::Write( const Frame& frame )
+{
+ int result = write( fd, frame.data, frame.GetFrameSize() );
+ return result;
+}
+#endif
+
+int RawHandler::Close()
+{
+ if ( fd != -1 )
+ {
+ close( fd );
+ fd = -1;
+ }
+ return 0;
+}
+
+
+off_t RawHandler::GetFileSize()
+{
+ struct stat file_status;
+ fstat( fd, &file_status );
+ return file_status.st_size;
+}
+
+int RawHandler::GetTotalFrames()
+{
+ return GetFileSize() / ( 480 * numBlocks );
+}
+
+
+bool RawHandler::Open( const char *s )
+{
+ unsigned char data[ 4 ];
+ assert( fd == -1 );
+ fd = open( s, O_RDONLY | O_NONBLOCK );
+ if ( fd < 0 )
+ return false;
+ if ( read( fd, data, 4 ) < 0 )
+ return false;
+ lseek( fd, 0, SEEK_SET );
+ numBlocks = ( ( data[ 3 ] & 0x80 ) == 0 ) ? 250 : 300;
+ filename = s;
+ return true;
+
+}
+
+int RawHandler::GetFrame( uint8_t *data, int frameNum )
+{
+ assert( fd != -1 );
+ int size = 480 * numBlocks;
+ if ( frameNum < 0 )
+ return -1;
+ off_t offset = ( ( off_t ) frameNum * ( off_t ) size );
+ fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+ if ( read( fd, data, size ) > 0 )
+ return 0;
+ else
+ return -1;
+}
+
+
+/***************************************************************************/
+
+
+AVIHandler::AVIHandler( int format ) : avi( NULL ), aviFormat( format ), isOpenDML( false ),
+ fccHandler( make_fourcc( "dvsd" ) ), channels( 2 ), isFullyInitialized( false ),
+ audioBuffer( NULL )
+{
+ extension = ".avi";
+ for ( int c = 0; c < 4; c++ )
+ audioChannels[ c ] = NULL;
+}
+
+
+AVIHandler::~AVIHandler()
+{
+ if ( audioBuffer != NULL )
+ {
+ delete audioBuffer;
+ audioBuffer = NULL;
+ }
+ for ( int c = 0; c < 4; c++ )
+ {
+ if ( audioChannels[ c ] != NULL )
+ {
+ delete audioChannels[ c ];
+ audioChannels[ c ] = NULL;
+ }
+ }
+
+ delete avi;
+}
+
+#if 0
+void AVIHandler::SetSampleFrame( const Frame& sample )
+{
+ Pack pack;
+ sample.GetAudioInfo( audioInfo );
+ sample.GetVideoInfo( videoInfo );
+
+ sample.GetAAUXPack( 0x50, pack );
+ dvinfo.dwDVAAuxSrc = *( DWORD* ) ( pack.data + 1 );
+ sample.GetAAUXPack( 0x51, pack );
+ dvinfo.dwDVAAuxCtl = *( DWORD* ) ( pack.data + 1 );
+
+ sample.GetAAUXPack( 0x52, pack );
+ dvinfo.dwDVAAuxSrc1 = *( DWORD* ) ( pack.data + 1 );
+ sample.GetAAUXPack( 0x53, pack );
+ dvinfo.dwDVAAuxCtl1 = *( DWORD* ) ( pack.data + 1 );
+
+ sample.GetVAUXPack( 0x60, pack );
+ dvinfo.dwDVVAuxSrc = *( DWORD* ) ( pack.data + 1 );
+ sample.GetVAUXPack( 0x61, pack );
+ dvinfo.dwDVVAuxCtl = *( DWORD* ) ( pack.data + 1 );
+
+#ifdef WITH_LIBDV
+
+ if ( sample.decoder->std == e_dv_std_smpte_314m )
+ fccHandler = make_fourcc( "dv25" );
+#endif
+}
+#endif
+
+bool AVIHandler::FileIsOpen()
+{
+ return avi != NULL;
+}
+
+
+bool AVIHandler::Create( const string& filename )
+{
+ assert( avi == NULL );
+
+ switch ( aviFormat )
+ {
+
+ case AVI_DV1_FORMAT:
+ fail_null( avi = new AVI1File );
+ if ( avi->Create( filename.c_str() ) == false )
+ return false;
+ //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency, AVI_LARGE_INDEX );
+ break;
+
+ case AVI_DV2_FORMAT:
+ fail_null( avi = new AVI2File );
+ if ( avi->Create( filename.c_str() ) == false )
+ return false;
+ //if ( GetOpenDML() )
+ //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
+ //( AVI_SMALL_INDEX | AVI_LARGE_INDEX ) );
+ //else
+ //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
+ //( AVI_SMALL_INDEX ) );
+ break;
+
+ default:
+ assert( aviFormat == AVI_DV1_FORMAT || aviFormat == AVI_DV2_FORMAT );
+ }
+
+ avi->setDVINFO( dvinfo );
+ avi->setFccHandler( make_fourcc( "iavs" ), fccHandler );
+ avi->setFccHandler( make_fourcc( "vids" ), fccHandler );
+ this->filename = filename;
+ FileTracker::GetInstance().Add( filename.c_str() );
+ return ( avi != NULL );
+}
+
+#if 0
+int AVIHandler::Write( const Frame& frame )
+{
+ assert( avi != NULL );
+ try
+ {
+ return avi->WriteFrame( frame ) ? 0 : -1;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+}
+#endif
+
+int AVIHandler::Close()
+{
+ if ( avi != NULL )
+ {
+ avi->WriteRIFF();
+ delete avi;
+ avi = NULL;
+ }
+ if ( audioBuffer != NULL )
+ {
+ delete audioBuffer;
+ audioBuffer = NULL;
+ }
+ for ( int c = 0; c < 4; c++ )
+ {
+ if ( audioChannels[ c ] != NULL )
+ {
+ delete audioChannels[ c ];
+ audioChannels[ c ] = NULL;
+ }
+ }
+ isFullyInitialized = false;
+ return 0;
+}
+
+off_t AVIHandler::GetFileSize()
+{
+ return avi->GetFileSize();
+}
+
+int AVIHandler::GetTotalFrames()
+{
+ return avi->GetTotalFrames();
+}
+
+
+bool AVIHandler::Open( const char *s )
+{
+ assert( avi == NULL );
+ fail_null( avi = new AVI1File );
+ if ( avi->Open( s ) )
+ {
+ avi->ParseRIFF();
+ if ( ! (
+ avi->verifyStreamFormat( make_fourcc( "dvsd" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "DVSD" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "dvcs" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "DVCS" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "dvcp" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "DVCP" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "CDVC" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "cdvc" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "DV25" ) ) ||
+ avi->verifyStreamFormat( make_fourcc( "dv25" ) ) ) )
+ return false;
+ avi->ReadIndex();
+ if ( avi->verifyStream( make_fourcc( "auds" ) ) )
+ aviFormat = AVI_DV2_FORMAT;
+ else
+ aviFormat = AVI_DV1_FORMAT;
+ isOpenDML = avi->isOpenDML();
+ filename = s;
+ return true;
+ }
+ else
+ return false;
+
+}
+
+int AVIHandler::GetFrame( uint8_t *data, int frameNum )
+{
+ int result = avi->GetDVFrame( data, frameNum );
+#if 0
+ if ( result == 0 )
+ {
+ /* get the audio from the audio stream, if available */
+ if ( aviFormat == AVI_DV2_FORMAT )
+ {
+ WAVEFORMATEX wav;
+
+ if ( ! isFullyInitialized &&
+ avi->getStreamFormat( ( void* ) &wav, make_fourcc( "auds" ) ) )
+ {
+ if ( channels > 0 && channels < 5 )
+ {
+ // Allocate interleaved audio buffer
+ audioBuffer = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES * channels ];
+
+ // Allocate non-interleaved audio buffers
+ for ( int c = 0; c < channels; c++ )
+ audioChannels[ c ] = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES ];
+
+ // Get the audio parameters from AVI for subsequent calls to method
+ audioInfo.channels = wav.nChannels;
+ audioInfo.frequency = wav.nSamplesPerSec;
+
+ // Skip initialization on subsequent calls to method
+ isFullyInitialized = true;
+ cerr << ">>> using audio from separate AVI audio stream" << endl;
+ }
+ }
+
+ // Get the frame from AVI
+ int n = avi->getFrame( audioBuffer, frameNum, make_fourcc( "01wb" ) );
+ if ( n > 0 )
+ {
+ // Temporary pointer to audio scratch buffer
+ int16_t * s = audioBuffer;
+
+ // Determine samples in this frame
+ audioInfo.samples = n / audioInfo.channels / sizeof( int16_t );
+
+ // Convert interleaved audio into non-interleaved
+ for ( int n = 0; n < audioInfo.samples; ++n )
+ for ( int i = 0; i < audioInfo.channels; i++ )
+ audioChannels[ i ][ n ] = *s++;
+
+ // Write interleaved audio into frame
+ frame.EncodeAudio( audioInfo, audioChannels );
+ }
+ }
+
+ // Parse important metadata in DV bitstream
+ frame.ExtractHeader();
+ }
+#endif
+ return result;
+}
+
+
+void AVIHandler::SetOpenDML( bool flag )
+{
+ isOpenDML = flag;
+}
+
+
+bool AVIHandler::GetOpenDML() const
+{
+ return isOpenDML;
+}
+
+
+/***************************************************************************/
+
+#ifdef HAVE_LIBQUICKTIME
+
+#ifndef HAVE_LIBDV
+#define DV_AUDIO_MAX_SAMPLES 1944
+#endif
+
+// Missing fourcc's in libquicktime (allows compilation)
+#ifndef QUICKTIME_DV_AVID
+#define QUICKTIME_DV_AVID "AVdv"
+#endif
+
+#ifndef QUICKTIME_DV_AVID_A
+#define QUICKTIME_DV_AVID_A "dvcp"
+#endif
+
+#ifndef QUICKTIME_DVCPRO
+#define QUICKTIME_DVCPRO "dvpp"
+#endif
+
+QtHandler::QtHandler() : fd( NULL )
+{
+ extension = ".mov";
+ Init();
+}
+
+
+QtHandler::~QtHandler()
+{
+ Close();
+}
+
+void QtHandler::Init()
+{
+ if ( fd != NULL )
+ Close();
+
+ fd = NULL;
+ samplingRate = 0;
+ samplesPerBuffer = 0;
+ channels = 2;
+ audioBuffer = NULL;
+ audioChannelBuffer = NULL;
+ isFullyInitialized = false;
+}
+
+
+bool QtHandler::FileIsOpen()
+{
+ return fd != NULL;
+}
+
+
+bool QtHandler::Create( const string& filename )
+{
+ Init();
+
+ if ( open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 ) != -1 )
+ {
+ fd = quicktime_open( const_cast<char*>( filename.c_str() ), 0, 1 );
+ if ( fd != NULL )
+ FileTracker::GetInstance().Add( filename.c_str() );
+ }
+ else
+ return false;
+ this->filename = filename;
+ return true;
+}
+
+void QtHandler::AllocateAudioBuffers()
+{
+ if ( channels > 0 && channels < 5 )
+ {
+ audioBufferSize = DV_AUDIO_MAX_SAMPLES * 2;
+ audioBuffer = new int16_t[ audioBufferSize * channels ];
+
+ audioChannelBuffer = new short int * [ channels ];
+ for ( int c = 0; c < channels; c++ )
+ audioChannelBuffer[ c ] = new short int[ audioBufferSize ];
+ isFullyInitialized = true;
+ }
+}
+
+inline void QtHandler::DeinterlaceStereo16( void* pInput, int iBytes,
+ void* pLOutput, void* pROutput )
+{
+ short int * piSampleInput = ( short int* ) pInput;
+ short int* piSampleLOutput = ( short int* ) pLOutput;
+ short int* piSampleROutput = ( short int* ) pROutput;
+
+ while ( ( char* ) piSampleInput < ( ( char* ) pInput + iBytes ) )
+ {
+ *piSampleLOutput++ = *piSampleInput++;
+ *piSampleROutput++ = *piSampleInput++;
+ }
+}
+
+#if 0
+int QtHandler::Write( const Frame& frame )
+{
+ if ( ! isFullyInitialized )
+ {
+ AudioInfo audio;
+
+ if ( frame.GetAudioInfo( audio ) )
+ {
+ channels = 2;
+ quicktime_set_audio( fd, channels, audio.frequency, 16, QUICKTIME_TWOS );
+ }
+ else
+ {
+ channels = 0;
+ }
+
+ quicktime_set_video( fd, 1, 720, frame.IsPAL() ? 576 : 480,
+ frame.GetFrameRate(), QUICKTIME_DV );
+ AllocateAudioBuffers();
+ }
+
+ int result = quicktime_write_frame( fd, const_cast<unsigned char*>( frame.data ),
+ frame.GetFrameSize(), 0 );
+
+ if ( channels > 0 )
+ {
+ AudioInfo audio;
+ if ( frame.GetAudioInfo( audio ) && ( unsigned int ) audio.samples < audioBufferSize )
+ {
+ long bytesRead = frame.ExtractAudio( audioBuffer );
+
+ DeinterlaceStereo16( audioBuffer, bytesRead,
+ audioChannelBuffer[ 0 ],
+ audioChannelBuffer[ 1 ] );
+
+ quicktime_encode_audio( fd, audioChannelBuffer, NULL, audio.samples );
+ }
+ }
+ return result;
+}
+#endif
+
+int QtHandler::Close()
+{
+ if ( fd != NULL )
+ {
+ quicktime_close( fd );
+ fd = NULL;
+ }
+ if ( audioBuffer != NULL )
+ {
+ delete audioBuffer;
+ audioBuffer = NULL;
+ }
+ if ( audioChannelBuffer != NULL )
+ {
+ for ( int c = 0; c < channels; c++ )
+ delete audioChannelBuffer[ c ];
+ delete audioChannelBuffer;
+ audioChannelBuffer = NULL;
+ }
+ return 0;
+}
+
+
+off_t QtHandler::GetFileSize()
+{
+ struct stat file_status;
+ stat( filename.c_str(), &file_status );
+ return file_status.st_size;
+}
+
+
+int QtHandler::GetTotalFrames()
+{
+ return ( int ) quicktime_video_length( fd, 0 );
+}
+
+
+bool QtHandler::Open( const char *s )
+{
+ Init();
+
+ fd = quicktime_open( s, 1, 0 );
+ if ( fd == NULL )
+ {
+ fprintf( stderr, "Error opening: %s\n", s );
+ return false;
+ }
+
+ if ( quicktime_has_video( fd ) <= 0 )
+ {
+ fprintf( stderr, "There must be at least one video track in the input file (%s).\n",
+ s );
+ Close();
+ return false;
+ }
+ char * fcc = quicktime_video_compressor( fd, 0 );
+ if ( strncmp( fcc, QUICKTIME_DV, 4 ) != 0 &&
+ strncmp( fcc, QUICKTIME_DV_AVID, 4 ) != 0 &&
+ strncmp( fcc, QUICKTIME_DV_AVID_A, 4 ) != 0 &&
+ strncmp( fcc, QUICKTIME_DVCPRO, 4 ) != 0 )
+ {
+ Close();
+ return false;
+ }
+ if ( quicktime_has_audio( fd ) )
+ channels = quicktime_track_channels( fd, 0 );
+ filename = s;
+ return true;
+}
+
+int QtHandler::GetFrame( uint8_t *data, int frameNum )
+{
+ assert( fd != NULL );
+
+ quicktime_set_video_position( fd, frameNum, 0 );
+ quicktime_read_frame( fd, data, 0 );
+
+#ifdef HAVE_LIBDV
+ if ( quicktime_has_audio( fd ) )
+ {
+ if ( ! isFullyInitialized )
+ AllocateAudioBuffers();
+
+ // Fetch the frequency of the audio track and calc number of samples needed
+ int frequency = quicktime_sample_rate( fd, 0 );
+ float fps = ( data[ 3 ] & 0x80 ) ? 25.0f : 29.97f;
+ int samples = mlt_sample_calculator( fps, frequency, frameNum );
+ int64_t seek = mlt_sample_calculator_to_now( fps, frequency, frameNum );
+
+ // Obtain a dv encoder and initialise it with minimal info
+ dv_encoder_t *encoder = dv_encoder_new( 0, 0, 0 );
+ encoder->isPAL = ( data[ 3 ] & 0x80 );
+ encoder->samples_this_frame = samples;
+
+ // Seek to the calculated position and decode
+ quicktime_set_audio_position( fd, seek, 0 );
+ lqt_decode_audio( fd, audioChannelBuffer, NULL, (long) samples );
+
+ // Encode the audio on the frame and done
+ dv_encode_full_audio( encoder, audioChannelBuffer, channels, frequency, data );
+ dv_encoder_free( encoder );
+ }
+#endif
+
+ return 0;
+}
+#endif
--- /dev/null
+/*
+* filehandler.h
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _FILEHANDLER_H
+#define _FILEHANDLER_H
+
+// enum { PAL_FORMAT, NTSC_FORMAT, AVI_DV1_FORMAT, AVI_DV2_FORMAT, QT_FORMAT, RAW_FORMAT, TEST_FORMAT, UNDEFINED };
+
+#include <vector>
+using std::vector;
+
+#include <string>
+using std::string;
+
+#include "riff.h"
+#include "avi.h"
+#include <sys/types.h>
+#include <stdint.h>
+
+enum { AVI, PLAYLIST, RAW_DV, QT, UNKNOWN_FORMAT };
+enum { PAL_FORMAT, NTSC_FORMAT, AVI_DV1_FORMAT, AVI_DV2_FORMAT, QT_FORMAT, RAW_FORMAT, TEST_FORMAT, UNDEFINED };
+enum { DISPLAY_XX, DISPLAY_GDKRGB, DISPLAY_GDKRGB32, DISPLAY_XV, DISPLAY_SDL };
+
+enum { NORM_UNSPECIFIED=0, NORM_PAL=1, NORM_NTSC=2 };
+enum { AUDIO_32KHZ=0, AUDIO_44KHZ=1, AUDIO_48KHZ=2 };
+enum { ASPECT_43=0, ASPECT_169=1 };
+
+enum FileCaptureMode {
+ CAPTURE_IGNORE,
+ CAPTURE_FRAME_APPEND,
+ CAPTURE_FRAME_INSERT,
+ CAPTURE_MOVIE_APPEND
+};
+
+class FileTracker
+{
+protected:
+ FileTracker();
+ ~FileTracker();
+public:
+ static FileTracker &GetInstance( );
+ void SetMode( FileCaptureMode );
+ FileCaptureMode GetMode( );
+ unsigned int Size();
+ char *Get( int );
+ void Add( const char * );
+ void Clear( );
+private:
+ static FileTracker *instance;
+ vector <char *> list;
+ FileCaptureMode mode;
+};
+
+class FileHandler
+{
+public:
+
+ FileHandler();
+ virtual ~FileHandler();
+
+ virtual bool GetAutoSplit() const;
+ virtual bool GetTimeStamp() const;
+ virtual string GetBaseName() const;
+ virtual string GetExtension() const;
+ virtual int GetMaxFrameCount() const;
+ virtual off_t GetMaxFileSize() const;
+ virtual off_t GetFileSize() = 0;
+ virtual int GetTotalFrames() = 0;
+ virtual string GetFilename() const;
+
+ virtual void SetAutoSplit( bool );
+ virtual void SetTimeStamp( bool );
+ virtual void SetBaseName( const string& base );
+ virtual void SetMaxFrameCount( int );
+ virtual void SetEveryNthFrame( int );
+ virtual void SetMaxFileSize( off_t );
+ //virtual void SetSampleFrame( const Frame& sample );
+
+ //virtual bool WriteFrame( const Frame& frame );
+ virtual bool FileIsOpen() = 0;
+ virtual bool Create( const string& filename ) = 0;
+ //virtual int Write( const Frame& frame ) = 0;
+ virtual int Close() = 0;
+ virtual bool Done( void );
+
+ virtual bool Open( const char *s ) = 0;
+ virtual int GetFrame( uint8_t *data, int frameNum ) = 0;
+ int GetFramesWritten() const
+ {
+ return framesWritten;
+ }
+
+protected:
+ bool done;
+ bool autoSplit;
+ bool timeStamp;
+ int maxFrameCount;
+ int framesWritten;
+ int everyNthFrame;
+ int framesToSkip;
+ off_t maxFileSize;
+ string base;
+ string extension;
+ string filename;
+};
+
+
+class RawHandler: public FileHandler
+{
+public:
+ int fd;
+
+ RawHandler();
+ ~RawHandler();
+
+ bool FileIsOpen();
+ bool Create( const string& filename );
+ //int Write( const Frame& frame );
+ int Close();
+ off_t GetFileSize();
+ int GetTotalFrames();
+ bool Open( const char *s );
+ int GetFrame( uint8_t *data, int frameNum );
+private:
+ int numBlocks;
+};
+
+
+class AVIHandler: public FileHandler
+{
+public:
+ AVIHandler( int format = AVI_DV1_FORMAT );
+ ~AVIHandler();
+
+ //void SetSampleFrame( const Frame& sample );
+ bool FileIsOpen();
+ bool Create( const string& filename );
+ //int Write( const Frame& frame );
+ int Close();
+ off_t GetFileSize();
+ int GetTotalFrames();
+ bool Open( const char *s );
+ int GetFrame( uint8_t *data, int frameNum );
+ bool GetOpenDML() const;
+ void SetOpenDML( bool );
+ int GetFormat() const
+ {
+ return aviFormat;
+ }
+
+protected:
+ AVIFile *avi;
+ int aviFormat;
+ //AudioInfo audioInfo;
+ //VideoInfo videoInfo;
+ bool isOpenDML;
+ DVINFO dvinfo;
+ FOURCC fccHandler;
+ int channels;
+ bool isFullyInitialized;
+ int16_t *audioBuffer;
+ int16_t *audioChannels[ 4 ];
+};
+
+
+#ifdef HAVE_LIBQUICKTIME
+#include <lqt.h>
+
+class QtHandler: public FileHandler
+{
+public:
+ QtHandler();
+ ~QtHandler();
+
+ bool FileIsOpen();
+ bool Create( const string& filename );
+ //int Write( const Frame& frame );
+ int Close();
+ off_t GetFileSize();
+ int GetTotalFrames();
+ bool Open( const char *s );
+ int GetFrame( uint8_t *data, int frameNum );
+ void AllocateAudioBuffers();
+
+private:
+ quicktime_t *fd;
+ long samplingRate;
+ int samplesPerBuffer;
+ int channels;
+ bool isFullyInitialized;
+ unsigned int audioBufferSize;
+ int16_t *audioBuffer;
+ short int** audioChannelBuffer;
+
+ void Init();
+ inline void DeinterlaceStereo16( void* pInput, int iBytes, void* pLOutput, void* pROutput );
+
+};
+#endif
+
+#endif
--- /dev/null
+/*
+ * kino_wrapper.cc -- c wrapper for kino file handler
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "kino_wrapper.h"
+#include "filehandler.h"
+
+extern "C"
+{
+
+#include <framework/mlt_pool.h>
+
+struct kino_wrapper_s
+{
+ FileHandler *handler;
+ int is_pal;
+};
+
+kino_wrapper kino_wrapper_init( )
+{
+ kino_wrapper self = ( kino_wrapper )malloc( sizeof( kino_wrapper_s ) );
+ if ( self != NULL )
+ self->handler = NULL;
+ return self;
+}
+
+int kino_wrapper_open( kino_wrapper self, char *src )
+{
+ if ( self != NULL )
+ {
+ // Rough file determination based on file type
+ if ( strncasecmp( strrchr( src, '.' ), ".avi", 4 ) == 0 )
+ self->handler = new AVIHandler( );
+ else if ( strncasecmp( strrchr( src, '.' ), ".dv", 3 ) == 0 || strncasecmp( strrchr( src, '.' ), ".dif", 4 ) == 0 )
+ self->handler = new RawHandler( );
+ #ifdef HAVE_LIBQUICKTIME
+ else if ( strncasecmp( strrchr( src, '.' ), ".mov", 4 ) == 0 )
+ self->handler = new QtHandler( );
+ #endif
+
+ // Open the file if we have a handler
+ if ( self->handler != NULL )
+ if ( !self->handler->Open( src ) )
+ self = NULL;
+
+ // Check the first frame to see if it's PAL or NTSC
+ if ( self != NULL && self->handler != NULL )
+ {
+ uint8_t *data = ( uint8_t * )mlt_pool_alloc( 144000 );
+ if ( self->handler->GetFrame( data, 0 ) == 0 )
+ self->is_pal = data[3] & 0x80;
+ else
+ self = NULL;
+ mlt_pool_release( data );
+ }
+ }
+
+ return kino_wrapper_is_open( self );
+}
+
+int kino_wrapper_get_frame_count( kino_wrapper self )
+{
+ return self != NULL && self->handler != NULL ? self->handler->GetTotalFrames( ) : 0;
+}
+
+int kino_wrapper_is_open( kino_wrapper self )
+{
+ return self != NULL && self->handler != NULL ? self->handler->FileIsOpen( ) : 0;
+}
+
+int kino_wrapper_is_pal( kino_wrapper self )
+{
+ return self != NULL ? self->is_pal : 0;
+}
+
+int kino_wrapper_get_frame( kino_wrapper self, uint8_t *data, int index )
+{
+ return self != NULL && self->handler != NULL ? !self->handler->GetFrame( data, index ) : 0;
+}
+
+void kino_wrapper_close( kino_wrapper self )
+{
+ if ( self )
+ delete self->handler;
+ free( self );
+}
+
+}
+
+
--- /dev/null
+/*
+ * kino_wrapper.h -- c wrapper for kino file handler
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MLT_PRODUCER_KINO_WRAPPER_H_
+#define MLT_PRODUCER_KINO_WRAPPER_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct kino_wrapper_s *kino_wrapper;
+
+extern kino_wrapper kino_wrapper_init( );
+extern int kino_wrapper_open( kino_wrapper, char * );
+extern int kino_wrapper_is_open( kino_wrapper );
+extern int kino_wrapper_is_pal( kino_wrapper );
+extern int kino_wrapper_get_frame_count( kino_wrapper );
+extern int kino_wrapper_get_frame( kino_wrapper, uint8_t *, int );
+extern void kino_wrapper_close( kino_wrapper );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * producer_kino.c -- a DV file format parser
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <stdlib.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include "kino_wrapper.h"
+
+/* NB: This is an abstract producer - it provides no codec support whatsoever. */
+
+#define FRAME_SIZE_525_60 10 * 150 * 80
+#define FRAME_SIZE_625_50 12 * 150 * 80
+
+typedef struct producer_kino_s *producer_kino;
+
+struct producer_kino_s
+{
+ struct mlt_producer_s parent;
+ kino_wrapper wrapper;
+};
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_kino_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
+{
+ kino_wrapper wrapper = kino_wrapper_init( );
+
+ if ( kino_wrapper_open( wrapper, filename ) )
+ {
+ producer_kino this = calloc( sizeof( struct producer_kino_s ), 1 );
+
+ if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+ {
+ mlt_producer producer = &this->parent;
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ double fps = kino_wrapper_is_pal( wrapper ) ? 25 : 30000.0 / 1001.0;
+
+ // Assign the wrapper
+ this->wrapper = wrapper;
+
+ // Pass wrapper properties (frame rate, count etc)
+ mlt_properties_set_position( properties, "length", kino_wrapper_get_frame_count( wrapper ) );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", kino_wrapper_get_frame_count( wrapper ) - 1 );
+ mlt_properties_set_double( properties, "real_fps", fps );
+ mlt_properties_set( properties, "resource", filename );
+
+ // Register transport implementation with the producer
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Register our get_frame implementation with the producer
+ producer->get_frame = producer_get_frame;
+
+ // Return the producer
+ return producer;
+ }
+ free( this );
+ }
+
+ kino_wrapper_close( wrapper );
+
+ return NULL;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ producer_kino this = producer->child;
+ uint8_t *data = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+ // Obtain the current frame number
+ uint64_t position = mlt_producer_frame( producer );
+
+ // Create an empty frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ // Seek and fetch
+ if ( kino_wrapper_get_frame( this->wrapper, data, position ) )
+ {
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Determine if we're PAL or NTSC
+ int is_pal = kino_wrapper_is_pal( this->wrapper );
+
+ // Pass the dv data
+ mlt_properties_set_data( properties, "dv_data", data, FRAME_SIZE_625_50, ( mlt_destructor )mlt_pool_release, NULL );
+
+ // Update other info on the frame
+ mlt_properties_set_int( properties, "width", 720 );
+ mlt_properties_set_int( properties, "height", is_pal ? 576 : 480 );
+ mlt_properties_set_int( properties, "top_field_first", is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 );
+ }
+ else
+ {
+ mlt_pool_release( data );
+ }
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ if ( parent != NULL )
+ {
+ // Obtain this
+ producer_kino this = parent->child;
+
+ // Close the file
+ if ( this != NULL )
+ kino_wrapper_close( this->wrapper );
+
+ // Close the parent
+ parent->close = NULL;
+ mlt_producer_close( parent );
+
+ // Free the memory
+ free( this );
+ }
+}
--- /dev/null
+/*
+* riff.cc library for RIFF file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU 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.
+*
+* Tag: $Name$
+*
+* Change log:
+*
+* $Log$
+* Revision 1.3 2005/07/25 14:41:29 lilo_booter
+* + Minor correction for entry length being less than the data length
+*
+* Revision 1.2 2005/07/25 07:21:39 lilo_booter
+* + fixes for opendml dv avi
+*
+* Revision 1.1 2005/04/15 14:28:26 lilo_booter
+* Initial version
+*
+* Revision 1.18 2005/04/01 23:43:10 ddennedy
+* apply endian fixes from Daniel Kobras
+*
+* Revision 1.17 2004/10/11 01:37:11 ddennedy
+* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
+*
+* Revision 1.16 2003/11/25 23:01:24 ddennedy
+* cleanup and a few bugfixes
+*
+* Revision 1.15 2003/10/21 16:34:34 ddennedy
+* GNOME2 port phase 1: initial checkin
+*
+* Revision 1.13.2.3 2003/08/26 20:39:00 ddennedy
+* relocate mutex unlock and add assert includes
+*
+* Revision 1.13.2.2 2003/01/28 12:54:13 lilo_booter
+* New 'no change' image transition
+*
+* Revision 1.13.2.1 2002/11/25 04:48:31 ddennedy
+* bugfix to report errors when loading files
+*
+* Revision 1.13 2002/09/13 06:49:49 ddennedy
+* build update, cleanup, bugfixes
+*
+* Revision 1.12 2002/04/21 06:36:40 ddennedy
+* kindler avc and 1394 bus reset support in catpure page, honor max file size
+*
+* Revision 1.11 2002/04/09 06:53:42 ddennedy
+* cleanup, new libdv 0.9.5, large AVI, dnd storyboard
+*
+* Revision 1.4 2002/03/25 21:34:25 arne
+* Support for large (64 bit) files mostly completed
+*
+* Revision 1.3 2002/03/10 21:28:29 arne
+* release 1.1b1, 64 bit support for type 1 avis
+*
+* Revision 1.2 2002/03/04 19:22:43 arne
+* updated to latest Kino avi code
+*
+* Revision 1.1.1.1 2002/03/03 19:08:08 arne
+* import of version 1.01
+*
+*/
+
+#include "config.h"
+
+// C++ includes
+
+#include <string>
+//#include <stdio.h>
+#include <iostream>
+#include <iomanip>
+#ifndef __FreeBSD__
+#include <byteswap.h>
+#endif /* __FreeBSD__ */
+
+using std::cout;
+using std::hex;
+using std::dec;
+using std::setw;
+using std::setfill;
+using std::endl;
+
+// C includes
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+// local includes
+
+#include "error.h"
+#include "riff.h"
+
+
+/** make a 32 bit "string-id"
+
+ \param s a pointer to 4 chars
+ \return the 32 bit "string id"
+ \bugs It is not checked whether we really have 4 characters
+
+ Some compilers understand constants like int id = 'ABCD'; but I
+ could not get it working on the gcc compiler so I had to use this
+ workaround. We can now use id = make_fourcc("ABCD") instead. */
+
+FOURCC make_fourcc( const char *s )
+{
+ if ( s[ 0 ] == 0 )
+ return 0;
+ else
+ return *( ( FOURCC* ) s );
+}
+
+
+RIFFDirEntry::RIFFDirEntry()
+{}
+
+
+RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 )
+{}
+
+
+/** Creates the object without an output file.
+
+*/
+
+RIFFFile::RIFFFile() : fd( -1 )
+{
+ pthread_mutex_init( &file_mutex, NULL );
+}
+
+
+/* Copy constructor
+
+ Duplicate the file descriptor
+*/
+
+RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 )
+{
+ if ( riff.fd != -1 )
+ {
+ fd = dup( riff.fd );
+ }
+ directory = riff.directory;
+}
+
+
+/** Destroys the object.
+
+ If it has an associated opened file, close it. */
+
+RIFFFile::~RIFFFile()
+{
+ Close();
+ pthread_mutex_destroy( &file_mutex );
+}
+
+
+RIFFFile& RIFFFile::operator=( const RIFFFile& riff )
+{
+ if ( fd != riff.fd )
+ {
+ Close();
+ if ( riff.fd != -1 )
+ {
+ fd = dup( riff.fd );
+ }
+ directory = riff.directory;
+ }
+ return *this;
+}
+
+
+/** Creates or truncates the file.
+
+ \param s the filename
+*/
+
+bool RIFFFile::Create( const char *s )
+{
+ fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 );
+
+ if ( fd == -1 )
+ return false;
+ else
+ return true;
+}
+
+
+/** Opens the file read only.
+
+ \param s the filename
+*/
+
+bool RIFFFile::Open( const char *s )
+{
+ fd = open( s, O_RDONLY | O_NONBLOCK );
+
+ if ( fd == -1 )
+ return false;
+ else
+ return true;
+}
+
+
+/** Destroys the object.
+
+ If it has an associated opened file, close it. */
+
+void RIFFFile::Close()
+{
+ if ( fd != -1 )
+ {
+ close( fd );
+ fd = -1;
+ }
+}
+
+
+/** Adds an entry to the list of containers.
+
+ \param type the type of this entry
+ \param name the name
+ \param length the length of the data in the container
+ \param list the container in which this object is contained.
+ \return the ID of the newly created entry
+
+ The topmost object is not contained in any other container. Use
+ the special ID RIFF_NO_PARENT to create the topmost object. */
+
+int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list )
+{
+ /* Put all parameters in an RIFFDirEntry object. The offset is
+ currently unknown. */
+
+ RIFFDirEntry entry( type, name, length, 0 /* offset */, list );
+
+ /* If the new chunk is in a list, then get the offset and size
+ of that list. The offset of this chunk is the end of the list
+ (parent_offset + parent_length) plus the size of the chunk
+ header. */
+
+ if ( list != RIFF_NO_PARENT )
+ {
+ RIFFDirEntry parent = GetDirectoryEntry( list );
+ entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE;
+ }
+
+ /* The list which this new chunk is a member of has now increased in
+ size. Get that directory entry and bump up its length by the size
+ of the chunk. Since that list may also be contained in another
+ list, walk up to the top of the tree. */
+
+ while ( list != RIFF_NO_PARENT )
+ {
+ RIFFDirEntry parent = GetDirectoryEntry( list );
+ parent.length += RIFF_HEADERSIZE + length;
+ SetDirectoryEntry( list, parent );
+ list = parent.parent;
+ }
+
+ directory.insert( directory.end(), entry );
+
+ return directory.size() - 1;
+}
+
+
+/** Modifies an entry.
+
+ \param i the ID of the entry which is to modify
+ \param type the type of this entry
+ \param name the name
+ \param length the length of the data in the container
+ \param list the container in which this object is contained.
+ \note Do not change length, offset, or the parent container.
+ \note Do not change an empty name ("") to a name and vice versa */
+
+void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list )
+{
+ RIFFDirEntry entry( type, name, length, offset, list );
+
+ assert( i >= 0 && i < ( int ) directory.size() );
+
+ directory[ i ] = entry;
+}
+
+
+/** Modifies an entry.
+
+ The entry.written flag is set to false because the contents has been modified
+
+ \param i the ID of the entry which is to modify
+ \param entry the new entry
+ \note Do not change length, offset, or the parent container.
+ \note Do not change an empty name ("") to a name and vice versa */
+
+void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry )
+{
+ assert( i >= 0 && i < ( int ) directory.size() );
+
+ entry.written = false;
+ directory[ i ] = entry;
+}
+
+
+/** Retrieves an entry.
+
+ Gets the most important member variables.
+
+ \param i the ID of the entry to retrieve
+ \param type
+ \param name
+ \param length
+ \param offset
+ \param list */
+
+void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const
+{
+ RIFFDirEntry entry;
+
+ assert( i >= 0 && i < ( int ) directory.size() );
+
+ entry = directory[ i ];
+ type = entry.type;
+ name = entry.name;
+ length = entry.length;
+ offset = entry.offset;
+ list = entry.parent;
+}
+
+
+/** Retrieves an entry.
+
+ Gets the whole RIFFDirEntry object.
+
+ \param i the ID of the entry to retrieve
+ \return the entry */
+
+RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const
+{
+ assert( i >= 0 && i < ( int ) directory.size() );
+
+ return directory[ i ];
+}
+
+
+/** Calculates the total size of the file
+
+ \return the size the file in bytes
+*/
+
+off_t RIFFFile::GetFileSize( void ) const
+{
+
+ /* If we have at least one entry, return the length field
+ of the FILE entry, which is the length of its contents,
+ which is the actual size of whatever is currently in the
+ AVI directory structure.
+
+ Note that the first entry does not belong to the AVI
+ file.
+
+ If we don't have any entry, the file size is zero. */
+
+ if ( directory.size() > 0 )
+ return directory[ 0 ].length;
+ else
+ return 0;
+}
+
+
+/** prints the attributes of the entry
+
+ \param i the ID of the entry to print
+*/
+
+void RIFFFile::PrintDirectoryEntry ( int i ) const
+{
+ RIFFDirEntry entry;
+ RIFFDirEntry parent;
+ FOURCC entry_name;
+ FOURCC list_name;
+
+ /* Get all attributes of the chunk object. If it is contained
+ in a list, get the name of the list too (otherwise the name of
+ the list is blank). If the chunk object doesn´t have a name (only
+ LISTs and RIFFs have a name), the name is blank. */
+
+ entry = GetDirectoryEntry( i );
+ if ( entry.parent != RIFF_NO_PARENT )
+ {
+ parent = GetDirectoryEntry( entry.parent );
+ list_name = parent.name;
+ }
+ else
+ {
+ list_name = make_fourcc( " " );
+ }
+ if ( entry.name != 0 )
+ {
+ entry_name = entry.name;
+ }
+ else
+ {
+ entry_name = make_fourcc( " " );
+ }
+
+ /* Print out the ascii representation of type and name, as well as
+ length and file offset. */
+
+ cout << hex << setfill( '0' ) << "type: "
+ << ((char *)&entry.type)[0]
+ << ((char *)&entry.type)[1]
+ << ((char *)&entry.type)[2]
+ << ((char *)&entry.type)[3]
+ << " name: "
+ << ((char *)&entry_name)[0]
+ << ((char *)&entry_name)[1]
+ << ((char *)&entry_name)[2]
+ << ((char *)&entry_name)[3]
+ << " length: 0x" << setw( 12 ) << entry.length
+ << " offset: 0x" << setw( 12 ) << entry.offset
+ << " list: "
+ << ((char *)&list_name)[0]
+ << ((char *)&list_name)[1]
+ << ((char *)&list_name)[2]
+ << ((char *)&list_name)[3] << dec << endl;
+
+ /* print the content itself */
+
+ PrintDirectoryEntryData( entry );
+}
+
+
+/** prints the contents of the entry
+
+ Prints a readable representation of the contents of an index.
+ Override this to print out any objects you store in the RIFF file.
+
+ \param entry the entry to print */
+
+void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
+ {}
+
+
+/** prints the contents of the whole directory
+
+ Prints a readable representation of the contents of an index.
+ Override this to print out any objects you store in the RIFF file.
+
+ \param entry the entry to print */
+
+void RIFFFile::PrintDirectory() const
+{
+ int i;
+ int count = directory.size();
+
+ for ( i = 0; i < count; ++i )
+ PrintDirectoryEntry( i );
+}
+
+
+/** finds the index
+
+ finds the index of a given directory entry type
+
+ \todo inefficient if the directory has lots of items
+ \param type the type of the entry to find
+ \param n the zero-based instance of type to locate
+ \return the index of the found object in the directory, or -1 if not found */
+
+int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const
+{
+ int i, j = 0;
+ int count = directory.size();
+
+ for ( i = 0; i < count; ++i )
+ if ( directory[ i ].type == type )
+ {
+ if ( j == n )
+ return i;
+ j++;
+ }
+
+ return -1;
+}
+
+
+/** Reads all items that are contained in one list
+
+ Read in one chunk and add it to the directory. If the chunk
+ happens to be of type LIST, then call ParseList recursively for
+ it.
+
+ \param parent The id of the item to process
+*/
+
+void RIFFFile::ParseChunk( int parent )
+{
+ FOURCC type;
+ DWORD length;
+ int typesize;
+
+ /* Check whether it is a LIST. If so, let ParseList deal with it */
+
+ fail_if( read( fd, &type, sizeof( type ) ) != sizeof( type ));
+ if ( type == make_fourcc( "LIST" ) )
+ {
+ typesize = (int) -sizeof( type );
+ fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 );
+ ParseList( parent );
+ }
+
+ /* it is a normal chunk, create a new directory entry for it */
+
+ else
+ {
+ fail_neg( read( fd, &length, sizeof( length ) ) );
+ if ( length & 1 )
+ length++;
+ AddDirectoryEntry( type, 0, length, parent );
+ fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 );
+ }
+}
+
+
+/** Reads all items that are contained in one list
+
+ \param parent The id of the list to process
+
+*/
+
+void RIFFFile::ParseList( int parent )
+{
+ FOURCC type;
+ FOURCC name;
+ int list;
+ DWORD length;
+ off_t pos;
+ off_t listEnd;
+
+ /* Read in the chunk header (type and length). */
+ fail_neg( read( fd, &type, sizeof( type ) ) );
+ fail_neg( read( fd, &length, sizeof( length ) ) );
+
+ if ( length & 1 )
+ length++;
+
+ /* The contents of the list starts here. Obtain its offset. The list
+ name (4 bytes) is already part of the contents). */
+
+ pos = lseek( fd, 0, SEEK_CUR );
+ fail_if( pos == ( off_t ) - 1 );
+ fail_neg( read( fd, &name, sizeof( name ) ) );
+
+ /* Add an entry for this list. */
+
+ list = AddDirectoryEntry( type, name, sizeof( name ), parent );
+
+ /* Read in any chunks contained in this list. This list is the
+ parent for all chunks it contains. */
+
+ listEnd = pos + length;
+ while ( pos < listEnd )
+ {
+ ParseChunk( list );
+ pos = lseek( fd, 0, SEEK_CUR );
+ fail_if( pos == ( off_t ) - 1 );
+ }
+}
+
+
+/** Reads the directory structure of the whole RIFF file
+
+*/
+
+void RIFFFile::ParseRIFF( void )
+{
+ FOURCC type;
+ DWORD length;
+ off_t filesize;
+ off_t pos;
+ int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
+
+ pos = lseek( fd, 0, SEEK_SET );
+
+ /* calculate file size from RIFF header instead from physical file. */
+
+ while ( ( read( fd, &type, sizeof( type ) ) > 0 ) &&
+ ( read( fd, &length, sizeof( length ) ) > 0 ) &&
+ ( type == make_fourcc( "RIFF" ) ) )
+ {
+
+ filesize += length + RIFF_HEADERSIZE;
+
+ fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 );
+ ParseList( container );
+ pos = lseek( fd, 0, SEEK_CUR );
+ fail_if( pos == ( off_t ) - 1 );
+ }
+}
+
+
+/** Reads one item including its contents from the RIFF file
+
+ \param chunk_index The index of the item to write
+ \param data A pointer to the data
+
+*/
+
+void RIFFFile::ReadChunk( int chunk_index, void *data, off_t data_len )
+{
+ RIFFDirEntry entry;
+
+ entry = GetDirectoryEntry( chunk_index );
+ pthread_mutex_lock( &file_mutex );
+ fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( read( fd, data, entry.length > data_len ? data_len : entry.length ) );
+ pthread_mutex_unlock( &file_mutex );
+}
+
+
+/** Writes one item including its contents to the RIFF file
+
+ \param chunk_index The index of the item to write
+ \param data A pointer to the data
+
+*/
+
+void RIFFFile::WriteChunk( int chunk_index, const void *data )
+{
+ RIFFDirEntry entry;
+
+ entry = GetDirectoryEntry( chunk_index );
+ pthread_mutex_lock( &file_mutex );
+ fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
+ fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
+ DWORD length = entry.length;
+ fail_neg( write( fd, &length, sizeof( length ) ) );
+ fail_neg( write( fd, data, entry.length ) );
+ pthread_mutex_unlock( &file_mutex );
+
+ /* Remember that this entry already has been written. */
+
+ directory[ chunk_index ].written = true;
+}
+
+
+/** Writes out the directory structure
+
+ For all items in the directory list that have not been written
+ yet, it seeks to the file position where that item should be
+ stored and writes the type and length field. If the item has a
+ name, it will also write the name field.
+
+ \note It does not write the contents of any item. Use WriteChunk to do that. */
+
+void RIFFFile::WriteRIFF( void )
+{
+ int i;
+ RIFFDirEntry entry;
+ int count = directory.size();
+
+ /* Start at the second entry (RIFF), since the first entry (FILE)
+ is needed only for internal purposes and is not written to the
+ file. */
+
+ for ( i = 1; i < count; ++i )
+ {
+
+ /* Only deal with entries that haven´t been written */
+
+ entry = GetDirectoryEntry( i );
+ if ( entry.written == false )
+ {
+
+ /* A chunk entry consist of its type and length, a list
+ entry has an additional name. Look up the entry, seek
+ to the start of the header, which is at the offset of
+ the data start minus the header size and write out the
+ items. */
+
+ fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ;
+ fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
+ DWORD length = entry.length;
+ fail_neg( write( fd, &length, sizeof( length ) ) );
+
+ /* If it has a name, it is a list. Write out the extra name
+ field. */
+
+ if ( entry.name != 0 )
+ {
+ fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) );
+ }
+
+ /* Remember that this entry already has been written. */
+
+ directory[ i ].written = true;
+ }
+ }
+}
--- /dev/null
+/*
+* riff.h library for RIFF file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* 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 2 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 General Public License for more details.
+*
+* You should have received a copy of the GNU 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.
+*
+* Tag: $Name$
+*
+* Change log:
+*
+* $Log$
+* Revision 1.2 2005/07/25 07:21:39 lilo_booter
+* + fixes for opendml dv avi
+*
+* Revision 1.1 2005/04/15 14:28:26 lilo_booter
+* Initial version
+*
+* Revision 1.14 2005/04/01 23:43:10 ddennedy
+* apply endian fixes from Daniel Kobras
+*
+* Revision 1.13 2004/10/11 01:37:11 ddennedy
+* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
+*
+* Revision 1.12 2004/01/06 22:53:42 ddennedy
+* metadata editing tweaks and bugfixes, new ui elements in preparation for publish functions
+*
+* Revision 1.11 2003/11/25 23:01:25 ddennedy
+* cleanup and a few bugfixes
+*
+* Revision 1.10 2003/10/21 16:34:34 ddennedy
+* GNOME2 port phase 1: initial checkin
+*
+* Revision 1.8.4.1 2002/11/25 04:48:31 ddennedy
+* bugfix to report errors when loading files
+*
+* Revision 1.8 2002/04/21 06:36:40 ddennedy
+* kindler avc and 1394 bus reset support in catpure page, honor max file size
+*
+* Revision 1.7 2002/04/09 06:53:42 ddennedy
+* cleanup, new libdv 0.9.5, large AVI, dnd storyboard
+*
+* Revision 1.3 2002/03/25 21:34:25 arne
+* Support for large (64 bit) files mostly completed
+*
+* Revision 1.2 2002/03/04 19:22:43 arne
+* updated to latest Kino avi code
+*
+* Revision 1.1.1.1 2002/03/03 19:08:08 arne
+* import of version 1.01
+*
+*/
+
+#ifndef _RIFF_H
+#define _RIFF_H 1
+
+#include <vector>
+using std::vector;
+
+#include <pthread.h>
+
+#include "endian_types.h"
+
+#define QUADWORD int64_le_t
+#define DWORD int32_le_t
+#define LONG u_int32_le_t
+#define WORD int16_le_t
+#define BYTE u_int8_le_t
+#define FOURCC u_int32_t // No endian conversion needed.
+
+#define RIFF_NO_PARENT (-1)
+#define RIFF_LISTSIZE (4)
+#define RIFF_HEADERSIZE (8)
+
+#ifdef __cplusplus
+extern "C"
+{
+ FOURCC make_fourcc( const char * s );
+}
+#endif
+
+class RIFFDirEntry
+{
+public:
+ FOURCC type;
+ FOURCC name;
+ off_t length;
+ off_t offset;
+ int parent;
+ int written;
+
+ RIFFDirEntry();
+ RIFFDirEntry( FOURCC t, FOURCC n, int l, int o, int p );
+};
+
+
+class RIFFFile
+{
+public:
+ RIFFFile();
+ RIFFFile( const RIFFFile& );
+ virtual ~RIFFFile();
+ RIFFFile& operator=( const RIFFFile& );
+
+ virtual bool Open( const char *s );
+ virtual bool Create( const char *s );
+ virtual void Close();
+ virtual int AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list );
+ virtual void SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list );
+ virtual void SetDirectoryEntry( int i, RIFFDirEntry &entry );
+ virtual void GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const;
+ virtual RIFFDirEntry GetDirectoryEntry( int i ) const;
+ virtual off_t GetFileSize( void ) const;
+ virtual void PrintDirectoryEntry( int i ) const;
+ virtual void PrintDirectoryEntryData( const RIFFDirEntry &entry ) const;
+ virtual void PrintDirectory( void ) const;
+ virtual int FindDirectoryEntry( FOURCC type, int n = 0 ) const;
+ virtual void ParseChunk( int parent );
+ virtual void ParseList( int parent );
+ virtual void ParseRIFF( void );
+ virtual void ReadChunk( int chunk_index, void *data, off_t data_len );
+ virtual void WriteChunk( int chunk_index, const void *data );
+ virtual void WriteRIFF( void );
+
+protected:
+ int fd;
+ pthread_mutex_t file_mutex;
+
+private:
+ vector<RIFFDirEntry> directory;
+};
+#endif
--- /dev/null
+include ../../../config.mak
+LDFLAGS=
+
+all: luma create_lumas
+ @./create_lumas
+
+luma: luma.c
+
+create_lumas:
+
+depend:
+
+distclean:
+ rm -rf PAL NTSC luma
+
+clean:
+ rm -f luma
+
+install: all
+ install -d $(DESTDIR)$(prefix)/share/mlt/lumas/PAL
+ install -d $(DESTDIR)$(prefix)/share/mlt/lumas/NTSC
+ install -m 644 PAL/* $(DESTDIR)$(prefix)/share/mlt/lumas/PAL
+ install -m 644 NTSC/* $(DESTDIR)$(prefix)/share/mlt/lumas/NTSC
--- /dev/null
+#!/bin/sh
+
+if [ "$help" = "1" ]
+then
+ cat << EOF
+Luma options:
+
+ --luma-compress - Produce compressed (png) lumas
+ --luma-8bpp - Produce 8 bit pgm lumas (defaut is 16 bit)
+
+EOF
+
+else
+
+ rm -f .8bit .compress .executed
+
+ for i in "$@"
+ do
+ case $i in
+ --luma-compress ) touch .compress ;;
+ --luma-8bit ) touch .8bit ;;
+ esac
+ done
+
+fi
+
--- /dev/null
+#!/bin/sh
+
+[ \( -d PAL \) -a \( ! $0 -nt .executed \) ] && exit 0
+
+bpp=16
+[ -f .8bit ] && bpp=8
+
+for i in PAL NTSC
+do
+ mkdir -p $i
+ rm -f $i/*.pgm $i/*.png
+
+ [ "$i" = "PAL" ] && h=576 || h=480
+ ./luma -h $h -bpp $bpp > $i/luma01.pgm
+ ./luma -h $h -bpp $bpp -bands $h > $i/luma02.pgm
+ ./luma -h $h -bpp $bpp -hmirror 1 > $i/luma03.pgm
+ ./luma -h $h -bpp $bpp -bands $h -vmirror 1 > $i/luma04.pgm
+ ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 > $i/luma05.pgm
+ ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -flip 1 > $i/luma06.pgm
+ ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -quart 1 > $i/luma07.pgm
+ ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -quart 1 -flip 1 > $i/luma08.pgm
+ ./luma -h $h -bpp $bpp -bands 12 -rband 0 > $i/luma09.pgm
+ ./luma -h $h -bpp $bpp -bands 12 -rband 0 -rotate 1 -flop 1 > $i/luma10.pgm
+ ./luma -h $h -bpp $bpp -bands 12 -rband 1 > $i/luma11.pgm
+ ./luma -h $h -bpp $bpp -bands 12 -rband 1 -vmirror 1 > $i/luma12.pgm
+ ./luma -h $h -bpp $bpp -bands 12 -rband 1 -rotate 1 -flop 1 > $i/luma13.pgm
+ ./luma -h $h -bpp $bpp -bands 12 -rband 1 -rotate 1 -vmirror 1 > $i/luma14.pgm
+ ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -hmirror 1 > $i/luma15.pgm
+ ./luma -h $h -bpp $bpp -type 1 > $i/luma16.pgm
+ ./luma -h $h -bpp $bpp -type 1 -bands 2 -rband 1 > $i/luma17.pgm
+ ./luma -h $h -bpp $bpp -type 2 > $i/luma18.pgm
+ ./luma -h $h -bpp $bpp -type 2 -quart 1 > $i/luma19.pgm
+ ./luma -h $h -bpp $bpp -type 2 -quart 1 -flip 1 > $i/luma20.pgm
+ ./luma -h $h -bpp $bpp -type 2 -quart 1 -bands 2 > $i/luma21.pgm
+ ./luma -h $h -bpp $bpp -type 3 > $i/luma22.pgm
+
+ if [ -f .compress ]
+ then
+ for f in $i/*.pgm
+ do
+ convert $f $f.png
+ rm -f $f
+ done
+ fi
+done
+
+touch .executed
+
--- /dev/null
+/*
+ * luma.c -- image generator for transition_luma
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef struct
+{
+ int type;
+ int w;
+ int h;
+ int bands;
+ int rband;
+ int vmirror;
+ int hmirror;
+ int dmirror;
+ int invert;
+ int offset;
+ int flip;
+ int flop;
+ int pflip;
+ int pflop;
+ int quart;
+ int rotate;
+}
+luma;
+
+void luma_init( luma *this )
+{
+ memset( this, 0, sizeof( luma ) );
+ this->type = 0;
+ this->w = 720;
+ this->h = 576;
+ this->bands = 1;
+ this->rband = 0;
+ this->vmirror = 0;
+ this->hmirror = 0;
+ this->dmirror = 0;
+ this->invert = 0;
+ this->offset = 0;
+ this->flip = 0;
+ this->flop = 0;
+ this->quart = 0;
+ this->pflop = 0;
+ this->pflip = 0;
+}
+
+static inline int sqrti( int n )
+{
+ int p = 0;
+ int q = 1;
+ int r = n;
+ int h = 0;
+
+ while( q <= n )
+ q = 4 * q;
+
+ while( q != 1 )
+ {
+ q = q / 4;
+ h = p + q;
+ p = p / 2;
+ if ( r >= h )
+ {
+ p = p + q;
+ r = r - h;
+ }
+ }
+
+ return p;
+}
+
+uint16_t *luma_render( luma *this )
+{
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ if ( this->quart )
+ {
+ this->w *= 2;
+ this->h *= 2;
+ }
+
+ if ( this->rotate )
+ {
+ int t = this->w;
+ this->w = this->h;
+ this->h = t;
+ }
+
+ int max = ( 1 << 16 ) - 1;
+ uint16_t *image = malloc( this->w * this->h * sizeof( uint16_t ) );
+ uint16_t *end = image + this->w * this->h;
+ uint16_t *p = image;
+ uint16_t *r = image;
+ int lower = 0;
+ int lpb = this->h / this->bands;
+ int rpb = max / this->bands;
+ int direction = 1;
+
+ int half_w = this->w / 2;
+ int half_h = this->h / 2;
+
+ if ( !this->dmirror && ( this->hmirror || this->vmirror ) )
+ rpb *= 2;
+
+ for ( i = 0; i < this->bands; i ++ )
+ {
+ lower = i * rpb;
+ direction = 1;
+
+ if ( this->rband && i % 2 == 1 )
+ {
+ direction = -1;
+ lower += rpb;
+ }
+
+ switch( this->type )
+ {
+ case 1:
+ {
+ int length = sqrti( half_w * half_w + lpb * lpb / 4 );
+ int value;
+ int x = 0;
+ int y = 0;
+ for ( j = 0; j < lpb; j ++ )
+ {
+ y = j - lpb / 2;
+ for ( k = 0; k < this->w; k ++ )
+ {
+ x = k - half_w;
+ value = sqrti( x * x + y * y );
+ *p ++ = lower + ( direction * rpb * ( ( max * value ) / length ) / max ) + ( j * this->offset * 2 / lpb ) + ( j * this->offset / lpb );
+ }
+ }
+ }
+ break;
+
+ case 2:
+ {
+ for ( j = 0; j < lpb; j ++ )
+ {
+ int value = ( ( j * this->w ) / lpb ) - half_w;
+ if ( value > 0 )
+ value = - value;
+ for ( k = - half_w; k < value; k ++ )
+ *p ++ = lower + ( direction * rpb * ( ( max * abs( k ) ) / half_w ) / max );
+ for ( k = value; k < abs( value ); k ++ )
+ *p ++ = lower + ( direction * rpb * ( ( max * abs( value ) ) / half_w ) / max ) + ( j * this->offset * 2 / lpb ) + ( j * this->offset / lpb );
+ for ( k = abs( value ); k < half_w; k ++ )
+ *p ++ = lower + ( direction * rpb * ( ( max * abs( k ) ) / half_w ) / max );
+ }
+ }
+ break;
+
+ case 3:
+ {
+ int length;
+ for ( j = -half_h; j < half_h; j ++ )
+ {
+ if ( j < 0 )
+ {
+ for ( k = - half_w; k < half_w; k ++ )
+ {
+ length = sqrti( k * k + j * j );
+ *p ++ = ( max / 4 * k ) / ( length + 1 );
+ }
+ }
+ else
+ {
+ for ( k = half_w; k > - half_w; k -- )
+ {
+ length = sqrti( k * k + j * j );
+ *p ++ = ( max / 2 ) + ( max / 4 * k ) / ( length + 1 );
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ for ( j = 0; j < lpb; j ++ )
+ for ( k = 0; k < this->w; k ++ )
+ *p ++ = lower + ( direction * ( rpb * ( ( k * max ) / this->w ) / max ) ) + ( j * this->offset * 2 / lpb );
+ break;
+ }
+ }
+
+ if ( this->quart )
+ {
+ this->w /= 2;
+ this->h /= 2;
+ for ( i = 1; i < this->h; i ++ )
+ {
+ p = image + i * this->w;
+ r = image + i * 2 * this->w;
+ j = this->w;
+ while ( j -- > 0 )
+ *p ++ = *r ++;
+ }
+ }
+
+ if ( this->dmirror )
+ {
+ for ( i = 0; i < this->h; i ++ )
+ {
+ p = image + i * this->w;
+ r = end - i * this->w;
+ j = ( this->w * ( this->h - i ) ) / this->h;
+ while ( j -- )
+ *( -- r ) = *p ++;
+ }
+ }
+
+ if ( this->flip )
+ {
+ uint16_t t;
+ for ( i = 0; i < this->h; i ++ )
+ {
+ p = image + i * this->w;
+ r = p + this->w;
+ while( p != r )
+ {
+ t = *p;
+ *p ++ = *( -- r );
+ *r = t;
+ }
+ }
+ }
+
+ if ( this->flop )
+ {
+ uint16_t t;
+ r = end;
+ for ( i = 1; i < this->h / 2; i ++ )
+ {
+ p = image + i * this->w;
+ j = this->w;
+ while( j -- )
+ {
+ t = *( -- p );
+ *p = *( -- r );
+ *r = t;
+ }
+ }
+ }
+
+ if ( this->hmirror )
+ {
+ p = image;
+ while ( p < end )
+ {
+ r = p + this->w;
+ while ( p != r )
+ *( -- r ) = *p ++;
+ p += this->w / 2;
+ }
+ }
+
+ if ( this->vmirror )
+ {
+ p = image;
+ r = end;
+ while ( p != r )
+ *( -- r ) = *p ++;
+ }
+
+ if ( this->invert )
+ {
+ p = image;
+ r = image;
+ while ( p < end )
+ *p ++ = max - *r ++;
+ }
+
+ if ( this->pflip )
+ {
+ uint16_t t;
+ for ( i = 0; i < this->h; i ++ )
+ {
+ p = image + i * this->w;
+ r = p + this->w;
+ while( p != r )
+ {
+ t = *p;
+ *p ++ = *( -- r );
+ *r = t;
+ }
+ }
+ }
+
+ if ( this->pflop )
+ {
+ uint16_t t;
+ end = image + this->w * this->h;
+ r = end;
+ for ( i = 1; i < this->h / 2; i ++ )
+ {
+ p = image + i * this->w;
+ j = this->w;
+ while( j -- )
+ {
+ t = *( -- p );
+ *p = *( -- r );
+ *r = t;
+ }
+ }
+ }
+
+ if ( this->rotate )
+ {
+ uint16_t *image2 = malloc( this->w * this->h * sizeof( uint16_t ) );
+ for ( i = 0; i < this->h; i ++ )
+ {
+ p = image + i * this->w;
+ r = image2 + this->h - i - 1;
+ for ( j = 0; j < this->w; j ++ )
+ {
+ *r = *( p ++ );
+ r += this->h;
+ }
+ }
+ i = this->w;
+ this->w = this->h;
+ this->h = i;
+ free( image );
+ image = image2;
+ }
+
+ return image;
+}
+
+int main( int argc, char **argv )
+{
+ int arg = 1;
+ int bpp = 8;
+
+ luma this;
+ uint16_t *image = NULL;
+
+ luma_init( &this );
+
+ for ( arg = 1; arg < argc - 1; arg ++ )
+ {
+ if ( !strcmp( argv[ arg ], "-bpp" ) )
+ bpp = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-type" ) )
+ this.type = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-w" ) )
+ this.w = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-h" ) )
+ this.h = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-bands" ) )
+ this.bands = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-rband" ) )
+ this.rband = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-hmirror" ) )
+ this.hmirror = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-vmirror" ) )
+ this.vmirror = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-dmirror" ) )
+ this.dmirror = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-offset" ) )
+ this.offset = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-invert" ) )
+ this.invert = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-flip" ) )
+ this.flip = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-flop" ) )
+ this.flop = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-pflip" ) )
+ this.pflip = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-pflop" ) )
+ this.pflop = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-quart" ) )
+ this.quart = atoi( argv[ ++ arg ] );
+ else if ( !strcmp( argv[ arg ], "-rotate" ) )
+ this.rotate = atoi( argv[ ++ arg ] );
+ else
+ fprintf( stderr, "ignoring %s\n", argv[ arg ] );
+ }
+
+ if ( bpp != 8 && bpp != 16 )
+ {
+ fprintf( stderr, "Invalid bpp %d\n", bpp );
+ return 1;
+ }
+
+ image = luma_render( &this );
+
+ if ( bpp == 16 )
+ {
+ uint16_t *end = image + this.w * this.h;
+ uint16_t *p = image;
+ uint8_t *q = ( uint8_t * )image;
+ while ( p < end )
+ {
+ *p ++ = ( *q << 8 ) + *( q + 1 );
+ q += 2;
+ }
+ printf( "P5\n" );
+ printf( "%d %d\n", this.w, this.h );
+ printf( "65535\n" );
+ fwrite( image, this.w * this.h * sizeof( uint16_t ), 1, stdout );
+ }
+ else
+ {
+ uint16_t *end = image + this.w * this.h;
+ uint16_t *p = image;
+ uint8_t *q = ( uint8_t * )image;
+ while ( p < end )
+ *q ++ = ( uint8_t )( *p ++ >> 8 );
+ printf( "P5\n" );
+ printf( "%d %d\n", this.w, this.h );
+ printf( "255\n" );
+ fwrite( image, this.w * this.h, 1, stdout );
+ }
+
+ return 0;
+}
+
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltmotion_est.so
+
+OBJS = factory.o \
+ filter_motion_est.o \
+ filter_crop_detect.o \
+ filter_autotrack_rectangle.o \
+ arrow_code.o \
+ filter_vismv.o \
+ producer_slowmotion.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+test: $(TARGET)
+ ~/mlt-devel/mlt/src/inigo/inigo -filter motion_est -filter vismv -filter benchmark -consumer sdl rescale=none real_time=0 audio_off=1 silent=1 /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=50000
+
+hist: $(TARGET)
+ ~/mlt-devel/mlt/src/inigo/inigo -filter motion_est -filter histogram -consumer sdl rescale=none real_time=0 audio_off=1 silent=1 /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=40000
+
+
+test2: $(TARGET)
+ inigo colour:black -filter watermark:"+mello.txt" composite.geometry="0,0:10%x10%;99=90%,90%" composite.out=99 -filter crop_detect -filter motion_est -filter vismv
+
+realtime: $(TARGET)
+ ~/mlt-devel/mlt/src/inigo/inigo -filter motion_est -filter vismv -consumer sdl rescale=none /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=30000
+
+testhist: $(TARGET)
+ ~/mlt-devel/mlt/src/inigo/inigo -consumer sdl rescale=none silent=1 -filter motion_est -filter histogram -filter vismv /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=10000
+
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+INTRO:
+
+This module is designed to provide application agnostic motion estimation.
+I wrote it from scratch because I found the other Open Source code to be
+limited by their difficulty of reuse.
+
+
+COMPILE:
+
+To compile this module, you must supply these options to the root configure script:
+
+--enable-gpl --enable-motion-est
+
+
+EXAMPLES:
+
+Estimate the motion:
+
+ > inigo -filter motion_est <movie_file>
+
+To display the motion vectors as pretty arrows:
+
+ > inigo -filter motion_est -filter vismv <movie_file>
+
+If your using a movie file that contains a crop, you will get better results with this:
+
+ > inigo -filter crop_detect -filter motion_est -filter vismv <movie_file>
+
+If your computer is unable to do the above examples in real time, try this:
+
+ > inigo -filter motion_est -filter vismv -consumer inigo real_time=0 <movie_file>
+
+If you'd like to see the motion vectors without the median denoising function, do this:
+
+ > inigo -filter motion_est denoise=0 -filter vismv <movie_file>
+
+To reconstruct each frame by applying the motion to the previous frame:
+
+ > inigo -filter motion_est show_reconstruction=1 <movie_file>
+
+To compare the reconstructed frame and the real frame (while paused):
+
+ > inigo -filter motion_est show_reconstruction=1 toggle_when_paused=1 <movie_file>
+
+To show the difference (residual) between the reconstructed frame the real frame:
+
+ > inigo -filter motion_est show_residual=1 <movie_file>
+
+To automatically track an object in the frame, try this:
+
+ > inigo -filter autotrack_rectangle:X,Y:WxH debug=1 <movie_file>
+
+(Where X,Y is the origin of the rectangle indexed from upper left and WxH is the dimensions of the rectangle.)
+
+To obscure that same object in the frame, try this:
+
+ > inigo -filter autotrack_rectangle:X,Y:WxH obscure=1 <movie_file>
+
+There is now a slow motion producer that does interpolation based on the motion vectors:
+
+ > inigo slowmotion:<movie_file> _speed=0.1 method=1 debug=1
+
+NOTES (and deficiencies):
+
+1. Ignore shot change detection when your using the autotrack_rectangle filter.
+
+2. Don't assume motion vectors displayed while stepping backwards and forward are that same vectors
+ that would be calculated while playing the footage from start to finish, nonstop. Stepping forward
+ should be fine after a few frames, however.
+
+3. SSE instructions are lazily assumed. MMX, Altivec, and SIMD-less would be good too.
+
+4. Motion estimation is only performed in the luma color space.
+
+5. Motion vectors should have sub-pixel accuracy.
+
+6. Motion vectors are not serializable yet.
+
+7. A diligent test suite is needed. (show_reconstruction & show_residual are a start)
+
+8. Multithreaded code will see HUGE benefits on multi-CPU systems. Donations of a multi-core cpu or a
+ multi-cpu system to the author will encourage development.
+
+9. Macroblock sizes are not dynamic (Though settable at runtime.)
+
+10. Notes (5), (7), and (9) would go a long ways to making this code suitable for a modern video encoder.
+
+11. Shot change works well but arbitrarily chosen thresholds need to be tuned.
+
+12. Given the documentation of other motion estimation code bases, I will GLADLY clarify and
+ document any piece of code upon request.
+
+13. Considerable effort has been put into the speed. I usually experience 10ms or less per frame for PAL on 2.8GHZ p4.
+
+Zachary Drew
+drew0054@tc.umn.edu
+
--- /dev/null
+/*
+ * /brief Draw arrows
+ * /author Zachary Drew, Copyright 2004
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_frame.h>
+#include "arrow_code.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define MIN(a,b) ((a) > (b) ? (b) : (a))
+
+#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+
+static int w;
+static int h;
+static int xstride;
+static int ystride;
+static mlt_image_format format;
+
+int init_arrows( mlt_image_format *image_format, int width, int height )
+{
+ w = width;
+ h = height;
+ format = *image_format;
+ switch( *image_format ) {
+ case mlt_image_yuv422:
+ xstride = 2;
+ ystride = xstride * w;
+ break;
+ default:
+ // I don't know
+ return 0;
+ }
+ return 1;
+
+}
+
+// ffmpeg borrowed
+static inline int clip(int a, int amin, int amax)
+{
+ if (a < amin)
+ return amin;
+ else if (a > amax)
+ return amax;
+ else
+ return a;
+}
+
+
+/**
+ * draws an line from (ex, ey) -> (sx, sy).
+ * Credits: modified from ffmpeg project
+ * @param ystride stride/linesize of the image
+ * @param xstride stride/element size of the image
+ * @param color color of the arrow
+ */
+void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey, int color)
+{
+ int t, x, y, fr, f;
+
+
+ sx= clip(sx, 0, w-1);
+ sy= clip(sy, 0, h-1);
+ ex= clip(ex, 0, w-1);
+ ey= clip(ey, 0, h-1);
+
+ buf[sy*ystride + sx*xstride]+= color;
+
+ if(ABS(ex - sx) > ABS(ey - sy)){
+ if(sx > ex){
+ t=sx; sx=ex; ex=t;
+ t=sy; sy=ey; ey=t;
+ }
+ buf+= sx*xstride + sy*ystride;
+ ex-= sx;
+ f= ((ey-sy)<<16)/ex;
+ for(x= 0; x <= ex; x++){
+ y = (x*f)>>16;
+ fr= (x*f)&0xFFFF;
+ buf[ y *ystride + x*xstride]+= (color*(0x10000-fr))>>16;
+ buf[(y+1)*ystride + x*xstride]+= (color* fr )>>16;
+ }
+ }else{
+ if(sy > ey){
+ t=sx; sx=ex; ex=t;
+ t=sy; sy=ey; ey=t;
+ }
+ buf+= sx*xstride + sy*ystride;
+ ey-= sy;
+ if(ey) f= ((ex-sx)<<16)/ey;
+ else f= 0;
+ for(y= 0; y <= ey; y++){
+ x = (y*f)>>16;
+ fr= (y*f)&0xFFFF;
+ buf[y*ystride + x *xstride]+= (color*(0x10000-fr))>>16;;
+ buf[y*ystride + (x+1)*xstride]+= (color* fr )>>16;;
+ }
+ }
+}
+
+void draw_rectangle_fill(uint8_t *buf, int x, int y, int w, int h, int color)
+{
+ int i,j;
+ for ( i = 0; i < w; i++ )
+ for ( j = 0; j < h; j++ )
+ buf[ (y+j)*ystride + (x+i)*xstride] = color;
+}
+
+void draw_rectangle_outline(uint8_t *buf, int x, int y, int w, int h, int color)
+{
+ int i,j;
+ for ( i = 0; i < w; i++ ) {
+ buf[ y*ystride + (x+i)*xstride ] += color;
+ buf[ (y+h)*ystride + (x+i)*xstride ] += color;
+ }
+ for ( j = 1; j < h+1; j++ ) {
+ buf[ (y+j)*ystride + x*xstride ] += color;
+ buf[ (y+j)*ystride + (x+w)*xstride ] += color;
+ }
+}
+/**
+ * draws an arrow from (ex, ey) -> (sx, sy).
+ * Credits: modified from ffmpeg project
+ * @param stride stride/linesize of the image
+ * @param color color of the arrow
+ */
+void draw_arrow(uint8_t *buf, int sx, int sy, int ex, int ey, int color){
+
+ int dx,dy;
+ dx= ex - sx;
+ dy= ey - sy;
+
+ if(dx*dx + dy*dy > 3*3){
+ int rx= dx + dy;
+ int ry= -dx + dy;
+ int length= sqrt((rx*rx + ry*ry)<<8);
+
+ rx= ROUNDED_DIV(rx*3<<4, length);
+ ry= ROUNDED_DIV(ry*3<<4, length);
+
+ draw_line(buf, sx, sy, sx + rx, sy + ry, color);
+ draw_line(buf, sx, sy, sx - ry, sy + rx, color);
+ }
+ draw_line(buf, sx, sy, ex, ey, color);
+}
--- /dev/null
+/**
+ * file: arrow_code.h
+ *
+ * /brief Misc functions to draw arrows
+ * /author Zachary Drew, Copyright 2004
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+extern int init_arrows( mlt_image_format *image_format, int width, int height );
+extern void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey, int color);
+extern void draw_arrow(uint8_t *buf, int sx, int sy, int ex, int ey, int color);
+extern void draw_rectangle_fill(uint8_t *buf, int x, int y, int w, int h, int color);
+extern void draw_rectangle_outline(uint8_t *buf, int x, int y, int w, int h, int color);
--- /dev/null
+/*
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_motion_est_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_vismv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_crop_detect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_autotrack_rectangle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_slowmotion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "motion_est", filter_motion_est_init );
+ MLT_REGISTER( filter_type, "vismv", filter_vismv_init );
+ MLT_REGISTER( filter_type, "crop_detect", filter_crop_detect_init );
+ MLT_REGISTER( filter_type, "autotrack_rectangle", filter_autotrack_rectangle_init );
+ MLT_REGISTER( producer_type, "slowmotion", producer_slowmotion_init );
+}
--- /dev/null
+/*
+ * filter_autotrack_rectangle.c
+ *
+ * /brief
+ * /author Zachary Drew, Copyright 2005
+ *
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include "filter_motion_est.h"
+#include "arrow_code.h"
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define MIN(a,b) ((a) > (b) ? (b) : (a))
+
+#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+void caculate_motion( struct motion_vector_s *vectors,
+ mlt_geometry_item boundry,
+ int macroblock_width,
+ int macroblock_height,
+ int mv_buffer_width,
+ int method,
+ int width,
+ int height )
+{
+
+
+ // translate pixel units (from bounds) to macroblock units
+ // make sure whole macroblock stay within bounds
+ int left_mb = ( boundry->x + macroblock_width - 1 ) / macroblock_width;
+ int top_mb = ( boundry->y + macroblock_height - 1 ) / macroblock_height;
+ int right_mb = ( boundry->x + boundry->w ) / macroblock_width - 1;
+ int bottom_mb = ( boundry->y + boundry->h ) / macroblock_height - 1;
+
+ int i, j, n = 0;
+
+ int average_x = 0, average_y = 0;
+
+ #define CURRENT ( vectors + j*mv_buffer_width + i )
+
+ for( i = left_mb; i <= right_mb; i++ ){
+ for( j = top_mb; j <= bottom_mb; j++ )
+ {
+ n++;
+ average_x += CURRENT->dx;
+ average_y += CURRENT->dy;
+ }
+ }
+
+ if ( n == 0 ) return;
+
+ average_x /= n;
+ average_y /= n;
+
+ n = 0;
+ int average2_x = 0, average2_y = 0;
+ for( i = left_mb; i <= right_mb; i++ ){
+ for( j = top_mb; j <= bottom_mb; j++ ){
+
+ if( ABS(CURRENT->dx - average_x) < 3 &&
+ ABS(CURRENT->dy - average_y) < 3 )
+ {
+ n++;
+ average2_x += CURRENT->dx;
+ average2_y += CURRENT->dy;
+ }
+ }
+ }
+
+ if ( n == 0 ) return;
+
+ boundry->x -= (double)average2_x / (double)n;
+ boundry->y -= (double)average2_y / (double)n;
+
+ if ( boundry->x < 0 )
+ boundry->x = 0;
+
+ if ( boundry->y < 0 )
+ boundry->y = 0;
+
+ if ( boundry->x + boundry->w > width )
+ boundry->x = width - boundry->w;
+
+ if ( boundry->y + boundry->h > height )
+ boundry->y = height - boundry->h;
+}
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ // Get the filter object
+ mlt_filter filter = mlt_frame_pop_service( frame );
+
+ // Get the filter's property object
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
+
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
+
+ // Get the frame position
+ mlt_position position = mlt_frame_get_position( frame );
+
+ // Get the new image
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ if( error != 0 )
+ mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr );
+
+ // Get the geometry object
+ mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
+
+ // Get the current geometry item
+ struct mlt_geometry_item_s boundry;
+ mlt_geometry_fetch(geometry, &boundry, position);
+
+ // Get the motion vectors
+ struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL );
+
+ // How did the rectangle move?
+ if( vectors != NULL &&
+ boundry.key != 1 ) // Paused?
+ {
+
+ int method = mlt_properties_get_int( filter_properties, "method" );
+
+ // Get the size of macroblocks in pixel units
+ int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" );
+ int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" );
+ int mv_buffer_width = *width / macroblock_width;
+
+ caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method, *width, *height );
+
+
+ // Make the geometry object a real boy
+ boundry.key = 1;
+ boundry.f[0] = 1;
+ boundry.f[1] = 1;
+ boundry.f[2] = 1;
+ boundry.f[3] = 1;
+ boundry.f[4] = 1;
+ mlt_geometry_insert(geometry, &boundry);
+ }
+
+ if( mlt_properties_get_int( filter_properties, "debug" ) == 1 )
+ {
+ init_arrows( format, *width, *height );
+ draw_rectangle_outline(*image, boundry.x, boundry.y, boundry.w, boundry.h, 100);
+ }
+
+ if( mlt_properties_get_int( filter_properties, "obscure" ) == 1 )
+ {
+ mlt_filter obscure = mlt_properties_get_data( filter_properties, "_obscure", NULL );
+
+ mlt_properties_pass_list( MLT_FILTER_PROPERTIES(obscure), filter_properties, "in, out");
+
+ // Because filter_obscure needs to be rewritten to use mlt_geometry
+ char geom[100];
+ sprintf( geom, "%d,%d:%dx%d", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "start", geom );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "end", geom );
+ }
+
+ if( mlt_properties_get_int( filter_properties, "collect" ) == 1 )
+ {
+ printf( "%d,%d,%d,%d\n", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
+ fflush( stdout );
+ }
+
+ return error;
+}
+
+static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the filter object
+ mlt_filter filter = mlt_frame_pop_service( frame );
+
+ // Get the filter's property object
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
+
+ // Get the frame properties
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
+
+ // Get the frame position
+ mlt_position position = mlt_frame_get_position( frame );
+
+ // Get the geometry object
+ mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
+ if (geometry == NULL) {
+ mlt_geometry geom = mlt_geometry_init();
+ char *arg = mlt_properties_get(filter_properties, "geometry");
+
+ // Initialize with the supplied geometry
+ struct mlt_geometry_item_s item;
+ mlt_geometry_parse_item( geom, &item, arg );
+
+ item.frame = 0;
+ item.key = 1;
+ item.mix = 100;
+
+ mlt_geometry_insert( geom, &item );
+ mlt_properties_set_data( filter_properties, "filter_geometry", geom, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
+ geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
+ }
+
+ // Get the current geometry item
+ mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
+ mlt_geometry_fetch(geometry, geometry_item, position);
+
+ mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
+
+ // Get the new image
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ if( error != 0 )
+ mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ /* modify the frame with the current geometry */
+ mlt_frame_push_service( frame, this);
+ mlt_frame_push_get_image( frame, attach_boundry_to_frame );
+
+
+
+ /* apply the motion estimation filter */
+ mlt_filter motion_est = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_motion_est", NULL );
+ mlt_filter_process( motion_est, frame);
+
+
+
+ /* calculate the new geometry based on the motion */
+ mlt_frame_push_service( frame, this);
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+
+ /* visualize the motion vectors */
+ if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
+ {
+ mlt_filter vismv = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_vismv", NULL );
+ if( vismv == NULL )
+ {
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ vismv = mlt_factory_filter( profile, "vismv", NULL );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
+ }
+
+ mlt_filter_process( vismv, frame );
+ }
+
+ if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "obscure" ) == 1 )
+ {
+ mlt_filter obscure = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_obscure", NULL );
+ if( obscure == NULL )
+ {
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ obscure = mlt_factory_filter( profile, "obscure", NULL );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_obscure", obscure, 0, (mlt_destructor)mlt_filter_close, NULL );
+ }
+
+ mlt_filter_process( obscure, frame );
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+
+mlt_filter filter_autotrack_rectangle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+
+ // Initialize with the supplied geometry if ther is one
+ if( arg != NULL )
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", arg );
+ else
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", "100,100:100x100" );
+
+ // create an instance of the motion_est and obscure filter
+ mlt_filter motion_est = mlt_factory_filter( profile, "motion_est", NULL );
+ if( motion_est != NULL )
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_motion_est", motion_est, 0, (mlt_destructor)mlt_filter_close, NULL );
+ else {
+ mlt_filter_close( this );
+ return NULL;
+ }
+
+
+ }
+
+ return this;
+}
+
+/** This source code will self destruct in 5...4...3...
+*/
--- /dev/null
+/**
+ * /brief Crop Detection filter
+ *
+ * /author Zachary Drew, Copyright 2005
+ *
+ * inspired by mplayer's cropdetect filter
+ *
+ * Note: The goemetry generated is zero-indexed and is inclusive of the end values
+ *
+ * Options:
+ * -filter crop_detect debug=1 // Visualize crop
+ * -filter crop_detect frequency=25 // Detect the crop once a second
+ * -filter crop_detect frequency=0 // Never detect unless the producer changes
+ * -filter crop_detect thresh=100 // Changes the threshold (default = 25)
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#define DEBUG
+#define DEFAULT_THRESH 20
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "arrow_code.h"
+
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ // Get the filter object and properties
+ mlt_filter filter = mlt_frame_pop_service( this );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the new image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if( error != 0 ) {
+ mlt_properties_debug( MLT_FRAME_PROPERTIES(this), "error after mlt_frame_get_image()", stderr );
+ return error;
+ }
+
+ // Parameter that describes how often to check for the crop
+ int frequency = mlt_properties_get_int( properties, "frequency");
+
+ // Producers may start with blank footage, by default we will skip, oh, 5 frames unless overridden
+ int skip = mlt_properties_get_int( properties, "skip");
+
+ // The result
+ mlt_geometry_item bounds = mlt_properties_get_data( properties, "bounds", NULL );
+
+ // Initialize if needed
+ if( bounds == NULL ) {
+ bounds = calloc( 1, sizeof( struct mlt_geometry_item_s ) );
+ bounds->w = *width;
+ bounds->h = *height;
+ mlt_properties_set_data( properties, "bounds", bounds, sizeof( struct mlt_geometry_item_s ), free, NULL );
+ }
+
+ // For periodic detection (with offset of 'skip')
+ if( frequency == 0 || (int)(mlt_frame_get_position(this)+skip) % frequency != 0)
+ {
+ // Inject in stream
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES(this), "bounds", bounds, sizeof( struct mlt_geometry_item_s ), NULL, NULL );
+
+ return 0;
+ }
+
+
+ // There is no way to detect a crop for sure, so make up an arbitrary one
+ int thresh = mlt_properties_get_int( properties, "thresh" );
+
+ int xstride, ystride;
+
+ switch( *format ) {
+ case mlt_image_yuv422:
+ xstride = 2;
+ ystride = 2 * *width;
+ break;
+ default:
+ fprintf(stderr, "image format not supported by filter_crop_detect\n");
+ return -1;
+ }
+
+ int x, y, average_brightness, deviation; // Scratch variables
+ uint8_t *q;
+
+ // Top crop
+ for( y = 0; y < *height/2; y++ ) {
+ bounds->y = y;
+ average_brightness = 0;
+ deviation = 0;
+ q = *image + y*ystride;
+ for( x = 0; x < *width; x++ )
+ average_brightness += q[x*xstride];
+
+ average_brightness /= *width;
+
+ for( x = 0; x < *width; x++ )
+ deviation += abs(average_brightness - q[x*xstride]);
+
+ if( deviation*10 >= thresh * *width )
+ break;
+ }
+
+ // Bottom crop
+ for( y = *height - 1; y >= *height/2; y-- ) {
+ bounds->h = y;
+ average_brightness = 0;
+ deviation = 0;
+ q = *image + y*ystride;
+ for( x = 0; x < *width; x++ )
+ average_brightness += q[x*xstride];
+
+ average_brightness /= *width;
+
+ for( x = 0; x < *width; x++ )
+ deviation += abs(average_brightness - q[x*xstride]);
+
+ if( deviation*10 >= thresh * *width)
+ break;
+ }
+
+ // Left crop
+ for( x = 0; x < *width/2; x++ ) {
+ bounds->x = x;
+ average_brightness = 0;
+ deviation = 0;
+ q = *image + x*xstride;
+ for( y = 0; y < *height; y++ )
+ average_brightness += q[y*ystride];
+
+ average_brightness /= *height;
+
+ for( y = 0; y < *height; y++ )
+ deviation += abs(average_brightness - q[y*ystride]);
+
+ if( deviation*10 >= thresh * *width )
+ break;
+ }
+
+ // Right crop
+ for( x = *width - 1; x >= *width/2; x-- ) {
+ bounds->w = x;
+ average_brightness = 0;
+ deviation = 0;
+ q = *image + x*xstride;
+ for( y = 0; y < *height; y++ )
+ average_brightness += q[y*ystride];
+
+ average_brightness /= *height;
+
+ for( y = 0; y < *height; y++ )
+ deviation += abs(average_brightness - q[y*ystride]);
+
+ if( deviation*10 >= thresh * *width )
+ break;
+ }
+
+ /* Debug: Draw arrows to show crop */
+ if( mlt_properties_get_int( properties, "debug") == 1 )
+ {
+ init_arrows( format, *width, *height );
+
+ draw_arrow(*image, bounds->x, *height/2, bounds->x+50, *height/2, 100);
+ draw_arrow(*image, *width/2, bounds->y, *width/2, bounds->y+50, 100);
+ draw_arrow(*image, bounds->w, *height/2, bounds->w-50, *height/2, 100);
+ draw_arrow(*image, *width/2, bounds->h, *width/2, bounds->h-50, 100);
+ draw_arrow(*image, bounds->x, bounds->y, bounds->x+40, bounds->y+30, 100);
+ draw_arrow(*image, bounds->x, bounds->h, bounds->x+40, bounds->h-30, 100);
+ draw_arrow(*image, bounds->w, bounds->y, bounds->w-40, bounds->y+30, 100);
+ draw_arrow(*image, bounds->w, bounds->h, bounds->w-40, bounds->h-30, 100);
+ }
+
+ // Convert to width and correct indexing
+ bounds->w -= bounds->x - 1;
+ bounds->h -= bounds->y - 1;
+
+ if( mlt_properties_get_int( properties, "debug") == 1 )
+ fprintf(stderr, "Top:%f Left:%f Width:%f Height:%f\n", bounds->y, bounds->x, bounds->w, bounds->h);
+
+ /* inject into frame */
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES(this), "bounds", bounds, sizeof( struct mlt_geometry_item_s ), NULL, NULL );
+
+ return error;
+}
+
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ // Put the filter object somewhere we can find it
+ mlt_frame_push_service( frame, this);
+
+ // Push the frame filter
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+mlt_filter filter_crop_detect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+
+ /* defaults */
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "frequency", 1);
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "thresh", 5);
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "clip", 5);
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "former_producer_id", -1);
+
+ }
+
+ return this;
+}
+
+/** This source code will self destruct in 5...4...3...
+*/
+
--- /dev/null
+/*
+ * /brief fast motion estimation filter
+ * /author Zachary Drew, Copyright 2005
+ *
+ * Currently only uses Gamma data for comparisonon (bug or feature?)
+ * SSE optimized where available.
+ *
+ * Vector orientation: The vector data that is generated for the current frame specifies
+ * the motion from the previous frame to the current frame. To know how a macroblock
+ * in the current frame will move in the future, the next frame is needed.
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+
+#include "filter_motion_est.h"
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#ifdef USE_SSE
+#include "sad_sse.h"
+#endif
+
+#define NDEBUG
+#include <assert.h>
+
+#undef DEBUG
+#undef DEBUG_ASM
+#undef BENCHMARK
+#undef COUNT_COMPARES
+
+#define DIAMOND_SEARCH 0x0
+#define FULL_SEARCH 0x1
+#define SHIFT 8
+#define MIN(a,b) ((a) > (b) ? (b) : (a))
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+
+struct motion_est_context_s
+{
+ int initialized; // true if filter has been initialized
+
+#ifdef COUNT_COMPARES
+ int compares;
+#endif
+
+ /* same as mlt_frame's parameters */
+ int width, height;
+
+ /* Operational details */
+ int mb_w, mb_h;
+ int xstride, ystride;
+ uint8_t *cache_image; // Copy of current frame
+ uint8_t *former_image; // Copy of former frame
+ int search_method;
+ int skip_prediction;
+ int shot_change;
+ int limit_x, limit_y; // max x and y of a motion vector
+ int initial_thresh;
+ int check_chroma; // if check_chroma == 1 then compare chroma
+ int denoise;
+ int previous_msad;
+ int show_reconstruction;
+ int toggle_when_paused;
+ int show_residual;
+
+ /* bounds */
+ struct mlt_geometry_item_s bounds; // Current bounds (from filters crop_detect, autotrack rectangle, or other)
+
+ /* bounds in macroblock units; macroblocks are completely contained within the boundry */
+ int left_mb, prev_left_mb, right_mb, prev_right_mb;
+ int top_mb, prev_top_mb, bottom_mb, prev_bottom_mb;
+
+ /* size of our vector buffers */
+ int mv_buffer_height, mv_buffer_width, mv_size;
+
+ /* vector buffers */
+ int former_vectors_valid; //<! true if the previous frame's buffered motion vector data is valid
+ motion_vector *former_vectors;
+ motion_vector *current_vectors;
+ motion_vector *denoise_vectors;
+ mlt_position former_frame_position, current_frame_position;
+
+ /* diagnostic metrics */
+ float predictive_misses; // How often do the prediction motion vectors fail?
+ int comparison_average; // How far does the best estimation deviate from a perfect comparison?
+ int bad_comparisons;
+ int average_length;
+ int average_x, average_y;
+
+ /* run-time configurable comparison functions */
+ int (*compare_reference)(uint8_t *, uint8_t *, int, int, int, int);
+ int (*compare_optimized)(uint8_t *, uint8_t *, int, int, int, int);
+
+};
+
+// This is used to constrains pixel operations between two blocks to be within the image boundry
+inline static int constrain( int *x, int *y, int *w, int *h,
+ const int dx, const int dy,
+ const int left, const int right,
+ const int top, const int bottom)
+{
+ uint32_t penalty = 1 << SHIFT; // Retain a few extra bits of precision
+ int x2 = *x + dx;
+ int y2 = *y + dy;
+ int w_remains = *w;
+ int h_remains = *h;
+
+ // Origin of macroblock moves left of image boundy
+ if( *x < left || x2 < left ) {
+ w_remains = *w - left + ((*x < x2) ? *x : x2);
+ *x += *w - w_remains;
+ }
+ // Portion of macroblock moves right of image boundry
+ else if( *x + *w > right || x2 + *w > right )
+ w_remains = right - ((*x > x2) ? *x : x2);
+
+ // Origin of macroblock moves above image boundy
+ if( *y < top || y2 < top ) {
+ h_remains = *h - top + ((*y < y2) ? *y : y2);
+ *y += *h - h_remains;
+ }
+ // Portion of macroblock moves bellow image boundry
+ else if( *y + *h > bottom || y2 + *h > bottom )
+ h_remains = bottom - ((*y > y2) ? *y : y2);
+
+ if( w_remains == *w && h_remains == *h ) return penalty;
+ if( w_remains <= 0 || h_remains <= 0) return 0; // Block is clipped out of existance
+ penalty = (*w * *h * penalty)
+ / ( w_remains * h_remains); // Recipricol of the fraction of the block that remains
+
+ assert(*x >= left); assert(x2 + *w - w_remains >= left);
+ assert(*y >= top); assert(y2 + *h - h_remains >= top);
+ assert(*x + w_remains <= right); assert(x2 + w_remains <= right);
+ assert(*y + h_remains <= bottom); assert(y2 + h_remains <= bottom);
+
+ *w = w_remains; // Update the width and height
+ *h = h_remains;
+
+ return penalty;
+}
+
+/** /brief Reference Sum of Absolute Differences comparison function
+*
+*/
+static int sad_reference( uint8_t *block1, uint8_t *block2, const int xstride, const int ystride, const int w, const int h )
+{
+ int i, j, score = 0;
+ for ( j = 0; j < h; j++ ){
+ for ( i = 0; i < w; i++ ){
+ score += ABS( block1[i*xstride] - block2[i*xstride] );
+ }
+ block1 += ystride;
+ block2 += ystride;
+ }
+
+ return score;
+}
+
+
+/** /brief Abstracted block comparison function
+*/
+inline static int block_compare( uint8_t *block1,
+ uint8_t *block2,
+ int x,
+ int y,
+ int dx,
+ int dy,
+ struct motion_est_context_s *c)
+{
+
+#ifdef COUNT_COMPARES
+ c->compares++;
+#endif
+
+ int score;
+
+ // Default comparison may be overridden by the slower, more capable reference comparison
+ int (*cmp)(uint8_t *, uint8_t *, int, int, int, int) = c->compare_optimized;
+
+ // vector displacement limited has been exceeded
+ if( ABS( dx ) >= c->limit_x || ABS( dy ) >= c->limit_y )
+ return MAX_MSAD;
+
+ int mb_w = c->mb_w; // Some writeable local copies
+ int mb_h = c->mb_h;
+
+ // Determine if either macroblock got clipped
+ int penalty = constrain( &x, &y, &mb_w, &mb_h, dx, dy, 0, c->width, 0, c->height);
+
+ // Some gotchas
+ if( penalty == 0 ) // Clipped out of existance: Return worst score
+ return MAX_MSAD;
+ else if( penalty != 1<<SHIFT ) // Nonstandard macroblock dimensions: Disable SIMD optimizizations.
+ cmp = c->compare_reference;
+
+ // Calculate the memory locations of the macroblocks
+ block1 += x * c->xstride + y * c->ystride;
+ block2 += (x+dx) * c->xstride + (y+dy) * c->ystride;
+
+ #ifdef DEBUG_ASM
+ if( penalty == 1<<SHIFT ){
+ score = c->compare_reference( block1, block2, c->xstride, c->ystride, mb_w, mb_h );
+ int score2 = c->compare_optimized( block1, block2, c->xstride, c->ystride, mb_w, mb_h );
+ if ( score != score2 )
+ fprintf(stderr, "Your assembly doesn't work! Reference: %d Asm: %d\n", score, score2);
+ }
+ else
+ #endif
+
+ score = cmp( block1, block2, c->xstride, c->ystride, mb_w, mb_h );
+
+ return ( score * penalty ) >> SHIFT; // Ditch the extra precision
+}
+
+static inline void check_candidates ( uint8_t *ref,
+ uint8_t *candidate_base,
+ const int x,
+ const int y,
+ const motion_vector *candidates,// Contains to_x & to_y
+ const int count, // Number of candidates
+ const int unique, // Sometimes we know the candidates are unique
+ motion_vector *result,
+ struct motion_est_context_s *c )
+{
+ int score, i, j;
+ /* Scan for the best candidate */
+ for ( i = 0; i < count; i++ )
+ {
+ // this little dohicky ignores duplicate candidates, if they are possible
+ if ( unique == 0 ) {
+ j = 0;
+ while ( j < i )
+ {
+ if ( candidates[j].dx == candidates[i].dx &&
+ candidates[j].dy == candidates[i].dy )
+ goto next_for_loop;
+
+ j++;
+ }
+ }
+
+ // Luma
+ score = block_compare( ref, candidate_base,
+ x, y,
+ candidates[i].dx, // from
+ candidates[i].dy,
+ c);
+
+ if ( score < result->msad ) { // New minimum
+ result->dx = candidates[i].dx;
+ result->dy = candidates[i].dy;
+ result->msad = score;
+ }
+ next_for_loop:;
+ }
+}
+
+/* /brief Diamond search
+* Operates on a single macroblock
+*/
+static inline void diamond_search(
+ uint8_t *ref, //<! Image data from previous frame
+ uint8_t *candidate_base, //<! Image data in current frame
+ const int x, //<! X upper left corner of macroblock
+ const int y, //<! U upper left corner of macroblock
+ struct motion_vector_s *result, //<! Best predicted mv and eventual result
+ struct motion_est_context_s *c) //<! motion estimation context
+{
+
+ // diamond search pattern
+ motion_vector candidates[4];
+
+ // Keep track of best and former best candidates
+ motion_vector best, former;
+ best.dx = 0;
+ best.dy = 0;
+ former.dx = 0;
+ former.dy = 0;
+
+ // The direction of the refinement needs to be known
+ motion_vector current;
+
+ int i, first = 1;
+
+ // Loop through the search pattern
+ while( 1 ) {
+
+ current.dx = result->dx;
+ current.dy = result->dy;
+
+ if ( first == 1 ) // Set the initial pattern
+ {
+ candidates[0].dx = result->dx + 1; candidates[0].dy = result->dy + 0;
+ candidates[1].dx = result->dx + 0; candidates[1].dy = result->dy + 1;
+ candidates[2].dx = result->dx - 1; candidates[2].dy = result->dy + 0;
+ candidates[3].dx = result->dx + 0; candidates[3].dy = result->dy - 1;
+ i = 4;
+ }
+ else // Construct the next portion of the search pattern
+ {
+ candidates[0].dx = result->dx + best.dx;
+ candidates[0].dy = result->dy + best.dy;
+ if (best.dx == former.dx && best.dy == former.dy) {
+ candidates[1].dx = result->dx + best.dy;
+ candidates[1].dy = result->dy + best.dx; // Yes, the wires
+ candidates[2].dx = result->dx - best.dy; // are crossed
+ candidates[2].dy = result->dy - best.dx;
+ i = 3;
+ } else {
+ candidates[1].dx = result->dx + former.dx;
+ candidates[1].dy = result->dy + former.dy;
+ i = 2;
+ }
+
+ former.dx = best.dx; former.dy = best.dy; // Keep track of new former best
+ }
+
+ check_candidates ( ref, candidate_base, x, y, candidates, i, 1, result, c );
+
+ // Which candidate was the best?
+ best.dx = result->dx - current.dx;
+ best.dy = result->dy - current.dy;
+
+ // A better canidate was not found
+ if ( best.dx == 0 && best.dy == 0 )
+ return;
+
+ if ( first == 1 ){
+ first = 0;
+ former.dx = best.dx; former.dy = best.dy; // First iteration, sensible value for former.d*
+ }
+ }
+}
+
+/* /brief Full (brute) search
+* Operates on a single macroblock
+*/
+__attribute__((used))
+static void full_search(
+ uint8_t *ref, //<! Image data from previous frame
+ uint8_t *candidate_base, //<! Image data in current frame
+ int x, //<! X upper left corner of macroblock
+ int y, //<! U upper left corner of macroblock
+ struct motion_vector_s *result, //<! Best predicted mv and eventual result
+ struct motion_est_context_s *c) //<! motion estimation context
+{
+ // Keep track of best candidate
+ int i,j,score;
+
+ // Go loopy
+ for( i = -c->mb_w; i <= c->mb_w; i++ ){
+ for( j = -c->mb_h; j <= c->mb_h; j++ ){
+
+ score = block_compare( ref, candidate_base,
+ x,
+ y,
+ x + i,
+ y + j,
+ c);
+
+ if ( score < result->msad ) {
+ result->dx = i;
+ result->dy = j;
+ result->msad = score;
+ }
+ }
+ }
+}
+
+// Macros for pointer calculations
+#define CURRENT(i,j) ( c->current_vectors + (j)*c->mv_buffer_width + (i) )
+#define FORMER(i,j) ( c->former_vectors + (j)*c->mv_buffer_width + (i) )
+#define DENOISE(i,j) ( c->denoise_vectors + (j)*c->mv_buffer_width + (i) )
+
+int ncompare (const void * a, const void * b)
+{
+ return ( *(const int*)a - *(const int*)b );
+}
+
+// motion vector denoising
+// for x and y components seperately,
+// change the vector to be the median value of the 9 adjacent vectors
+static void median_denoise( motion_vector *v, struct motion_est_context_s *c )
+{
+ int xvalues[9], yvalues[9];
+
+ int i,j,n;
+ for( j = c->top_mb; j <= c->bottom_mb; j++ )
+ for( i = c->left_mb; i <= c->right_mb; i++ ){
+ {
+ n = 0;
+
+ xvalues[n ] = CURRENT(i,j)->dx; // Center
+ yvalues[n++] = CURRENT(i,j)->dy;
+
+ if( i > c->left_mb ) // Not in First Column
+ {
+ xvalues[n ] = CURRENT(i-1,j)->dx; // Left
+ yvalues[n++] = CURRENT(i-1,j)->dy;
+
+ if( j > c->top_mb ) {
+ xvalues[n ] = CURRENT(i-1,j-1)->dx; // Upper Left
+ yvalues[n++] = CURRENT(i-1,j-1)->dy;
+ }
+
+ if( j < c->bottom_mb ) {
+ xvalues[n ] = CURRENT(i-1,j+1)->dx; // Bottom Left
+ yvalues[n++] = CURRENT(i-1,j+1)->dy;
+ }
+ }
+ if( i < c->right_mb ) // Not in Last Column
+ {
+ xvalues[n ] = CURRENT(i+1,j)->dx; // Right
+ yvalues[n++] = CURRENT(i+1,j)->dy;
+
+
+ if( j > c->top_mb ) {
+ xvalues[n ] = CURRENT(i+1,j-1)->dx; // Upper Right
+ yvalues[n++] = CURRENT(i+1,j-1)->dy;
+ }
+
+ if( j < c->bottom_mb ) {
+ xvalues[n ] = CURRENT(i+1,j+1)->dx; // Bottom Right
+ yvalues[n++] = CURRENT(i+1,j+1)->dy;
+ }
+ }
+ if( j > c->top_mb ) // Not in First Row
+ {
+ xvalues[n ] = CURRENT(i,j-1)->dx; // Top
+ yvalues[n++] = CURRENT(i,j-1)->dy;
+ }
+
+ if( j < c->bottom_mb ) // Not in Last Row
+ {
+ xvalues[n ] = CURRENT(i,j+1)->dx; // Bottom
+ yvalues[n++] = CURRENT(i,j+1)->dy;
+ }
+
+ qsort (xvalues, n, sizeof(int), ncompare);
+ qsort (yvalues, n, sizeof(int), ncompare);
+
+ if( n % 2 == 1 ) {
+ DENOISE(i,j)->dx = xvalues[n/2];
+ DENOISE(i,j)->dy = yvalues[n/2];
+ }
+ else {
+ DENOISE(i,j)->dx = (xvalues[n/2] + xvalues[n/2+1])/2;
+ DENOISE(i,j)->dy = (yvalues[n/2] + yvalues[n/2+1])/2;
+ }
+ }
+ }
+
+ motion_vector *t = c->current_vectors;
+ c->current_vectors = c->denoise_vectors;
+ c->denoise_vectors = t;
+
+}
+
+// Credits: ffmpeg
+// return the median
+static inline int median_predictor(int a, int b, int c) {
+ if ( a > b ){
+ if ( c > b ){
+ if ( c > a ) b = a;
+ else b = c;
+ }
+ } else {
+ if ( b > c ){
+ if ( c > a ) b = c;
+ else b = a;
+ }
+ }
+ return b;
+}
+
+
+/** /brief Motion search
+*
+* For each macroblock in the current frame, estimate the block from the last frame that
+* matches best.
+*
+* Vocab: Colocated - the pixel in the previous frame at the current position
+*
+* Based on enhanced predictive zonal search. [Tourapis 2002]
+*/
+static void motion_search( uint8_t *from, //<! Image data.
+ uint8_t *to, //<! Image data. Rigid grid.
+ struct motion_est_context_s *c) //<! The context
+{
+
+#ifdef COUNT_COMPARES
+ compares = 0;
+#endif
+
+ motion_vector candidates[10];
+ motion_vector *here; // This one gets used alot (about 30 times per macroblock)
+ int n = 0;
+
+ int i, j, count=0;
+
+ // For every macroblock, perform motion vector estimation
+ for( i = c->left_mb; i <= c->right_mb; i++ ){
+ for( j = c->top_mb; j <= c->bottom_mb; j++ ){
+
+ here = CURRENT(i,j);
+ here->valid = 1;
+ here->color = 100;
+ here->msad = MAX_MSAD;
+ count++;
+ n = 0;
+
+
+ /* Stack the predictors [i.e. checked in reverse order] */
+
+ /* Adjacent to collocated */
+ if( c->former_vectors_valid )
+ {
+ // Top of colocated
+ if( j > c->prev_top_mb ){// && COL_TOP->valid ){
+ candidates[n ].dx = FORMER(i,j-1)->dx;
+ candidates[n++].dy = FORMER(i,j-1)->dy;
+ }
+
+ // Left of colocated
+ if( i > c->prev_left_mb ){// && COL_LEFT->valid ){
+ candidates[n ].dx = FORMER(i-1,j)->dx;
+ candidates[n++].dy = FORMER(i-1,j)->dy;
+ }
+
+ // Right of colocated
+ if( i < c->prev_right_mb ){// && COL_RIGHT->valid ){
+ candidates[n ].dx = FORMER(i+1,j)->dx;
+ candidates[n++].dy = FORMER(i+1,j)->dy;
+ }
+
+ // Bottom of colocated
+ if( j < c->prev_bottom_mb ){// && COL_BOTTOM->valid ){
+ candidates[n ].dx = FORMER(i,j+1)->dx;
+ candidates[n++].dy = FORMER(i,j+1)->dy;
+ }
+
+ // And finally, colocated
+ candidates[n ].dx = FORMER(i,j)->dx;
+ candidates[n++].dy = FORMER(i,j)->dy;
+ }
+
+ // For macroblocks not in the top row
+ if ( j > c->top_mb) {
+
+ // Top if ( TOP->valid ) {
+ candidates[n ].dx = CURRENT(i,j-1)->dx;
+ candidates[n++].dy = CURRENT(i,j-1)->dy;
+ //}
+
+ // Top-Right, macroblocks not in the right row
+ if ( i < c->right_mb ){// && TOP_RIGHT->valid ) {
+ candidates[n ].dx = CURRENT(i+1,j-1)->dx;
+ candidates[n++].dy = CURRENT(i+1,j-1)->dy;
+ }
+ }
+
+ // Left, Macroblocks not in the left column
+ if ( i > c->left_mb ){// && LEFT->valid ) {
+ candidates[n ].dx = CURRENT(i-1,j)->dx;
+ candidates[n++].dy = CURRENT(i-1,j)->dy;
+ }
+
+ /* Median predictor vector (median of left, top, and top right adjacent vectors) */
+ if ( i > c->left_mb && j > c->top_mb && i < c->right_mb
+ )//&& LEFT->valid && TOP->valid && TOP_RIGHT->valid )
+ {
+ candidates[n ].dx = median_predictor( CURRENT(i-1,j)->dx, CURRENT(i,j-1)->dx, CURRENT(i+1,j-1)->dx);
+ candidates[n++].dy = median_predictor( CURRENT(i-1,j)->dy, CURRENT(i,j-1)->dy, CURRENT(i+1,j-1)->dy);
+ }
+
+ // Zero vector
+ candidates[n ].dx = 0;
+ candidates[n++].dy = 0;
+
+ int x = i * c->mb_w;
+ int y = j * c->mb_h;
+ check_candidates ( to, from, x, y, candidates, n, 0, here, c );
+
+
+#ifndef FULLSEARCH
+ diamond_search( to, from, x, y, here, c);
+#else
+ full_search( to, from, x, y, here, c);
+#endif
+
+ assert( x + c->mb_w + here->dx > 0 ); // All macroblocks must have area > 0
+ assert( y + c->mb_h + here->dy > 0 );
+ assert( x + here->dx < c->width );
+ assert( y + here->dy < c->height );
+
+ } /* End column loop */
+ } /* End row loop */
+
+#ifdef USE_SSE
+ asm volatile ( "emms" );
+#endif
+
+#ifdef COUNT_COMPARES
+ fprintf(stderr, "%d comparisons per block were made", compares/count);
+#endif
+ return;
+}
+
+void collect_post_statistics( struct motion_est_context_s *c ) {
+
+ c->comparison_average = 0;
+ c->average_length = 0;
+ c->average_x = 0;
+ c->average_y = 0;
+
+ int i, j, count = 0;
+
+ for ( i = c->left_mb; i <= c->right_mb; i++ ){
+ for ( j = c->top_mb; j <= c->bottom_mb; j++ ){
+
+ count++;
+ c->comparison_average += CURRENT(i,j)->msad;
+ c->average_x += CURRENT(i,j)->dx;
+ c->average_y += CURRENT(i,j)->dy;
+
+
+ }
+ }
+
+ if ( count > 0 )
+ {
+ c->comparison_average /= count;
+ c->average_x /= count;
+ c->average_y /= count;
+ c->average_length = sqrt( c->average_x * c->average_x + c->average_y * c->average_y );
+ }
+
+}
+
+static void init_optimizations( struct motion_est_context_s *c )
+{
+ switch(c->mb_w){
+#ifdef USE_SSE
+ case 4: if(c->mb_h == 4) c->compare_optimized = sad_sse_422_luma_4x4;
+ else c->compare_optimized = sad_sse_422_luma_4w;
+ break;
+ case 8: if(c->mb_h == 8) c->compare_optimized = sad_sse_422_luma_8x8;
+ else c->compare_optimized = sad_sse_422_luma_8w;
+ break;
+ case 16: if(c->mb_h == 16) c->compare_optimized = sad_sse_422_luma_16x16;
+ else c->compare_optimized = sad_sse_422_luma_16w;
+ break;
+ case 32: if(c->mb_h == 32) c->compare_optimized = sad_sse_422_luma_32x32;
+ else c->compare_optimized = sad_sse_422_luma_32w;
+ break;
+ case 64: c->compare_optimized = sad_sse_422_luma_64w;
+ break;
+#endif
+ default: c->compare_optimized = sad_reference;
+ break;
+ }
+}
+
+inline static void set_red(uint8_t *image, struct motion_est_context_s *c)
+{
+ int n;
+ for( n = 0; n < c->width * c->height * 2; n+=4 )
+ {
+ image[n] = 79;
+ image[n+1] = 91;
+ image[n+2] = 79;
+ image[n+3] = 237;
+ }
+
+}
+
+static void show_residual( uint8_t *result, struct motion_est_context_s *c )
+{
+ int i, j;
+ int x,y,w,h;
+ int dx, dy;
+ int tx,ty;
+ uint8_t *b, *r;
+
+// set_red(result,c);
+
+ for( j = c->top_mb; j <= c->bottom_mb; j++ ){
+ for( i = c->left_mb; i <= c->right_mb; i++ ){
+
+ dx = CURRENT(i,j)->dx;
+ dy = CURRENT(i,j)->dy;
+ w = c->mb_w;
+ h = c->mb_h;
+ x = i * w;
+ y = j * h;
+
+ // Denoise function caused some blocks to be completely clipped, ignore them
+ if (constrain( &x, &y, &w, &h, dx, dy, 0, c->width, 0, c->height) == 0 )
+ continue;
+
+ for( ty = y; ty < y + h ; ty++ ){
+ for( tx = x; tx < x + w ; tx++ ){
+
+ b = c->former_image + (tx+dx)*c->xstride + (ty+dy)*c->ystride;
+ r = result + tx*c->xstride + ty*c->ystride;
+
+ r[0] = 16 + ABS( r[0] - b[0] );
+
+ if( dx % 2 == 0 )
+ r[1] = 128 + ABS( r[1] - b[1] );
+ else
+ // FIXME: may exceed boundies
+ r[1] = 128 + ABS( r[1] - ( *(b-1) + b[3] ) /2 );
+ }
+ }
+ }
+ }
+}
+
+static void show_reconstruction( uint8_t *result, struct motion_est_context_s *c )
+{
+ int i, j;
+ int x,y,w,h;
+ int dx,dy;
+ uint8_t *r, *s;
+ int tx,ty;
+
+ for( i = c->left_mb; i <= c->right_mb; i++ ){
+ for( j = c->top_mb; j <= c->bottom_mb; j++ ){
+
+ dx = CURRENT(i,j)->dx;
+ dy = CURRENT(i,j)->dy;
+ w = c->mb_w;
+ h = c->mb_h;
+ x = i * w;
+ y = j * h;
+
+ // Denoise function caused some blocks to be completely clipped, ignore them
+ if (constrain( &x, &y, &w, &h, dx, dy, 0, c->width, 0, c->height) == 0 )
+ continue;
+
+ for( ty = y; ty < y + h ; ty++ ){
+ for( tx = x; tx < x + w ; tx++ ){
+
+ r = result + tx*c->xstride + ty*c->ystride;
+ s = c->former_image + (tx+dx)*c->xstride + (ty+dy)*c->ystride;
+
+ r[0] = s[0];
+
+ if( dx % 2 == 0 )
+ r[1] = s[1];
+ else
+ // FIXME: may exceed boundies
+ r[1] = ( *(s-1) + s[3] ) /2;
+ }
+ }
+ }
+ }
+}
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the filter
+ mlt_filter filter = mlt_frame_pop_service( frame );
+
+ // Get the motion_est context object
+ struct motion_est_context_s *c = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "context", NULL);
+
+
+ // Get the new image and frame number
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ #ifdef BENCHMARK
+ struct timeval start; gettimeofday(&start, NULL );
+ #endif
+
+
+ if( error != 0 )
+ mlt_properties_debug( MLT_FRAME_PROPERTIES(frame), "error after mlt_frame_get_image() in motion_est", stderr );
+
+ c->current_frame_position = mlt_frame_get_position( frame );
+
+ /* Context Initialization */
+ if ( c->initialized == 0 ) {
+
+ // Get the filter properties object
+ mlt_properties properties = mlt_filter_properties( filter );
+
+ c->width = *width;
+ c->height = *height;
+
+ /* Get parameters that may have been overridden */
+ if( mlt_properties_get( properties, "macroblock_width") != NULL )
+ c->mb_w = mlt_properties_get_int( properties, "macroblock_width");
+
+ if( mlt_properties_get( properties, "macroblock_height") != NULL )
+ c->mb_h = mlt_properties_get_int( properties, "macroblock_height");
+
+ if( mlt_properties_get( properties, "prediction_thresh") != NULL )
+ c->initial_thresh = mlt_properties_get_int( properties, "prediction_thresh" );
+ else
+ c->initial_thresh = c->mb_w * c->mb_h;
+
+ if( mlt_properties_get( properties, "search_method") != NULL )
+ c->search_method = mlt_properties_get_int( properties, "search_method");
+
+ if( mlt_properties_get( properties, "skip_prediction") != NULL )
+ c->skip_prediction = mlt_properties_get_int( properties, "skip_prediction");
+
+ if( mlt_properties_get( properties, "limit_x") != NULL )
+ c->limit_x = mlt_properties_get_int( properties, "limit_x");
+
+ if( mlt_properties_get( properties, "limit_y") != NULL )
+ c->limit_y = mlt_properties_get_int( properties, "limit_y");
+
+ if( mlt_properties_get( properties, "check_chroma" ) != NULL )
+ c->check_chroma = mlt_properties_get_int( properties, "check_chroma" );
+
+ if( mlt_properties_get( properties, "denoise" ) != NULL )
+ c->denoise = mlt_properties_get_int( properties, "denoise" );
+
+ if( mlt_properties_get( properties, "show_reconstruction" ) != NULL )
+ c->show_reconstruction = mlt_properties_get_int( properties, "show_reconstruction" );
+
+ if( mlt_properties_get( properties, "show_residual" ) != NULL )
+ c->show_residual = mlt_properties_get_int( properties, "show_residual" );
+
+ if( mlt_properties_get( properties, "toggle_when_paused" ) != NULL )
+ c->toggle_when_paused = mlt_properties_get_int( properties, "toggle_when_paused" );
+
+ init_optimizations( c );
+
+ // Calculate the dimensions in macroblock units
+ c->mv_buffer_width = (*width / c->mb_w);
+ c->mv_buffer_height = (*height / c->mb_h);
+
+ // Size of the motion vector buffer
+ c->mv_size = c->mv_buffer_width * c->mv_buffer_height * sizeof(struct motion_vector_s);
+
+ // Allocate the motion vector buffers
+ c->former_vectors = mlt_pool_alloc( c->mv_size );
+ c->current_vectors = mlt_pool_alloc( c->mv_size );
+ c->denoise_vectors = mlt_pool_alloc( c->mv_size );
+
+ // Register motion buffers for destruction
+ mlt_properties_set_data( properties, "current_motion_vectors", (void *)c->current_vectors, 0, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "former_motion_vectors", (void *)c->former_vectors, 0, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "denoise_motion_vectors", (void *)c->denoise_vectors, 0, mlt_pool_release, NULL );
+
+ c->former_vectors_valid = 0;
+ memset( c->former_vectors, 0, c->mv_size );
+
+ // Calculate the size of our steps (the number of bytes that seperate adjacent pixels in X and Y direction)
+ switch( *format ) {
+ case mlt_image_yuv422:
+ c->xstride = 2;
+ c->ystride = c->xstride * *width;
+ break;
+ default:
+ // I don't know
+ fprintf(stderr, "\"I am unfamiliar with your new fangled pixel format!\" -filter_motion_est\n");
+ return -1;
+ }
+
+ // Allocate a cache for the previous frame's image
+ c->former_image = mlt_pool_alloc( *width * *height * 2 );
+ c->cache_image = mlt_pool_alloc( *width * *height * 2 );
+
+ // Register for destruction
+ mlt_properties_set_data( properties, "cache_image", (void *)c->cache_image, 0, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "former_image", (void *)c->former_image, 0, mlt_pool_release, NULL );
+
+ c->former_frame_position = c->current_frame_position;
+ c->previous_msad = 0;
+
+ c->initialized = 1;
+ }
+
+ /* Check to see if somebody else has given us bounds */
+ struct mlt_geometry_item_s *bounds = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "bounds", NULL );
+
+ if( bounds != NULL ) {
+ // translate pixel units (from bounds) to macroblock units
+ // make sure whole macroblock stays within bounds
+ c->left_mb = ( bounds->x + c->mb_w - 1 ) / c->mb_w;
+ c->top_mb = ( bounds->y + c->mb_h - 1 ) / c->mb_h;
+ c->right_mb = ( bounds->x + bounds->w ) / c->mb_w - 1;
+ c->bottom_mb = ( bounds->y + bounds->h ) / c->mb_h - 1;
+ c->bounds.x = bounds->x;
+ c->bounds.y = bounds->y;
+ c->bounds.w = bounds->w;
+ c->bounds.h = bounds->h;
+ } else {
+ c->left_mb = c->prev_left_mb = 0;
+ c->top_mb = c->prev_top_mb = 0;
+ c->right_mb = c->prev_right_mb = c->mv_buffer_width - 1; // Zero indexed
+ c->bottom_mb = c->prev_bottom_mb = c->mv_buffer_height - 1;
+ c->bounds.x = 0;
+ c->bounds.y = 0;
+ c->bounds.w = *width;
+ c->bounds.h = *height;
+ }
+
+ // If video is advancing, run motion vector algorithm and etc...
+ if( c->former_frame_position + 1 == c->current_frame_position )
+ {
+
+ // Swap the motion vector buffers and reuse allocated memory
+ struct motion_vector_s *temp = c->current_vectors;
+ c->current_vectors = c->former_vectors;
+ c->former_vectors = temp;
+
+ // This is done because filter_vismv doesn't pay attention to frame boundry
+ memset( c->current_vectors, 0, c->mv_size );
+
+ // Perform the motion search
+ motion_search( c->cache_image, *image, c );
+
+ collect_post_statistics( c );
+
+
+ // Detect shot changes
+ if( c->comparison_average > 10 * c->mb_w * c->mb_h &&
+ c->comparison_average > c->previous_msad * 2 )
+ {
+ fprintf(stderr, " - SAD: %d <<Shot change>>\n", c->comparison_average);
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "shot_change", 1);
+ // c->former_vectors_valid = 0; // Invalidate the previous frame's predictors
+ c->shot_change = 1;
+ }
+ else {
+ c->former_vectors_valid = 1;
+ c->shot_change = 0;
+ //fprintf(stderr, " - SAD: %d\n", c->comparison_average);
+ }
+
+ c->previous_msad = c->comparison_average;
+
+ if( c->comparison_average != 0 ) { // If the frame is not a duplicate of the previous frame
+
+ // denoise the vector buffer
+ if( c->denoise )
+ median_denoise( c->current_vectors, c );
+
+ // Pass the new vector data into the frame
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "motion_est.vectors",
+ (void*)c->current_vectors, c->mv_size, NULL, NULL );
+
+ // Cache the frame's image. Save the old cache. Reuse memory.
+ // After this block, exactly two unique frames will be cached
+ uint8_t *timg = c->cache_image;
+ c->cache_image = c->former_image;
+ c->former_image = timg;
+ memcpy( c->cache_image, *image, *width * *height * c->xstride );
+
+
+ }
+ else {
+ // Undo the Swap, This fixes the ugliness caused by a duplicate frame
+ temp = c->current_vectors;
+ c->current_vectors = c->former_vectors;
+ c->former_vectors = temp;
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "motion_est.vectors",
+ (void*)c->former_vectors, c->mv_size, NULL, NULL );
+ }
+
+
+ if( c->shot_change == 1)
+ ;
+ else if( c->show_reconstruction )
+ show_reconstruction( *image, c );
+ else if( c->show_residual )
+ show_residual( *image, c );
+
+ }
+ // paused
+ else if( c->former_frame_position == c->current_frame_position )
+ {
+ // Pass the old vector data into the frame if it's valid
+ if( c->former_vectors_valid == 1 ) {
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "motion_est.vectors",
+ (void*)c->current_vectors, c->mv_size, NULL, NULL );
+
+ if( c->shot_change == 1)
+ ;
+ else if( c->toggle_when_paused == 1 ) {
+ if( c->show_reconstruction )
+ show_reconstruction( *image, c );
+ else if( c->show_residual )
+ show_residual( *image, c );
+ c->toggle_when_paused = 2;
+ }
+ else if( c->toggle_when_paused == 2 )
+ c->toggle_when_paused = 1;
+ else {
+ if( c->show_reconstruction )
+ show_reconstruction( *image, c );
+ else if( c->show_residual )
+ show_residual( *image, c );
+ }
+
+ }
+
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "shot_change", c->shot_change);
+ }
+ // there was jump in frame number
+ else {
+// fprintf(stderr, "Warning: there was a frame number jumped from %d to %d.\n", c->former_frame_position, c->current_frame_position);
+ c->former_vectors_valid = 0;
+ }
+
+
+ // Cache our bounding geometry for the next frame's processing
+ c->prev_left_mb = c->left_mb;
+ c->prev_top_mb = c->top_mb;
+ c->prev_right_mb = c->right_mb;
+ c->prev_bottom_mb = c->bottom_mb;
+
+ // Remember which frame this is
+ c->former_frame_position = c->current_frame_position;
+
+
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.macroblock_width", c->mb_w );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.macroblock_height", c->mb_h );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.left_mb", c->left_mb );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.right_mb", c->right_mb );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.top_mb", c->top_mb );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.bottom_mb", c->bottom_mb );
+
+ #ifdef BENCHMARK
+ struct timeval finish; gettimeofday(&finish, NULL ); int difference = (finish.tv_sec - start.tv_sec) * 1000000 + (finish.tv_usec - start.tv_usec);
+ fprintf(stderr, " in frame %d:%d usec\n", c->current_frame_position, difference);
+ #endif
+
+
+ return error;
+}
+
+
+
+/** filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ // Keeps tabs on the filter object
+ mlt_frame_push_service( frame, this);
+
+ // Push the frame filter
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+mlt_filter filter_motion_est_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ // Get the properties object
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ // Initialize the motion estimation context
+ struct motion_est_context_s *context;
+ context = mlt_pool_alloc( sizeof(struct motion_est_context_s) );
+ mlt_properties_set_data( properties, "context", (void *)context, sizeof( struct motion_est_context_s ),
+ mlt_pool_release, NULL );
+
+
+ // Register the filter
+ this->process = filter_process;
+
+ /* defaults that may be overridden */
+ context->mb_w = 16;
+ context->mb_h = 16;
+ context->skip_prediction = 0;
+ context->limit_x = 64;
+ context->limit_y = 64;
+ context->search_method = DIAMOND_SEARCH; // FIXME: not used
+ context->check_chroma = 0;
+ context->denoise = 1;
+ context->show_reconstruction = 0;
+ context->show_residual = 0;
+ context->toggle_when_paused = 0;
+
+ /* reference functions that may have optimized versions */
+ context->compare_reference = sad_reference;
+
+ // The rest of the buffers will be initialized when the filter is first processed
+ context->initialized = 0;
+ }
+ return this;
+}
--- /dev/null
+/*
+ * Perform motion estimation
+ * Zachary K Drew, Copyright 2004
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _FILTER_MOTION_EST_H_
+#define _FILTER_MOTION_EST_H_
+
+#include <framework/mlt_filter.h>
+
+extern mlt_filter filter_motion_est_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+#define MAX_MSAD 0xffff
+
+struct motion_vector_s
+{
+ int msad; //<! Sum of Absolute Differences for this vector
+ int dx; //<! integer X displacement
+ int dy; //<! integer Y displacement
+ int vert_dev; //<! a measure of vertical color deviation
+ int horiz_dev; //<! a measure of horizontal color deviation
+ int valid;
+ int quality;
+ uint8_t color; //<! The color
+};
+
+typedef struct motion_vector_s motion_vector;
+#endif
--- /dev/null
+/*
+ * /brief Draw motion vectors
+ * /author Zachary Drew, Copyright 2004
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include "filter_motion_est.h"
+#include "arrow_code.h"
+
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+static void paint_arrows( uint8_t *image, struct motion_vector_s *vectors, int w, int h, int mb_w, int mb_h )
+{
+ int i, j, x, y;
+ struct motion_vector_s *p;
+ for( i = 0; i < w/mb_w; i++ ){
+ for( j = 0; j < h/mb_h; j++ ){
+ x = i*mb_w;
+ y = j*mb_h;
+ p = vectors + (w/mb_w)*j + i;
+
+ if ( p->valid == 1 ) {
+ //draw_rectangle_outline(image, x-1, y-1, mb_w+1, mb_h+1,100);
+ //x += mb_w/4;
+ //y += mb_h/4;
+ //draw_rectangle_outline(image, x + p->dx, y + p->dy, mb_w, mb_h,100);
+ x += mb_w/2;
+ y += mb_h/2;
+ draw_arrow(image, x, y, x + p->dx, y + p->dy, 100);
+ //draw_rectangle_fill(image, x + p->dx, y + p->dy, mb_w, mb_h, 100);
+ }
+ else if ( p->valid == 2 ) {
+ draw_rectangle_outline(image, x+1, y+1, mb_w-2, mb_h-2,100);
+ }
+ else if ( p->valid == 3 ) {
+ draw_rectangle_fill(image, x-p->dx, y-p->dy, mb_w, mb_h,0);
+ }
+ else if ( p->valid == 4 ) {
+ draw_line(image, x, y, x + 4, y, 100);
+ draw_line(image, x, y, x, y + 4, 100);
+ draw_line(image, x + 4, y, x, y + 4, 100);
+
+ draw_line(image, x+mb_w-1, y+mb_h-1, x+mb_w-5, y+mb_h-1, 100);
+ draw_line(image, x+mb_w-1, y+mb_h-1, x+mb_w-1, y+mb_h-5, 100);
+ draw_line(image, x+mb_w-5, y+mb_h-1, x+mb_w-1, y+mb_h-5, 100);
+ }
+ }
+ }
+}
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the frame properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES(frame);
+
+ // Get the new image
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+ if( error != 0 )
+ mlt_properties_debug( MLT_FRAME_PROPERTIES(frame), "error after mlt_frame_get_image()", stderr );
+
+
+ // Get the size of macroblocks in pixel units
+ int macroblock_height = mlt_properties_get_int( properties, "motion_est.macroblock_height" );
+ int macroblock_width = mlt_properties_get_int( properties, "motion_est.macroblock_width" );
+
+ // Get the motion vectors
+ struct motion_vector_s *current_vectors = mlt_properties_get_data( properties, "motion_est.vectors", NULL );
+
+ init_arrows( format, *width, *height );
+
+ if ( mlt_properties_get_int( properties, "shot_change" ) == 1 )
+ {
+ draw_line(*image, 0, 0, *width, *height, 100);
+ draw_line(*image, 0, *height, *width, 0, 100);
+ }
+ if( current_vectors != NULL ) {
+ paint_arrows( *image, current_vectors, *width, *height, macroblock_width, macroblock_height);
+ }
+
+ return error;
+}
+
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the frame filter
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+
+mlt_filter filter_vismv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+
+ }
+
+ return this;
+}
+
+/** This source code will self destruct in 5...4...3...
+*/
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * producer_slowmotion.c -- create subspeed frames
+ * Author: Zachary Drew
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include "filter_motion_est.h"
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <assert.h>
+#define SHIFT 8
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+// This is used to constrains pixel operations between two blocks to be within the image boundry
+inline static int constrain( int *x, int *y, int *w, int *h,
+ const int dx, const int dy,
+ const int left, const int right,
+ const int top, const int bottom)
+{
+ uint32_t penalty = 1 << SHIFT; // Retain a few extra bits of precision
+ int x2 = *x + dx;
+ int y2 = *y + dy;
+ int w_remains = *w;
+ int h_remains = *h;
+
+ // Origin of macroblock moves left of image boundy
+ if( *x < left || x2 < left ) {
+ w_remains = *w - left + ((*x < x2) ? *x : x2);
+ *x += *w - w_remains;
+ }
+ // Portion of macroblock moves right of image boundry
+ else if( *x + *w > right || x2 + *w > right )
+ w_remains = right - ((*x > x2) ? *x : x2);
+
+ // Origin of macroblock moves above image boundy
+ if( *y < top || y2 < top ) {
+ h_remains = *h - top + ((*y < y2) ? *y : y2);
+ *y += *h - h_remains;
+ }
+ // Portion of macroblock moves bellow image boundry
+ else if( *y + *h > bottom || y2 + *h > bottom )
+ h_remains = bottom - ((*y > y2) ? *y : y2);
+
+ if( w_remains == *w && h_remains == *h ) return penalty;
+ if( w_remains <= 0 || h_remains <= 0) return 0; // Block is clipped out of existance
+ penalty = (*w * *h * penalty)
+ / ( w_remains * h_remains); // Recipricol of the fraction of the block that remains
+
+ *w = w_remains; // Update the width and height
+ *h = h_remains;
+
+ return penalty;
+}
+
+static void motion_interpolate( uint8_t *first_image, uint8_t *second_image, uint8_t *output,
+ int top_mb, int bottom_mb, int left_mb, int right_mb,
+ int mb_w, int mb_h,
+ int width, int height,
+ int xstride, int ystride,
+ double scale,
+ motion_vector *vectors )
+{
+ assert ( scale >= 0.0 && scale <= 1.0 );
+
+ int i, j;
+ int x,y,w,h;
+ int dx, dy;
+ int scaled_dx, scaled_dy;
+ int tx,ty;
+ uint8_t *f,*s,*r;
+ motion_vector *here;
+ int mv_width = width / mb_w;
+
+ for( j = top_mb; j <= bottom_mb; j++ ){
+ for( i = left_mb; i <= right_mb; i++ ){
+
+ here = vectors + j*mv_width + i;
+ scaled_dx = (1.0 - scale) * (double)here->dx;
+ scaled_dy = (1.0 - scale) * (double)here->dy;
+ dx = here->dx;
+ dy = here->dy;
+ w = mb_w; h = mb_h;
+ x = i * w; y = j * h;
+
+ // Denoise function caused some blocks to be completely clipped, ignore them
+ if (constrain( &x, &y, &w, &h, dx, dy, 0, width, 0, height) == 0 )
+ continue;
+
+ for( ty = y; ty < y + h ; ty++ ){
+ for( tx = x; tx < x + w ; tx++ ){
+
+ f = first_image + (tx + dx )*xstride + (ty + dy )*ystride;
+ s = second_image + (tx )*xstride + (ty )*ystride;
+ r = output + (tx+scaled_dx)*xstride + (ty+scaled_dy)*ystride;
+/*
+ if( ABS(f[0] - s[0]) > 3 * here->msad / (mb_w * mb_h * 2) )
+ {
+ r[0] = f[0];
+ r[1] = f[1];
+ }
+
+ else
+ {
+
+*/
+ r[0] = ( 1.0 - scale ) * (double)f[0] + scale * (double)s[0];
+
+ if( dx % 2 == 0 )
+ {
+ if( scaled_dx % 2 == 0 )
+ r[1] = ( 1.0 - scale ) * (double)f[1] + scale * (double) s[1];
+ else
+ *(r-1) = ( 1.0 - scale ) * (double)f[1] + scale * (double) s[1];
+ }
+ else
+ {
+ if( scaled_dx %2 == 0 )
+ // FIXME: may exceed boundies
+ r[1] = ( 1.0 - scale ) * ( (double)(*(f-1) + (double)f[3]) / 2.0 ) + scale * (double) s[1];
+ else
+ // FIXME: may exceed boundies
+ *(r-1) = ( 1.0 - scale ) * ( (double)(*(f-1) + (double)f[3]) / 2.0 ) + scale * (double) s[1];
+ }
+// }
+ }
+ }
+ }
+ }
+}
+
+// Image stack(able) method
+static int slowmotion_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ // Get the filter object and properties
+ mlt_producer producer = mlt_frame_pop_service( this );
+ mlt_frame second_frame = mlt_frame_pop_service( this );
+ mlt_frame first_frame = mlt_frame_pop_service( this );
+
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Frame properties objects
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this );
+ mlt_properties first_frame_properties = MLT_FRAME_PROPERTIES( first_frame );
+ mlt_properties second_frame_properties = MLT_FRAME_PROPERTIES( second_frame );
+
+ // image stride
+ int size, xstride, ystride;
+ switch( *format ){
+ case mlt_image_yuv422:
+ size = *width * *height * 2;
+ xstride = 2;
+ ystride = 2 * *width;
+ break;
+ default:
+ fprintf(stderr, "Unsupported image format\n");
+ return -1;
+ }
+
+ uint8_t *output = mlt_properties_get_data( producer_properties, "output_buffer", 0 );
+ if( output == NULL )
+ {
+ output = mlt_pool_alloc( size );
+
+ // Let someone else clean up
+ mlt_properties_set_data( producer_properties, "output_buffer", output, size, mlt_pool_release, NULL );
+ }
+
+ uint8_t *first_image = mlt_properties_get_data( first_frame_properties, "image", NULL );
+ uint8_t *second_image = mlt_properties_get_data( second_frame_properties, "image", NULL );
+
+ // which frames are buffered?
+
+ int error = 0;
+
+ if( first_image == NULL )
+ {
+ error = mlt_frame_get_image( first_frame, &first_image, format, width, height, writable );
+
+ if( error != 0 ) {
+ fprintf(stderr, "first_image == NULL get image died\n");
+ return error;
+ }
+ }
+
+ if( second_image == NULL )
+ {
+ error = mlt_frame_get_image( second_frame, &second_image, format, width, height, writable );
+
+ if( error != 0 ) {
+ fprintf(stderr, "second_image == NULL get image died\n");
+ return error;
+ }
+ }
+
+ // These need to passed onto the frame for other
+ mlt_properties_pass_list( frame_properties, second_frame_properties,
+ "motion_est.left_mb, motion_est.right_mb, \
+ motion_est.top_mb, motion_est.bottom_mb, \
+ motion_est.macroblock_width, motion_est.macroblock_height" );
+
+ // Pass the pointer to the vectors without serializing
+ mlt_properties_set_data( frame_properties, "motion_est.vectors",
+ mlt_properties_get_data( second_frame_properties, "motion_est.vectors", NULL ),
+ 0, NULL, NULL );
+
+
+ // Start with a base image
+ memcpy( output, first_image, size );
+
+ if( mlt_properties_get_int( producer_properties, "method" ) == 1 ) {
+
+ mlt_position first_position = mlt_frame_get_position( first_frame );
+ double actual_position = mlt_producer_get_speed( producer ) * (double)mlt_frame_get_position( this );
+ double scale = actual_position - first_position;
+
+ motion_interpolate
+ (
+ first_image, second_image, output,
+ mlt_properties_get_int( second_frame_properties, "motion_est.top_mb" ),
+ mlt_properties_get_int( second_frame_properties, "motion_est.bottom_mb" ),
+ mlt_properties_get_int( second_frame_properties, "motion_est.left_mb" ),
+ mlt_properties_get_int( second_frame_properties, "motion_est.right_mb" ),
+ mlt_properties_get_int( second_frame_properties, "motion_est.macroblock_width" ),
+ mlt_properties_get_int( second_frame_properties, "motion_est.macroblock_height" ),
+ *width, *height,
+ xstride, ystride,
+ scale,
+ mlt_properties_get_data( second_frame_properties, "motion_est.vectors", NULL )
+ );
+
+ if( mlt_properties_get_int( producer_properties, "debug" ) == 1 ) {
+ mlt_filter watermark = mlt_properties_get_data( producer_properties, "watermark", NULL );
+
+ if( watermark == NULL ) {
+ mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
+ watermark = mlt_factory_filter( profile, "watermark", NULL );
+ mlt_properties_set_data( producer_properties, "watermark", watermark, 0, (mlt_destructor)mlt_filter_close, NULL );
+ mlt_producer_attach( producer, watermark );
+ }
+
+ mlt_properties wm_properties = MLT_FILTER_PROPERTIES( watermark );
+
+ char disp[30];
+ sprintf(disp, "+%10.2f.txt", actual_position);
+ mlt_properties_set( wm_properties, "resource", disp );
+
+ }
+
+ }
+
+ *image = output;
+ mlt_properties_set_data( frame_properties, "image", output, size, NULL, NULL );
+
+ // Make sure that no further scaling is done
+ mlt_properties_set( frame_properties, "rescale.interps", "none" );
+ mlt_properties_set( frame_properties, "scale", "off" );
+
+ mlt_frame_close( first_frame );
+ mlt_frame_close( second_frame );
+
+ return 0;
+}
+
+static int slowmotion_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ // Construct a new frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES(this);
+
+
+ if( frame != NULL )
+ {
+
+ mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL );
+ mlt_frame second_frame = mlt_properties_get_data( properties, "second_frame", NULL );
+
+ mlt_position first_position = (first_frame != NULL) ? mlt_frame_get_position( first_frame ) : -1;
+ mlt_position second_position = (second_frame != NULL) ? mlt_frame_get_position( second_frame ) : -1;
+
+ // Get the real producer
+ mlt_producer real_producer = mlt_properties_get_data( properties, "producer", NULL );
+
+ // Our "in" needs to be the same, keep it so
+ mlt_properties_pass_list( MLT_PRODUCER_PROPERTIES( real_producer ), properties, "in" );
+
+ // Calculate our positions
+ double actual_position = mlt_producer_get_speed( this ) * (double)mlt_producer_position( this );
+ mlt_position need_first = floor( actual_position );
+ mlt_position need_second = need_first + 1;
+
+ if( need_first != first_position )
+ {
+ mlt_frame_close( first_frame );
+ first_position = -1;
+ first_frame = NULL;
+ }
+
+ if( need_second != second_position)
+ {
+ mlt_frame_close( second_frame );
+ second_position = -1;
+ second_frame = NULL;
+ }
+
+ if( first_frame == NULL )
+ {
+ // Seek the producer to the correct place
+ mlt_producer_seek( real_producer, need_first );
+
+ // Get the frame
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index );
+ }
+
+ if( second_frame == NULL )
+ {
+ // Seek the producer to the correct place
+ mlt_producer_seek( real_producer, need_second );
+
+ // Get the frame
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &second_frame, index );
+ }
+
+ // Make sure things are in their place
+ mlt_properties_set_data( properties, "first_frame", first_frame, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "second_frame", second_frame, 0, NULL, NULL );
+
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 0 );
+
+ // Stack the producer and producer's get image
+ mlt_frame_push_service( *frame, first_frame );
+ mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( first_frame ) );
+
+ mlt_frame_push_service( *frame, second_frame );
+ mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( second_frame ) );
+
+ mlt_frame_push_service( *frame, this );
+ mlt_frame_push_service( *frame, slowmotion_get_image );
+
+ // Give the returned frame temporal identity
+ mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+ }
+
+ return 0;
+}
+
+mlt_producer producer_slowmotion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_producer this = mlt_producer_new( );
+
+ // Wrap fezzik
+ mlt_producer real_producer = mlt_factory_producer( profile, "fezzik", arg );
+
+ // We need to apply the motion estimation filter manually
+ mlt_filter filter = mlt_factory_filter( profile, "motion_est", NULL );
+
+ if ( this != NULL && real_producer != NULL && filter != NULL)
+ {
+ // attach the motion_est filter to the real producer
+ mlt_producer_attach( real_producer, filter );
+
+ // Get the properties of this producer
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Fezzik normalised it for us already
+ mlt_properties_set_int( properties, "fezzik_normalised", 1);
+
+ // Store the producer and fitler
+ mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ mlt_properties_set_data( properties, "motion_est", filter, 0, ( mlt_destructor )mlt_filter_close, NULL );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "macroblock_width", 16 );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "macroblock_height", 16 );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "denoise", 0 );
+
+ // Grap some stuff from the real_producer
+ mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ),
+ "in, out, length, resource" );
+
+ // Since we control the seeking, prevent it from seeking on its own
+ mlt_producer_set_speed( real_producer, 0 );
+
+ //mlt_properties_set( properties, "method", "onefield" );
+
+ // Override the get_frame method
+ this->get_frame = slowmotion_get_frame;
+
+ }
+ else
+ {
+ if ( this )
+ mlt_producer_close( this );
+ if ( real_producer )
+ mlt_producer_close( real_producer );
+ if ( filter )
+ mlt_filter_close( filter );
+
+ this = NULL;
+ }
+ return this;
+}
--- /dev/null
+/*
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+
+
+#define SAD_SSE_INIT \
+ asm volatile ( "pxor %%mm6,%%mm6\n\t" :: );\
+
+// Sum two 8x1 pixel blocks
+#define SAD_SSE_SUM_8(OFFSET) \
+ "movq " #OFFSET "(%0),%%mm0 \n\t"\
+ "movq " #OFFSET "(%1),%%mm1 \n\t"\
+ "psadbw %%mm1,%%mm0 \n\t"\
+ "paddw %%mm0,%%mm6 \n\t"\
+
+#define SAD_SSE_FINISH(RESULT) \
+ asm volatile( "movd %%mm6,%0" : "=r" (RESULT) : );
+
+// Advance by ystride
+#define SAD_SSE_NEXTROW \
+ "add %2,%0 \n\t"\
+ "add %2,%1 \n\t"\
+
+// BROKEN!
+inline static int sad_sse_4x4( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_INIT
+ #define ROW SAD_SSE_SUM_8(0) SAD_SSE_NEXTROW
+ asm volatile ( ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+inline static int sad_sse_8x8( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_INIT
+ #define ROW SAD_SSE_SUM_8(0) SAD_SSE_NEXTROW
+ asm volatile ( ROW ROW ROW ROW ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+inline static int sad_sse_16x16( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_INIT
+ #define ROW SAD_SSE_SUM_8(0) SAD_SSE_SUM_8(8) SAD_SSE_NEXTROW
+ asm volatile ( ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+inline static int sad_sse_32x32( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_INIT
+ #define ROW SAD_SSE_SUM_8(0) SAD_SSE_SUM_8(8) SAD_SSE_SUM_8(16) SAD_SSE_SUM_8(24)\
+ SAD_SSE_NEXTROW
+
+ asm volatile ( ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+// BROKEN!
+inline static int sad_sse_4w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_SUM_8(0)
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+inline static int sad_sse_8w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_SUM_8(0)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+inline static int sad_sse_16w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_SUM_8(0)
+ SAD_SSE_SUM_8(8)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+inline static int sad_sse_32w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_SUM_8(0)
+ SAD_SSE_SUM_8(8)
+ SAD_SSE_SUM_8(16)
+ SAD_SSE_SUM_8(24)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+inline static int sad_sse_64w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_SUM_8(0)
+ SAD_SSE_SUM_8(8)
+ SAD_SSE_SUM_8(16)
+ SAD_SSE_SUM_8(24)
+ SAD_SSE_SUM_8(32)
+ SAD_SSE_SUM_8(40)
+ SAD_SSE_SUM_8(48)
+ SAD_SSE_SUM_8(56)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+static __attribute__((used)) __attribute__((aligned(8))) uint64_t sad_sse_422_mask_chroma = 0x00ff00ff00ff00ffULL;
+
+#define SAD_SSE_422_LUMA_INIT \
+ asm volatile ( "movq %0,%%mm7\n\t"\
+ "pxor %%mm6,%%mm6\n\t" :: "m" (sad_sse_422_mask_chroma) );\
+
+// Sum two 4x1 pixel blocks
+#define SAD_SSE_422_LUMA_SUM_4(OFFSET) \
+ "movq " #OFFSET "(%0),%%mm0 \n\t"\
+ "movq " #OFFSET "(%1),%%mm1 \n\t"\
+ "pand %%mm7,%%mm0 \n\t"\
+ "pand %%mm7,%%mm1 \n\t"\
+ "psadbw %%mm1,%%mm0 \n\t"\
+ "paddw %%mm0,%%mm6 \n\t"\
+
+static int sad_sse_422_luma_4x4( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_422_LUMA_INIT
+ #define ROW SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_NEXTROW
+ asm volatile ( ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+static int sad_sse_422_luma_8x8( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_422_LUMA_INIT
+ #define ROW SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_422_LUMA_SUM_4(8) SAD_SSE_NEXTROW
+ asm volatile ( ROW ROW ROW ROW ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+static int sad_sse_422_luma_16x16( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_422_LUMA_INIT
+ #define ROW SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_422_LUMA_SUM_4(8) SAD_SSE_422_LUMA_SUM_4(16) SAD_SSE_422_LUMA_SUM_4(24) SAD_SSE_NEXTROW
+ asm volatile ( ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+static int sad_sse_422_luma_32x32( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+ SAD_SSE_422_LUMA_INIT
+ #define ROW SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_422_LUMA_SUM_4(8) SAD_SSE_422_LUMA_SUM_4(16) SAD_SSE_422_LUMA_SUM_4(24)\
+ SAD_SSE_422_LUMA_SUM_4(32) SAD_SSE_422_LUMA_SUM_4(40) SAD_SSE_422_LUMA_SUM_4(48) SAD_SSE_422_LUMA_SUM_4(56)\
+ SAD_SSE_NEXTROW
+
+ asm volatile ( ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ ROW ROW ROW ROW ROW ROW ROW ROW
+ :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+
+ SAD_SSE_FINISH(result)
+ return result;
+ #undef ROW
+
+}
+
+static int sad_sse_422_luma_4w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_422_LUMA_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_422_LUMA_SUM_4(0)
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+static int sad_sse_422_luma_8w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_422_LUMA_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_422_LUMA_SUM_4(0)
+ SAD_SSE_422_LUMA_SUM_4(8)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+static int sad_sse_422_luma_16w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_422_LUMA_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_422_LUMA_SUM_4(0)
+ SAD_SSE_422_LUMA_SUM_4(8)
+ SAD_SSE_422_LUMA_SUM_4(16)
+ SAD_SSE_422_LUMA_SUM_4(24)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+static int sad_sse_422_luma_32w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_422_LUMA_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_422_LUMA_SUM_4(0)
+ SAD_SSE_422_LUMA_SUM_4(8)
+ SAD_SSE_422_LUMA_SUM_4(16)
+ SAD_SSE_422_LUMA_SUM_4(24)
+ SAD_SSE_422_LUMA_SUM_4(32)
+ SAD_SSE_422_LUMA_SUM_4(40)
+ SAD_SSE_422_LUMA_SUM_4(48)
+ SAD_SSE_422_LUMA_SUM_4(56)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+
+}
+
+static int sad_sse_422_luma_64w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+ int result;
+
+ SAD_SSE_422_LUMA_INIT
+
+ while( h != 0 ) {
+ asm volatile (
+ SAD_SSE_422_LUMA_SUM_4(0)
+ SAD_SSE_422_LUMA_SUM_4(8)
+ SAD_SSE_422_LUMA_SUM_4(16)
+ SAD_SSE_422_LUMA_SUM_4(24)
+ SAD_SSE_422_LUMA_SUM_4(32)
+ SAD_SSE_422_LUMA_SUM_4(40)
+ SAD_SSE_422_LUMA_SUM_4(48)
+ SAD_SSE_422_LUMA_SUM_4(56)
+ SAD_SSE_422_LUMA_SUM_4(64)
+ SAD_SSE_422_LUMA_SUM_4(72)
+ SAD_SSE_422_LUMA_SUM_4(80)
+ SAD_SSE_422_LUMA_SUM_4(88)
+ SAD_SSE_422_LUMA_SUM_4(96)
+ SAD_SSE_422_LUMA_SUM_4(104)
+ SAD_SSE_422_LUMA_SUM_4(112)
+ SAD_SSE_422_LUMA_SUM_4(120)
+
+ :: "r" (block1), "r" (block2)
+ );
+
+ h--;
+ block1 += ystride;
+ block2 += ystride;
+ }
+ SAD_SSE_FINISH(result)
+ return result;
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltnormalize$(LIBSUF)
+
+OBJS = factory.o \
+ filter_volume.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "volume", filter_volume_init );
+}
--- /dev/null
+/*
+ * filter_volume.c -- adjust audio volume
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+
+#define MAX_CHANNELS 6
+#define EPSILON 0.00001
+
+/* The following normalise functions come from the normalize utility:
+ Copyright (C) 1999--2002 Chris Vaill */
+
+#define samp_width 16
+
+#ifndef ROUND
+# define ROUND(x) floor((x) + 0.5)
+#endif
+
+#define DBFSTOAMP(x) pow(10,(x)/20.0)
+
+/** Return nonzero if the two strings are equal, ignoring case, up to
+ the first n characters.
+*/
+int strncaseeq(const char *s1, const char *s2, size_t n)
+{
+ for ( ; n > 0; n--)
+ {
+ if (tolower(*s1++) != tolower(*s2++))
+ return 0;
+ }
+ return 1;
+}
+
+/** Limiter function.
+
+ / tanh((x + lev) / (1-lev)) * (1-lev) - lev (for x < -lev)
+ |
+ x' = | x (for |x| <= lev)
+ |
+ \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev)
+
+ With limiter level = 0, this is equivalent to a tanh() function;
+ with limiter level = 1, this is equivalent to clipping.
+*/
+static inline double limiter( double x, double lmtr_lvl )
+{
+ double xp = x;
+
+ if (x < -lmtr_lvl)
+ xp = tanh((x + lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) - lmtr_lvl;
+ else if (x > lmtr_lvl)
+ xp = tanh((x - lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) + lmtr_lvl;
+
+// if ( x != xp )
+// fprintf( stderr, "filter_volume: sample %f limited %f\n", x, xp );
+
+ return xp;
+}
+
+
+/** Takes a full smoothing window, and returns the value of the center
+ element, smoothed.
+
+ Currently, just does a mean filter, but we could do a median or
+ gaussian filter here instead.
+*/
+static inline double get_smoothed_data( double *buf, int count )
+{
+ int i, j;
+ double smoothed = 0;
+
+ for ( i = 0, j = 0; i < count; i++ )
+ {
+ if ( buf[ i ] != -1.0 )
+ {
+ smoothed += buf[ i ];
+ j++;
+ }
+ }
+ smoothed /= j;
+// fprintf( stderr, "smoothed over %d values, result %f\n", j, smoothed );
+
+ return smoothed;
+}
+
+/** Get the max power level (using RMS) and peak level of the audio segment.
+ */
+double signal_max_power( int16_t *buffer, int channels, int samples, int16_t *peak )
+{
+ // Determine numeric limits
+ int bytes_per_samp = (samp_width - 1) / 8 + 1;
+ int16_t max = (1 << (bytes_per_samp * 8 - 1)) - 1;
+ int16_t min = -max - 1;
+
+ double *sums = (double *) calloc( channels, sizeof(double) );
+ int c, i;
+ int16_t sample;
+ double pow, maxpow = 0;
+
+ /* initialize peaks to effectively -inf and +inf */
+ int16_t max_sample = min;
+ int16_t min_sample = max;
+
+ for ( i = 0; i < samples; i++ )
+ {
+ for ( c = 0; c < channels; c++ )
+ {
+ sample = *buffer++;
+ sums[ c ] += (double) sample * (double) sample;
+
+ /* track peak */
+ if ( sample > max_sample )
+ max_sample = sample;
+ else if ( sample < min_sample )
+ min_sample = sample;
+ }
+ }
+ for ( c = 0; c < channels; c++ )
+ {
+ pow = sums[ c ] / (double) samples;
+ if ( pow > maxpow )
+ maxpow = pow;
+ }
+
+ free( sums );
+
+ /* scale the pow value to be in the range 0.0 -- 1.0 */
+ maxpow /= ( (double) min * (double) min);
+
+ if ( -min_sample > max_sample )
+ *peak = min_sample / (double) min;
+ else
+ *peak = max_sample / (double) max;
+
+ return sqrt( maxpow );
+}
+
+/* ------ End normalize functions --------------------------------------- */
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties of the a frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+ double gain = mlt_properties_get_double( properties, "volume.gain" );
+ double max_gain = mlt_properties_get_double( properties, "volume.max_gain" );
+ double limiter_level = 0.5; /* -6 dBFS */
+ int normalise = mlt_properties_get_int( properties, "volume.normalise" );
+ double amplitude = mlt_properties_get_double( properties, "volume.amplitude" );
+ int i, j;
+ double sample;
+ int16_t peak;
+
+ // Get the filter from the frame
+ mlt_filter this = mlt_properties_get_data( properties, "filter_volume", NULL );
+
+ // Get the properties from the filter
+ mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
+
+ if ( mlt_properties_get( properties, "volume.limiter" ) != NULL )
+ limiter_level = mlt_properties_get_double( properties, "volume.limiter" );
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+// fprintf( stderr, "filter_volume: frequency %d\n", *frequency );
+
+ // Determine numeric limits
+ int bytes_per_samp = (samp_width - 1) / 8 + 1;
+ int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1;
+ int samplemin = -samplemax - 1;
+
+ if ( normalise )
+ {
+ int window = mlt_properties_get_int( filter_props, "window" );
+ double *smooth_buffer = mlt_properties_get_data( filter_props, "smooth_buffer", NULL );
+
+ if ( window > 0 && smooth_buffer != NULL )
+ {
+ int smooth_index = mlt_properties_get_int( filter_props, "_smooth_index" );
+
+ // Compute the signal power and put into smoothing buffer
+ smooth_buffer[ smooth_index ] = signal_max_power( *buffer, *channels, *samples, &peak );
+// fprintf( stderr, "filter_volume: raw power %f ", smooth_buffer[ smooth_index ] );
+ if ( smooth_buffer[ smooth_index ] > EPSILON )
+ {
+ mlt_properties_set_int( filter_props, "_smooth_index", ( smooth_index + 1 ) % window );
+
+ // Smooth the data and compute the gain
+// fprintf( stderr, "smoothed %f over %d frames\n", get_smoothed_data( smooth_buffer, window ), window );
+ gain *= amplitude / get_smoothed_data( smooth_buffer, window );
+ }
+ }
+ else
+ {
+ gain *= amplitude / signal_max_power( *buffer, *channels, *samples, &peak );
+ }
+ }
+
+// if ( gain > 1.0 && normalise )
+// fprintf(stderr, "filter_volume: limiter level %f gain %f\n", limiter_level, gain );
+
+ if ( max_gain > 0 && gain > max_gain )
+ gain = max_gain;
+
+ // Initialise filter's previous gain value to prevent an inadvertant jump from 0
+ mlt_position last_position = mlt_properties_get_position( filter_props, "_last_position" );
+ mlt_position current_position = mlt_frame_get_position( frame );
+ if ( mlt_properties_get( filter_props, "_previous_gain" ) == NULL
+ || current_position != last_position + 1 )
+ mlt_properties_set_double( filter_props, "_previous_gain", gain );
+
+ // Start the gain out at the previous
+ double previous_gain = mlt_properties_get_double( filter_props, "_previous_gain" );
+
+ // Determine ramp increment
+ double gain_step = ( gain - previous_gain ) / *samples;
+// fprintf( stderr, "filter_volume: previous gain %f current gain %f step %f\n", previous_gain, gain, gain_step );
+
+ // Save the current gain for the next iteration
+ mlt_properties_set_double( filter_props, "_previous_gain", gain );
+ mlt_properties_set_position( filter_props, "_last_position", current_position );
+
+ // Ramp from the previous gain to the current
+ gain = previous_gain;
+
+ int16_t *p = *buffer;
+
+ // Apply the gain
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ sample = *p * gain;
+ *p = ROUND( sample );
+
+ if ( gain > 1.0 )
+ {
+ /* use limiter function instead of clipping */
+ if ( normalise )
+ *p = ROUND( samplemax * limiter( sample / (double) samplemax, limiter_level ) );
+
+ /* perform clipping */
+ else if ( sample > samplemax )
+ *p = samplemax;
+ else if ( sample < samplemin )
+ *p = samplemin;
+ }
+ p++;
+ }
+ gain += gain_step;
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+ mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
+
+ // Parse the gain property
+ if ( mlt_properties_get( properties, "gain" ) == NULL )
+ {
+ double gain = 1.0; // no adjustment
+
+ if ( mlt_properties_get( filter_props, "gain" ) != NULL )
+ {
+ char *p = mlt_properties_get( filter_props, "gain" );
+
+ if ( strncaseeq( p, "normalise", 9 ) )
+ mlt_properties_set( filter_props, "normalise", "" );
+ else
+ {
+ if ( strcmp( p, "" ) != 0 )
+ gain = fabs( strtod( p, &p) );
+
+ while ( isspace( *p ) )
+ p++;
+
+ /* check if "dB" is given after number */
+ if ( strncaseeq( p, "db", 2 ) )
+ gain = DBFSTOAMP( gain );
+
+ // If there is an end adjust gain to the range
+ if ( mlt_properties_get( filter_props, "end" ) != NULL )
+ {
+ // Determine the time position of this frame in the transition duration
+ mlt_position in = mlt_filter_get_in( this );
+ mlt_position out = mlt_filter_get_out( this );
+ mlt_position time = mlt_frame_get_position( frame );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+
+ double end = -1;
+ char *p = mlt_properties_get( filter_props, "end" );
+ if ( strcmp( p, "" ) != 0 )
+ end = fabs( strtod( p, &p) );
+
+ while ( isspace( *p ) )
+ p++;
+
+ /* check if "dB" is given after number */
+ if ( strncaseeq( p, "db", 2 ) )
+ end = DBFSTOAMP( gain );
+
+ if ( end != -1 )
+ gain += ( end - gain ) * position;
+ }
+ }
+ }
+ mlt_properties_set_double( properties, "volume.gain", gain );
+ }
+
+ // Parse the maximum gain property
+ if ( mlt_properties_get( filter_props, "max_gain" ) != NULL )
+ {
+ char *p = mlt_properties_get( filter_props, "max_gain" );
+ double gain = fabs( strtod( p, &p) ); // 0 = no max
+
+ while ( isspace( *p ) )
+ p++;
+
+ /* check if "dB" is given after number */
+ if ( strncaseeq( p, "db", 2 ) )
+ gain = DBFSTOAMP( gain );
+
+ mlt_properties_set_double( properties, "volume.max_gain", gain );
+ }
+
+ // Parse the limiter property
+ if ( mlt_properties_get( filter_props, "limiter" ) != NULL )
+ {
+ char *p = mlt_properties_get( filter_props, "limiter" );
+ double level = 0.5; /* -6dBFS */
+ if ( strcmp( p, "" ) != 0 )
+ level = strtod( p, &p);
+
+ while ( isspace( *p ) )
+ p++;
+
+ /* check if "dB" is given after number */
+ if ( strncaseeq( p, "db", 2 ) )
+ {
+ if ( level > 0 )
+ level = -level;
+ level = DBFSTOAMP( level );
+ }
+ else
+ {
+ if ( level < 0 )
+ level = -level;
+ }
+ mlt_properties_set_double( properties, "volume.limiter", level );
+ }
+
+ // Parse the normalise property
+ if ( mlt_properties_get( filter_props, "normalise" ) != NULL )
+ {
+ char *p = mlt_properties_get( filter_props, "normalise" );
+ double amplitude = 0.2511886431509580; /* -12dBFS */
+ if ( strcmp( p, "" ) != 0 )
+ amplitude = strtod( p, &p);
+
+ while ( isspace( *p ) )
+ p++;
+
+ /* check if "dB" is given after number */
+ if ( strncaseeq( p, "db", 2 ) )
+ {
+ if ( amplitude > 0 )
+ amplitude = -amplitude;
+ amplitude = DBFSTOAMP( amplitude );
+ }
+ else
+ {
+ if ( amplitude < 0 )
+ amplitude = -amplitude;
+ if ( amplitude > 1.0 )
+ amplitude = 1.0;
+ }
+
+ // If there is an end adjust gain to the range
+ if ( mlt_properties_get( filter_props, "end" ) != NULL )
+ {
+ // Determine the time position of this frame in the transition duration
+ mlt_position in = mlt_filter_get_in( this );
+ mlt_position out = mlt_filter_get_out( this );
+ mlt_position time = mlt_frame_get_position( frame );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ amplitude *= position;
+ }
+ mlt_properties_set_int( properties, "volume.normalise", 1 );
+ mlt_properties_set_double( properties, "volume.amplitude", amplitude );
+ }
+
+ // Parse the window property and allocate smoothing buffer if needed
+ int window = mlt_properties_get_int( filter_props, "window" );
+ if ( mlt_properties_get( filter_props, "smooth_buffer" ) == NULL && window > 1 )
+ {
+ // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalisation
+ double *smooth_buffer = (double*) calloc( window, sizeof( double ) );
+ int i;
+ for ( i = 0; i < window; i++ )
+ smooth_buffer[ i ] = -1.0;
+ mlt_properties_set_data( filter_props, "smooth_buffer", smooth_buffer, 0, free, NULL );
+ }
+
+ // Put a filter reference onto the frame
+ mlt_properties_set_data( properties, "filter_volume", this, 0, NULL, NULL );
+
+ // Override the get_audio method
+ mlt_frame_push_audio( frame, filter_get_audio );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
+ if ( this != NULL && mlt_filter_init( this, NULL ) == 0 )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ this->process = filter_process;
+ if ( arg != NULL )
+ mlt_properties_set( properties, "gain", arg );
+
+ mlt_properties_set_int( properties, "window", 75 );
+ mlt_properties_set( properties, "max_gain", "20dB" );
+ }
+ return this;
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltoldfilm$(LIBSUF)
+
+OBJS = factory.o \
+ filter_oldfilm.o \
+ filter_dust.o \
+ filter_lines.o \
+ filter_grain.o \
+ filter_tcolor.o \
+ filter_vignette.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+ install -d $(DESTDIR)$(prefix)/share/mlt/oldfilm
+ install -m 644 *.svg "$(DESTDIR)$(prefix)/share/mlt/oldfilm"
+ install -m 644 *.yml "$(DESTDIR)$(prefix)/share/mlt/oldfilm"
+
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /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://web.resource.org/cc/"
+ 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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="240"
+ height="217.14285"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="dust1.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="291.42857"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="586"
+ inkscape:window-x="436"
+ inkscape:window-y="136" />
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3134">
+ <stop
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;"
+ id="stop3136" />
+ <stop
+ offset="1"
+ style="stop-color:#001b00;stop-opacity:0;"
+ id="stop3138" />
+ </linearGradient>
+ <radialGradient
+ r="120"
+ id="radialGradient3140"
+ fx="314.28571"
+ fy="352.36218"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ cy="352.36218"
+ cx="314.28571"
+ xlink:href="#linearGradient3134"
+ gradientTransform="matrix(1,0,0,3.824003,0,-899.71827)" />
+ </defs>
+ <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
+ transform="translate(-194.28571,-243.79076)"
+ inkscape:label="Ebene 1"
+ id="layer1"
+ inkscape:groupmode="layer">
+ <path
+ id="path2171"
+ d="M 434.28571,447.71572 L 430.39845,446.98174 L 426.51105,447.70281 L 422.63086,444.95151 L 418.7369,447.33 L 415.17888,439.03832 L 412.09038,427.88049 L 408.04927,427.373 L 404.01654,426.16139 L 399.94798,424.73438 L 396.42874,433.48045 L 392.63006,438.51018 L 388.67834,436.48311 L 385.04569,429.98727 L 381.14982,427.38011 L 377.22038,430.04453 L 373.32102,426.68233 L 369.57046,421.84943 L 366.19199,413.43864 L 362.28568,414.01743 L 358.41557,411.70186 L 354.37205,414.23864 L 350.36416,417.63832 L 346.46571,411.53673 L 343.35113,399.8993 L 339.53706,393.48698 L 335.97202,384.88788 L 331.83892,385.51799 L 327.91078,390.9871 L 323.71372,391.53811 L 319.89063,384.19765 L 316.99236,371.9895 L 314.28571,359.01937 L 310.03453,356.25903 L 306.24091,347.69311 L 302.06907,343.31261 L 297.78709,344.91141 L 293.67116,336.67107 L 291.59835,319.53116 L 287.10897,318.6664 L 283.4444,307.67153 L 280.04266,294.45327 L 276.07896,284.41415 L 271.53901,279.72473 L 266.90066,282.14486 L 262.2405,285.1173 L 257.57444,282.31501 L 253.08029,290.12947 L 250.29693,306.96613 L 245.94295,304.25049 L 241.56118,305.99325 L 236.95654,305.68247 L 233.22692,317.10051 L 228.78363,307.65672 L 224.00778,313.52323 L 220.67578,327.30269 L 216.67116,337.38838 L 216.28682,356.83475 L 214.77646,375.27519 L 210.68738,385.66713 L 208.55789,403.71361 L 204.2669,410.01158 L 200.22848,418.80047 L 195.90418,428.29105 L 194.28571,447.71572 L 198.62042,451.1118 L 202.83258,456.61128 L 207.52564,457.54492 L 211.21379,445.24351 L 215.01113,434.95466 L 219.46816,432.02441 L 222.58281,418.55997 L 224.71894,402.03506 L 228.28989,393.27594 L 232.3716,390.64936 L 235.43763,377.86937 L 239.72488,376.16203 L 243.34806,366.44008 L 247.61567,368.35802 L 251.57005,364.6111 L 255.55167,361.42094 L 259.82848,362.36269 L 263.77023,355.28606 L 267.94113,354.12226 L 272.11574,355.01674 L 274.69118,340.04655 L 278.35161,329.88559 L 282.69754,328.43278 L 286.73607,321.49359 L 290.72647,316.70065 L 294.42096,308.72595 L 298.44502,301.8398 L 300.7415,286.26813 L 303.33482,272.24893 L 307.18187,265.01712 L 311.16529,259.69604 L 314.28571,247.95603 L 317.70857,259.01241 L 321.90346,263.15776 L 324.16016,278.97807 L 328.02132,287.63463 L 330.16565,304.13419 L 334.23472,311.78945 L 337.48222,325.11445 L 342.00603,325.72722 L 343.08521,344.83013 L 346.64696,357.4439 L 351.04337,360.80678 L 354.97171,369.80216 L 358.55898,378.84144 L 362.09284,388.24908 L 364.20762,404.75745 L 368.33325,411.71191 L 372.37551,402.09016 L 376.70855,395.08488 L 381.38937,392.28894 L 385.94136,397.67996 L 390.80528,391.4668 L 395.50331,399.6484 L 399.72846,389.6019 L 402.99765,374.47207 L 407.33054,381.30715 L 410.66368,394.85799 L 415.33507,395.57009 L 419.35168,405.67599 L 421.4868,423.88654 L 425.40104,435.69199 L 430.12905,437.93294 L 434.28571,447.71572"
+ style="opacity:1;fill:url(#radialGradient3140);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.80000019;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="194.28572"
+ height="257.14285"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="dust2.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ff4f64"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.43137255"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.248863"
+ inkscape:cx="100.71553"
+ inkscape:cy="161.55164"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1043"
+ inkscape:window-height="804"
+ inkscape:window-x="387"
+ inkscape:window-y="16" />
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3147">
+ <stop
+ offset="0"
+ style="stop-color:#fffdff;stop-opacity:1;"
+ id="stop3149" />
+ <stop
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0.30222222;"
+ id="stop3151" />
+ </linearGradient>
+ <radialGradient
+ r="97.14286"
+ id="radialGradient3155"
+ fx="274.28571"
+ fy="335.21933"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ cy="335.21933"
+ cx="274.28571"
+ xlink:href="#linearGradient3147"
+ gradientTransform="matrix(1,0,0,1.3235293,0,-108.45329)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3147"
+ id="radialGradient4250"
+ cx="277.85837"
+ cy="302.23911"
+ fx="277.85837"
+ fy="302.23911"
+ r="91.162598"
+ gradientTransform="matrix(1,0,0,0.4733379,0,159.17788)"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="reflect" />
+ </defs>
+ <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
+ transform="translate(-177.14285,-206.6479)"
+ inkscape:label="Ebene 1"
+ id="layer1"
+ inkscape:groupmode="layer">
+ <path
+ style="opacity:1;stroke-opacity:1;fill-rule:nonzero;fill-opacity:1;stroke:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-width:9.80000019;fill:url(#radialGradient3155)"
+ id="path2261"
+ d="M-463.907729645 59.4947232865L-465.88997919 53.0618899264L-467.549378959 46.5383133996L-469.743557035 39.9685086158L-468.666248439 33.1262751708L-466.83275019 26.2872473927L-464.142489942 19.7377041588L-464.524207184 12.4160838201L-468.615066603 6.33194857812L-468.910847311 -0.683251608856L-470.528779939 -7.51573387404L-475.092658479 -12.8099924273L-479.739067212 -18.0319697201L-485.73817452 -21.945195162L-491.476673252 -26.2314819186L-495.419964008 -32.6422392782L-494.847794523 -40.1469027611L-492.480636045 -47.3546670613L-491.9760398 -54.9243888592L-487.59770564 -61.5755338577L-480.048594523 -64.1090020642L-480.681659877 -71.3075932316L-483.088938826 -78.121218889L-482.704413791 -85.3251250446L-480.732250787 -92.2644835516L-480.193664405 -99.1533349308L-480.563047873 -106.053328008L-481.025575984 -113.263921496L-485.519721132 -118.921593287L-487.829778625 -125.3369764L-488.982048988 -132.057525418L-490.622429463 -138.759025882L-494.477291489 -144.481000656L-490.017834216 -150.068529921L-486.181732441 -156.10107214L-488.547366928 -163.013327043L-487.825680898 -170.283448339L-483.680993685 -176.261759704L-477.054071133 -179.262182402L-473.931082668 -185.714303117L-473.702723282 -192.878852696L-475.469781914 -199.784629318L-477.829700823 -206.510923223L-477.23499722 -214.07026968L-471.904284019 -219.46293952L-465.900025338 -223.606434671L-462.736688564 -230.180092316L-460.270934889 -237.140901407L-462.186942229 -244.272641747L-466.091911117 -251.164436042L-473.420709869 -254.170150512L-476.639538032 -261.370949763L-474.91411937 -269.067396674L-476.902252287 -276.267891846L-473.768056392 -283.048497964L-471.927801573 -289.824732823L-469.462772581 -296.399497362L-473.63808065 -302.194623254L-473.385435919 -309.332749914L-470.992019665 -316.077927348L-473.315226176 -322.847607657L-469.159901772 -328.49003245L-468.226046876 -335.434929388L-466.477069188 -342.175668962L-467.83251643 -349.006427025L-468.619692204 -342.861624953L-469.232433338 -336.696984939L-469.270127678 -330.399469569L-471.539300702 -324.52486537L-469.175868919 -318.623388776L-469.451228748 -312.272214651L-469.494680958 -305.649307059L-465.230740834 -300.581407327L-466.848690529 -294.42110035L-465.396556407 -288.219612646L-464.671633164 -282.020530147L-462.277656517 -276.256589365L-457.32019267 -272.060157806L-455.620890942 -265.791277581L-452.61976616 -260.162976606L-452.506823585 -253.785534658L-448.590863545 -248.313985908L-442.40244285 -245.67276686L-436.775329639 -241.776492435L-434.694933516 -235.255962589L-430.649188281 -230.08736824L-426.360593181 -225.118427295L-426.522251677 -218.24464111L-422.736390033 -212.505100921L-421.184044907 -205.52226576L-416.037969794 -200.553596602L-414.115707783 -193.510946069L-417.221248935 -186.904159263L-417.863146941 -180.340520036L-419.806708769 -174.038460026L-423.287581644 -168.296267035L-424.069044195 -161.627041247L-428.887255151 -156.278326662L-434.045287782 -151.256513851L-433.326629484 -143.830959692L-436.493579921 -137.076276188L-437.682349818 -130.098447357L-437.141816316 -123.040749956L-435.698813185 -116.074162685L-432.15564799 -109.90475618L-436.107369158 -104.582431036L-441.225344756 -100.369407072L-445.873539454 -95.7110641291L-450.663205747 -91.1983077948L-456.689257495 -87.7139504068L-460.611723363 -81.9634454213L-460.944089225 -74.9120245456L-464.955139375 -69.103024621L-465.072247513 -62.0229138732L-462.619857147 -55.3800632798L-466.83973372 -49.3915250869L-466.686726973 -42.06714546L-471.932610662 -36.8701051203L-472.475682082 -29.5057587537L-475.351295643 -22.6593122799L-472.818255363 -15.6788617489L-468.420393858 -10.3894993088L-466.007500221 -3.94772275733L-462.565501903 2.02266925624L-457.257152241 6.41747950113L-455.108038321 13.1308446653L-449.705425696 17.6585084437L-446.615339789 23.6840617883L-443.691410505 29.7919715425L-443.834112479 27.6258854399L-444.622542121 25.6033445736L-445.953081595 23.7975458469L-446.0491224 21.5565593461L-446.983100742 19.5267545249L-446.689395405 17.3117691233L-446.889202368 15.1341090161L-447.892328034 13.190950437L-447.54431353 10.9365319366L-447.355692539 8.66322179673L-448.319103587 6.42725471482L-447.57495036 4.10907749707L-445.818960067 2.31007059794L-445.593591355 -0.193753811675L-443.169512041 -0.794859146968L-441.594740826 -2.7333079502L-441.751576788 -5.1941918928L-440.347046752 -7.22097325163L-440.726728996 -9.55444992462L-441.874587681 -11.6212572274L-441.279520581 -13.9047599671L-439.438387611 -15.380808409L-437.432020582 -16.5135110874L-436.161599633 -18.4356335139L-435.92862693 -20.6173392212L-435.42668271 -22.753262458L-435.289712269 -25.0248976546L-433.983265008 -26.8883039232L-433.401121503 -29.2199353346L-434.254295445 -31.466597375L-435.111107727 -33.726545982L-437.151631256 -35.0218292415L-436.066282761 -36.9725956002L-434.851799059 -38.8456946579L-434.261655739 -41.0107210837L-433.223767866 -43.0002932298L-431.126756235 -44.2246334879L-429.933945352 -46.3397384612L-427.520250123 -46.0469733464L-425.297751369 -47.0329425968L-424.522781181 -49.3719034696L-422.41135462 -50.6420194625L-420.152486655 -51.1432050003L-417.853768022 -51.4069550266L-416.343748636 -52.9997806889L-414.732249004 -54.4898548719L-413.142907066 -56.1717566343L-412.890969446 -58.4720454787L-411.787687693 -60.5370227362L-412.242940004 -62.8335651446L-410.712191286 -64.4296673016L-409.449633098 -66.2453436368L-408.595453403 -68.2653602801L-408.543146068 -70.4579282112L-407.465522226 -72.3239843987L-406.320615683 -74.1495330409L-407.826904864 -76.02385871L-407.945837444 -78.425495413L-408.36933663 -80.7099492081L-407.976077791 -82.9998025194L-407.655011284 -85.3917960569L-405.808786488 -86.9461997334L-404.873082686 -88.9962164888L-403.756626889 -90.9536731947L-397.015159322 -91.0875840666L-390.409053459 -92.4383921199L-383.620259345 -93.8219196157L-376.994491283 -91.7968093889L-370.108965629 -91.7478139483L-363.235893609 -92.1646413613L-356.987905447 -89.2793105236L-350.793396387 -86.2808863355L-343.842106007 -85.5492613123L-337.846546474 -81.9563545468L-332.833046291 -77.1444674512L-326.396322465 -74.5256837838L-320.822682615 -69.3981094471L-313.365931136 -68.0735955266L-311.047140319 -61.1767629572L-308.174302228 -54.4917130963L-306.055039952 -48.0460317659L-305.089013164 -41.3300156367L-299.578942435 -37.2714270775L-295.592428915 -31.7089878212L-289.895297312 -27.9775705903L-285.639189489 -22.6609617795L-281.415140784 -16.9781719087L-281.393337828 -9.89747894781L-279.283967767 -3.30117966192L-275.372610809 2.41387878645L-271.347798333 8.20347366812L-265.11734486 11.5049845176L-259.515641334 15.5179817881L-253.976959999 19.6175230504L-247.375812407 21.7977822101L-240.433782143 22.167775948L-239.544101582 29.4445099406L-242.97202649 35.924611786L-242.064651312 42.8609559472L-240.932582714 49.7641885758L-235.875328983 55.3471574642L-235.685237064 62.8777100318L-232.12551614 69.3802882778L-225.456960119 72.6184200711L-219.734331322 76.9985748797L-212.546927371 77.5234186006L-206.655383583 81.7414625545L-204.281614953 88.5874398762L-202.30547674 95.1905802403L-198.477159637 100.922122185L-193.660055705 105.827832694L-187.733919809 109.313718409L-182.636160048 115.376982806L-182.453614469 123.29638978L-180.99130228 130.963204121L-184.00757162 138.161851102L-187.837601453 144.43631717L-193.19247332 149.472526087L-194.774067819 157.084692164L-200.952125162 161.804662608L-194.878176039 165.975823665L-190.712234237 172.053353709L-183.838431296 175.255536902L-179.976744528 181.781678135L-179.70717508 189.033679487L-179.707524286 196.290689282L-175.832563251 203.061642595L-177.631031812 210.652864013L-180.358123591 208.53676233L-183.803320815 208.750144593L-186.87372201 209.809469837L-189.649358458 211.496294007L-192.583175366 210.729191221L-195.593005129 210.359524987L-198.618901455 210.227625364L-201.64437624 210.368864214L-204.734675973 210.300945345L-207.825490993 210.263157502L-210.749350006 209.229731918L-213.801342219 208.679946962L-216.729400603 210.041902322L-219.92615288 209.584493111L-222.350567264 211.767511497L-225.599554575 212.063207005L-228.739810938 212.892946167L-231.950331853 213.385117439L-235.155858612 213.991415061L-238.157642158 215.269027736L-241.503032849 215.34158236L-244.554121011 213.96762309L-247.864645937 212.856056485L-251.076496236 214.226885889L-253.645603234 212.177454149L-256.796092225 211.242093076L-258.410648036 208.246765103L-261.505692815 206.832736128L-264.712676556 205.973103819L-267.110552773 203.676606036L-270.223625316 202.321910284L-271.875847769 199.356007014L-274.938364664 198.530965573L-278.029093095 197.818855413L-281.213587679 197.411447687L-284.368701276 198.004950004L-287.543728528 199.639966139L-291.00723176 198.76920717L-293.203261068 196.025206168L-296.595064481 195.104469596L-299.708539427 196.698131903L-302.684890145 198.535200815L-306.239611818 198.985809476L-309.642139815 197.862467333L-313.050764021 197.342308962L-315.829040105 195.300137149L-318.383425209 193.087822822L-321.436829683 191.640086984L-322.993255201 194.838351242L-326.301468505 196.144903726L-329.129589518 197.919157743L-331.460373071 200.309491152L-334.552324992 201.935547844L-337.013053599 204.415273993L-337.070893795 208.087065083L-339.466049913 210.870699841L-343.190919309 211.240383306L-346.325092863 213.28691061L-349.411626896 215.352829212L-351.416666196 218.479253594L-355.059179679 219.163910493L-358.486906456 217.754176941L-361.971268653 217.024557135L-365.344666436 215.887318206L-367.497455987 215.118464771L-369.140452102 213.52906167L-370.956846922 212.219664462L-372.586680972 210.684264789L-373.582755094 208.537109875L-374.756630394 206.481762054L-376.667672899 204.952112289L-379.087039004 204.579849594L-380.412740154 202.586469081L-381.848765139 200.671036583L-382.44933235 198.225768735L-384.379529127 196.608871638L-386.85074277 197.161228453L-389.373247355 196.939946876L-392.023342351 196.513171599L-393.691505691 194.41022802L-395.536117279 192.697574772L-396.472760733 190.361237447L-398.214891073 188.592344191L-400.393978306 187.402562571L-402.555552842 186.204999056L-404.999459756 185.839100375L-406.987317202 184.395269713L-409.301670059 183.57065158L-409.658809292 181.097743833L-408.945213044 178.703249825L-408.438424655 176.151456706L-409.713383331 173.883646537L-411.613665903 172.100126219L-411.782373285 169.499442387L-412.68444869 167.244265553L-413.866678827 165.122498721L-412.558795255 162.88642467L-412.062380327 160.343953664L-411.959640475 157.724783003L-413.016169027 155.32595874L-413.672415771 152.80133923L-414.541212917 150.34175492L-416.771654718 148.987123198L-418.851714884 147.411290898L-418.330558988 145.106933813L-417.657132366 142.84238935L-416.234235109 140.95782181L-414.855702462 139.040563572L-414.478702599 136.694236778L-414.767053241 134.335374292L-413.836449574 132.1714512L-413.110224194 129.93065156L-413.149501247 127.467441358L-414.523918352 125.422954917L-415.746304722 123.390388409L-416.582215979 121.170747128L-416.894014401 118.684463613L-415.645874389 116.511684176L-414.388953725 114.482804246L-413.533864173 112.254570599L-413.478493878 109.789706516L-414.221650015 107.438889685L-416.437888322 106.253459483L-417.794681612 104.137787257L-418.015690606 101.805456524L-418.492221524 99.5116540235L-417.070120913 97.4638685624L-417.317748055 94.9830468759L-417.767223284 95.8396999193L-417.922143721 96.7946250872L-417.658938212 97.787485909L-418.147471428 98.6910261949L-417.301037261 99.2909032575L-416.814989134 100.207451406L-416.882609931 101.219396491L-416.751915844 102.225142226L-415.921762019 102.759076832L-414.960332592 102.982448702L-414.262196509 103.724666009L-413.265607358 103.937011446L-412.735704401 104.761955569L-412.292972883 105.636780553L-411.777471754 106.51271637L-411.788356472 107.529026605L-411.311241608 108.278620776L-410.924989835 109.078832987L-411.057030184 109.981927129L-410.761080066 110.845308394L-410.507457033 111.701840902L-410.675743374 112.579139191L-410.788036755 113.456303577L-410.562441643 114.311367323L-410.6028685 115.207449498L-410.36848858 116.073280688L-410.650092067 116.913685341L-410.949756917 117.747820511L-410.480326073 118.540437468L-410.299521252 119.443718473L-410.516663021 120.325616809L-410.759483342 121.200793086L-410.231165342 122.003722639L-409.472202857 122.593461328L-408.464177327 122.706677108L-407.785610554 123.460653512L-407.23275184 124.271892541L-406.882346412 125.188940825L-405.97604137 125.564903249L-405.175863361 126.132751581L-404.29777269 126.686382126L-403.815317332 127.605504987L-403.158914412 128.363608374L-402.539129541 129.151930805L-403.043065313 130.098344903L-403.34817618 131.126235104L-402.817832682 132.076293196L-402.694794327 133.157374214L-401.748601706 133.177068849L-400.893856682 133.583368366L-399.97744352 133.71210377L-399.06507283 133.86691208L-398.033020291 133.775508341L-397.116144292 134.258028278L-396.723815166 135.199554114L-395.958683661 135.8740664L-395.052053046 135.478083389L-394.075965566 135.63943656L-393.291650272 136.192559725L-392.377602856 136.485156783L-391.461505526 136.325103352L-390.53170496 136.343056797L-389.551444331 136.319763258L-388.769911502 136.911928713" />
+ <path
+ id="path3275"
+ d="M 193.52657,268.85494 L 196.21468,268.3195 L 198.85198,267.71783 L 201.69355,267.96748 L 204.57393,267.98941 L 207.40716,267.76168 L 210.17135,268.16093 L 213.07609,268.11678 L 215.62066,268.85718 L 217.60818,269.97429 L 220.26216,270.59636 L 223.17753,270.55063 L 225.8528,271.16357 L 228.65319,270.66657 L 231.57332,270.90391 L 234.25862,271.44468 L 236.83056,272.12166 L 239.29588,271.28646 L 241.82144,270.50296 L 243.90868,269.31191 L 244.30342,267.70325 L 247.09102,267.17023 L 248.7518,265.87439 L 251.43723,265.13153 L 252.43375,263.62071 L 255.22434,263.12308 L 257.77501,262.34573 L 260.70964,262.36233 L 263.62717,262.53003 L 266.0192,261.69988 L 268.25858,260.75864 L 271.10496,260.41658 L 273.46908,259.51306 L 276.04712,259.20928 L 278.67872,259.0884 L 281.15376,259.61513 L 283.81642,259.71086 L 286.3649,260.14722 L 288.98848,259.86028 L 291.57093,259.47108 L 294.168,259.83222 L 296.48345,260.61287 L 298.20355,261.74339 L 300.9083,261.90419 L 303.51903,262.31034 L 306.13221,262.4557 L 308.6521,262.84865 L 311.11867,263.46936 L 313.82479,263.26833 L 314.59115,264.80821 L 316.49863,266.04191 L 319.07256,266.86811 L 322.05839,267.08082 L 323.95246,268.18416 L 326.42091,268.90524 L 329.27457,269.02076 L 331.72002,269.80523 L 334.45898,269.16804 L 337.30899,269.65104 L 339.90969,269.10352 L 342.42858,268.45827 L 345.48472,268.41841 L 347.81558,269.46197 L 350.48053,268.81708 L 353.4116,268.85494 L 355.28034,269.82676 L 356.03733,271.15224 L 355.81457,272.53663 L 356.94306,273.79198 L 359.16015,274.45565 L 360.96755,275.404 L 361.74233,276.75822 L 363.96322,277.55081 L 363.12037,278.88976 L 363.66231,280.27134 L 365.86107,281.13189 L 366.55513,282.52905 L 367.78192,283.75554 L 367.19975,285.10788 L 367.41773,286.46173 L 369.02098,287.52509 L 366.49292,288.31114 L 364.91092,289.61502 L 364.15483,291.08773 L 364.32723,292.61079 L 361.61539,292.99142 L 359.73825,294.09185 L 357.81457,295.08739 L 355.67752,295.95338 L 355.93972,297.50097 L 354.48713,298.85259 L 352.63219,300.04231 L 352.2208,301.56742 L 352.41957,303.13644 L 354.49724,304.26399 L 355.1223,305.74634 L 356.96311,306.91381 L 355.32924,307.91299 L 354.17254,309.08302 L 354.72621,310.42905 L 353.68427,311.69196 L 351.76986,312.49474 L 350.36118,313.54935 L 348.30903,314.38214 L 347.82055,315.72349 L 347.64283,317.03404 L 348.37142,318.29044 L 349.41034,319.52701 L 348.93789,320.85644 L 349.09324,322.24229 L 347.42678,323.3167 L 346.185,324.44236 L 345.48444,325.69123 L 345.64185,327.02915 L 346.87301,328.20178 L 347.3392,329.48617 L 347.86922,330.76366 L 345.62155,331.63092 L 344.73022,333.02273 L 344.17793,334.4614 L 345.283,335.80854 L 346.76921,336.9064 L 347.46325,338.20485 L 349.18693,339.19105 L 351.35007,339.89618 L 351.83986,341.17231 L 352.62311,342.40704 L 353.576,343.64207 L 353.4116,344.97267 L 350.54841,345.19741 L 347.67676,345.38983 L 345.28496,344.43492 L 342.30604,344.24988 L 339.35177,343.8842 L 336.40961,344.27613 L 334.63769,342.92192 L 331.65947,342.43295 L 329.92211,341.10893 L 326.93443,340.77916 L 325.4112,339.51604 L 323.69005,338.32575 L 322.52656,336.94173 L 320.49844,335.87052 L 318.86475,334.54313 L 316.0004,334.07271 L 313.28916,333.96062 L 310.5994,334.17243 L 308.31306,335.09828 L 305.43534,335.01307 L 302.70104,335.2819 L 300.35267,336.06817 L 297.61014,336.4021 L 294.89152,336.01756 L 291.93647,335.6717 L 289.76752,334.55784 L 286.8656,334.36764 L 284.07988,333.89847 L 281.0364,333.90561 L 278.03684,334.17754 L 275.16577,334.87599 L 273.46908,336.28342 L 270.97426,336.85112 L 268.36226,337.24573 L 266.66031,338.37655 L 264.44253,339.2227 L 261.73548,339.2416 L 259.25886,339.81847 L 256.45382,339.70266 L 254.13443,340.54295 L 251.34418,340.77439 L 248.76305,341.37952 L 246.63158,340.29445 L 243.68547,340.13281 L 241.07739,339.88262 L 238.44785,340.05959 L 235.83806,339.92643 L 233.21635,339.90729 L 230.257,339.67357 L 227.26481,339.65804 L 224.99804,338.63323 L 222.76561,337.58766 L 219.94304,337.85152 L 217.39346,338.54277 L 215.01369,339.49662 L 212.02504,339.5037 L 209.41943,339.87094 L 206.98346,340.4816 L 205.30771,341.64393 L 202.7935,342.25485 L 200.38026,342.84533 L 197.9433,343.40808 L 195.53661,344.03456 L 193.52657,344.97267 L 191.81721,343.98059 L 191.24652,342.67414 L 190.65904,341.37939 L 189.05398,340.35205 L 188.1617,339.06122 L 188.78644,337.72736 L 188.02932,336.47196 L 186.95015,335.28392 L 187.80815,333.83816 L 186.69578,332.44149 L 187.83049,331.07889 L 190.21868,330.28668 L 190.51922,328.89958 L 189.19138,327.69192 L 188.9004,326.37296 L 188.91654,325.04513 L 191.67272,324.8437 L 194.39736,324.54592 L 195.74182,323.19526 L 198.23407,322.42156 L 198.2201,320.92384 L 198.21507,319.4261 L 200.64772,318.57431 L 202.08178,317.23258 L 202.4686,315.90217 L 203.45589,314.66107 L 204.81833,313.52188 L 205.58694,312.23738 L 204.473,310.97693 L 204.12808,309.59816 L 204.19798,308.19855 L 205.25284,306.91381 L 206.63363,305.65025 L 207.27664,304.23174 L 205.66553,303.00202 L 205.44391,301.51172 L 204.24735,300.15147 L 204.73194,298.67384 L 202.20698,297.99463 L 200.81764,296.69135 L 198.27875,296.25753 L 195.77947,295.76381 L 194.03485,294.68043 L 192.92783,293.38437 L 191.86587,292.12578 L 189.79667,291.28538 L 188.75047,290.06783 L 187.50549,288.90349 L 187.41767,287.57527 L 188.15487,286.30443 L 189.00265,285.03766 L 190.79573,284.08376 L 192.3734,283.01488 L 193.16448,281.72606 L 193.84812,280.3582 L 192.75538,279.06639 L 193.30719,277.76615 L 193.68267,276.44853 L 192.87475,275.18688 L 192.00468,273.93678 L 191.06278,272.6497 L 191.56876,271.2961 L 191.70866,269.88824 L 193.52657,268.85494"
+ style="fill:url(#radialGradient4250);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1.71851885;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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="387.78088"
+ height="236.67728"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="dust3.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="520"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="586"
+ inkscape:window-x="359"
+ inkscape:window-y="103" />
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-112.24286,-169.15633)">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.80000019;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 117.14286,383.79075 C 132.89134,377.9933 141.35833,331.00385 162.85714,309.50504 C 179.04427,293.31791 204.56791,260.36922 214.28571,240.93361 C 221.87943,225.74618 269.62455,193.26419 288.57143,183.79075 C 322.78762,166.68266 384.04214,174.38325 414.28571,189.50504 C 443.33513,204.02975 470.5072,233.37658 482.85714,258.07647 C 494.64175,281.64568 500.51676,319.90009 488.57143,343.79075 C 472.23394,376.46573 461.06991,400.93361 420,400.93361 C 386.98682,400.93361 357.67527,390.56986 345.71429,366.6479 C 331.7481,338.71553 313.48288,313.61367 300,286.6479 C 271.13254,228.91297 304.25918,299.42543 294.28571,269.50504"
+ id="path2160" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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="99.410004"
+ height="233.62619"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="dust4.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="DiamondS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DiamondS"
+ style="overflow:visible">
+ <path
+ id="path3217"
+ d="M 0,-7.0710768 L -7.0710894,0 L 0,7.0710589 L 7.0710462,0 L 0,-7.0710768 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="scale(0.2,0.2)" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="DistanceIn"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="DistanceIn">
+ <g
+ id="g2300">
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:1.14999998;stroke-linecap:square"
+ d="M 0,0 L 2,0"
+ id="path2306" />
+ <path
+ style="fill:#000000;fill-rule:evenodd;stroke:none"
+ d="M 0,0 L 13,4 L 9,0 L 13,-4 L 0,0 z "
+ id="path2302" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square"
+ d="M 0,-4 L 0,40"
+ id="path2304" />
+ </g>
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Send"
+ style="overflow:visible">
+ <path
+ id="path3164"
+ 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(-0.3,0,0,-0.3,0.69,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4074468"
+ inkscape:cx="71.428571"
+ inkscape:cy="105.71429"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="586"
+ inkscape:window-x="875"
+ inkscape:window-y="0"
+ width="99.41px"
+ height="233.62619px"
+ showguides="true"
+ inkscape:guide-bbox="true" />
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-243.11429,-209.90743)">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Send);marker-mid:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 291.42857,218.07647 C 272.34606,222.088 251.42857,231.57861 251.42857,269.50504 C 251.42857,297.73622 256.73845,331.55337 268.57143,355.21933 C 278.43679,374.95004 253.6971,424.96798 245.71429,440.93361"
+ id="path2160" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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="172.63466"
+ height="265.65714"
+ id="svg2186"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="dust5.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs2188" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="520"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="586"
+ inkscape:window-x="0"
+ inkscape:window-y="0" />
+ <metadata
+ id="metadata2191">
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.17143,-216.67647)">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 185.71429,480.93361 C 188.96777,468.49843 168.57143,429.55044 168.57143,400.93361 C 168.57143,374.88777 199.28869,339.49909 208.57143,320.93361 C 222.78379,292.50889 246.00003,285.60027 277.14286,275.21933 C 307.18704,265.2046 334.28571,323.13529 334.28571,343.79075 C 334.28571,376.81996 350.98623,401.15478 317.14286,418.07647 C 290.79544,431.25018 254.79122,418.32922 231.42857,406.6479 C 210.48639,396.17681 220,341.45229 220,320.93361 C 220,279.3284 208.87913,269.19734 242.85714,235.21933 C 249.33287,228.7436 269.08286,222.10647 277.14286,218.07647"
+ id="path2194" />
+ </g>
+</svg>
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+#include <limits.h>
+
+extern mlt_filter filter_dust_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_grain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_lines_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_oldfilm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_tcolor_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+static mlt_properties oldfilm_metadata( mlt_service_type type, const char *id, void *data )
+{
+ char file[ PATH_MAX ];
+ snprintf( file, PATH_MAX, "%s/oldfilm/filter_%s.yml", mlt_environment( "MLT_DATA" ), id );
+ return mlt_properties_parse_yaml( file );
+}
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "oldfilm", filter_oldfilm_init );
+ MLT_REGISTER( filter_type, "dust", filter_dust_init );
+ MLT_REGISTER( filter_type, "lines", filter_lines_init );
+ MLT_REGISTER( filter_type, "grain", filter_grain_init );
+ MLT_REGISTER( filter_type, "tcolor", filter_tcolor_init );
+ MLT_REGISTER( filter_type, "vignette", filter_vignette_init );
+
+ MLT_REGISTER_METADATA( filter_type, "vignette", oldfilm_metadata, NULL );
+ MLT_REGISTER_METADATA( filter_type, "tcolor", oldfilm_metadata, NULL );
+ MLT_REGISTER_METADATA( filter_type, "grain", oldfilm_metadata, NULL );
+ MLT_REGISTER_METADATA( filter_type, "lines", oldfilm_metadata, NULL );
+ MLT_REGISTER_METADATA( filter_type, "dust", oldfilm_metadata, NULL );
+ MLT_REGISTER_METADATA( filter_type, "oldfilm", oldfilm_metadata, NULL );
+
+}
+
+
+
--- /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://web.resource.org/cc/"
+ 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="119.44395"
+ height="103.53123"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="dus.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="520"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1672"
+ inkscape:window-height="969"
+ inkscape:window-x="0"
+ inkscape:window-y="0" />
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-63.761736,-89.023088)">
+ <path
+ sodipodi:type="star"
+ style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path2160"
+ sodipodi:sides="4"
+ sodipodi:cx="88.571426"
+ sodipodi:cy="120.93361"
+ sodipodi:r1="28.571428"
+ sodipodi:r2="11.714285"
+ sodipodi:arg1="0.64350111"
+ sodipodi:arg2="1.4288993"
+ inkscape:flatsided="false"
+ inkscape:rounded="0.6"
+ inkscape:randomized="-0.046"
+ d="M 112.40071,138.48615 C 104.63523,149.2232 103.39252,131.84024 90.301427,133.56163 C 77.210335,135.28301 81.871947,152.01419 71.336774,143.68907 C 60.801601,135.36395 78.494219,134.93387 76.174183,122.07035 C 73.854148,109.20683 57.818168,114.22716 66.13398,103.91357 C 74.449793,93.59998 72.430606,110.63192 85.604689,108.83809 C 98.778772,107.04427 94.053041,90.946435 104.56934,98.710649 C 115.08564,106.47486 97.158906,106.41587 99.590228,119.11612 C 102.02155,131.81637 120.16619,127.74909 112.40071,138.48615 z " />
+ <path
+ sodipodi:type="star"
+ style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path2162"
+ sodipodi:sides="4"
+ sodipodi:cx="111.42857"
+ sodipodi:cy="178.07648"
+ sodipodi:r1="12.777531"
+ sodipodi:r2="5.2387872"
+ sodipodi:arg1="-0.46364761"
+ sodipodi:arg2="0.32175055"
+ inkscape:flatsided="true"
+ inkscape:rounded="0.6"
+ inkscape:randomized="-0.046"
+ d="M 122.44277,172.69754 C 127.84769,182.25699 126.41544,184.43858 116.88365,189.64343 C 107.35186,194.84828 104.6647,193.25522 99.636235,183.39008 C 94.607766,173.52495 95.553578,171.33947 105.19655,166.41559 C 114.83951,161.4917 117.03785,163.1381 122.44277,172.69754 z " />
+ <path
+ sodipodi:type="star"
+ style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path2164"
+ sodipodi:sides="4"
+ sodipodi:cx="168.57143"
+ sodipodi:cy="103.79075"
+ sodipodi:r1="12.777531"
+ sodipodi:r2="9.035079"
+ sodipodi:arg1="0.46364761"
+ sodipodi:arg2="1.2490458"
+ inkscape:flatsided="true"
+ inkscape:rounded="0.6"
+ inkscape:randomized="-0.046"
+ d="M 180.40305,108.92085 C 175.41043,118.48299 172.98728,120.00139 163.16132,115.65738 C 153.33537,111.31338 152.8374,107.92884 157.36569,97.901837 C 161.89398,87.874831 165.34367,86.870642 174.60621,92.239286 C 183.86875,97.60793 185.39567,99.358711 180.40305,108.92085 z " />
+ </g>
+</svg>
--- /dev/null
+/*
+ * filter_dust.c -- dust filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//#include <framework/mlt_filter.h>
+//#include <framework/mlt_frame.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <time.h>
+
+static void overlay_image(uint8_t *src, int src_width, int src_height , uint8_t * overlay, int overlay_width, int overlay_height, uint8_t * alpha , int xpos, int ypos, int upsidedown , int mirror ){
+ int x,y;
+
+ for (y=ypos;y<src_height ; y++){
+ if ( y>=0 && (y-ypos)<overlay_height ){
+ uint8_t *scanline_image=src+src_width*y*2;
+ int overlay_y = upsidedown ? ( overlay_height - ( y - ypos ) - 1 ) : ( y - ypos );
+ uint8_t *scanline_overlay=overlay + overlay_width * 2 * overlay_y;
+
+ for ( x = xpos ; x < src_width && x-xpos < overlay_width ;x++){
+ if ( x>0 ){
+ int overlay_x = mirror ? overlay_width - ( x - xpos ) -1 : ( x - xpos );
+ double alp=(double)*(alpha+ overlay_width * overlay_y + overlay_x )/255.0;
+ uint8_t* image_pixel = scanline_image + x * 2;
+ uint8_t* overlay_pixel = scanline_overlay + overlay_x * 2;
+
+ *image_pixel=(double)(*overlay_pixel)*alp+ (double)*image_pixel*(1.0-alp) ;
+ if (xpos%2==0)
+ image_pixel++;
+ else
+ image_pixel+=3;
+
+ mirror? overlay_pixel-- : overlay_pixel++;
+
+ *image_pixel=(double)(*(overlay_pixel))*alp + (double)(*image_pixel )*(1.0-alp) ;
+ }
+
+ }
+ }
+ }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ int maxdia = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "maxdiameter" );
+ int maxcount = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "maxcount" );
+
+ mlt_position in = mlt_filter_get_in( filter );
+ mlt_position out = mlt_filter_get_out( filter );
+ mlt_position time = mlt_frame_get_position( this );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+ // load svg
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ char *factory = mlt_properties_get( properties, "factory" );
+ char temp[1204]="";
+ sprintf( temp, "%s/oldfilm/", mlt_environment( "MLT_DATA" ) );
+
+ mlt_properties direntries=mlt_properties_new();
+ mlt_properties_dir_list(direntries,temp,"dust*.svg",1);
+
+ if (!maxcount)
+ return 0;
+ srand(position*10000);
+
+ int im=rand()%maxcount;
+ int piccount=mlt_properties_count(direntries);
+ while (im-- && piccount){
+
+ int picnum=rand()%piccount;
+
+ int y1=rand()%*height;
+ int x1=rand()%*width;
+ char resource[1024]="";
+ char savename[1024]="",savename1[1024]="", cachedy[100];
+ int dx=(*width*maxdia/100);
+ int luma_width,luma_height;
+ uint8_t *luma_image = NULL;
+ uint8_t *alpha =NULL;
+ int updown= rand()%2;
+ int mirror=rand()%2;
+
+ sprintf(resource,"%s",mlt_properties_get_value(direntries,picnum));
+ sprintf(savename,"cache-%d-%d",picnum,dx);
+ sprintf(savename1,"cache-alpha-%d-%d",picnum,dx);
+ sprintf(cachedy,"cache-dy-%d-%d",picnum,dx);
+
+ luma_image= mlt_properties_get_data( properties , savename , NULL );
+ alpha= mlt_properties_get_data( properties , savename1 , NULL );
+
+ if (luma_image == NULL || alpha == NULL ){
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+ mlt_producer producer = mlt_factory_producer( profile, factory, resource );
+
+ if ( producer != NULL )
+ {
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ mlt_properties_set( producer_properties, "eof", "loop" );
+ mlt_frame luma_frame = NULL;
+
+ if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 ){
+
+ mlt_properties_set_double ( MLT_FRAME_PROPERTIES ( luma_frame ) , "consumer_aspect_ratio" , 1.0 );
+ mlt_image_format luma_format = mlt_image_yuv422;
+ luma_width = dx;
+ luma_height = luma_width * mlt_properties_get_int( MLT_FRAME_PROPERTIES ( luma_frame ) , "height" ) / mlt_properties_get_int( MLT_FRAME_PROPERTIES ( luma_frame ) , "width" );
+
+ mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "best" );// none/nearest/tiles/hyper
+
+ mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 );
+ alpha =mlt_frame_get_alpha_mask(luma_frame);
+
+ uint8_t* savealpha=mlt_pool_alloc ( luma_width * luma_height );
+ uint8_t* savepic=mlt_pool_alloc ( luma_width * luma_height * 2);
+
+ if (savealpha && savepic ){
+ memcpy (savealpha, alpha , luma_width * luma_height );
+ memcpy (savepic, luma_image , luma_width * luma_height * 2);
+
+ mlt_properties_set_data ( properties , savename , savepic , sizeof(uint8_t*) , mlt_pool_release, NULL );
+ mlt_properties_set_data ( properties , savename1 , savealpha , sizeof(uint8_t*) , mlt_pool_release, NULL );
+ mlt_properties_set_int ( properties , cachedy , luma_height );
+
+ overlay_image(*image,*width,*height,luma_image,luma_width,luma_height, alpha, x1, y1 , updown , mirror );
+ }
+ mlt_frame_close( luma_frame );
+ }
+ mlt_producer_close( producer );
+ }
+ }else {
+ overlay_image ( *image , *width, *height , luma_image , dx , mlt_properties_get_int ( properties , cachedy ) , alpha , x1 , y1 , updown , mirror );
+ }
+ }
+ if (piccount>0 )
+ return 0;
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+
+ int h = *height;
+ int w = *width;
+ if (maxcount==0)
+ return 0;
+
+ int im=rand()%maxcount;
+
+ while (im-- ){
+ int type=im%2;
+ int y1=rand()%h;
+ int x1=rand()%w;
+ int dx=rand()%maxdia;
+ int dy=rand()%maxdia;
+ int x=0,y=0;//,v=0;
+ double v=0.0;
+ for ( x = -dx ; x < dx ; x++ )
+ for ( y = -dy ; y < dy ; y++ ) {
+ if ( x1+x < w && x1+x > 0 && y1+y < h && y1+y > 0 ){
+ uint8_t *pix=*image+(y+y1)*w*2+(x+x1)*2;
+ //v=(1.0-fabs(x)/dx)*(1.0-fabs(y)/dy);
+ v=pow((double)x/(double)dx*5.0,2.0)+pow((double)y/(double)dy*5.0,2.0);
+ if (v>10)
+ v=10;
+ v=1.0-(v/10.0);
+
+ switch(type){
+ case 0:
+ *pix-=(*pix)*v;
+ break;
+ case 1:
+ *pix+=(255-*pix)*v;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+
+mlt_filter filter_dust_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "maxdiameter", "2" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "maxcount", "10" );
+ }
+ return this;
+}
+
+
--- /dev/null
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: dust
+title: Dust
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+ - Video # this may produce video
+description: Add dust and specks to the Video, as in old movies
+icon:
+ filename: oldfilm/fdust.svg # relative to $MLT_DATA/modules/
+ content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - need to do some speed improvement.
+
+parameters:
+ - identifier: maxdiameter
+ title: Maximal Diameter
+ type: integer
+ description: Maximal diameter of a dust piece
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 2
+ mutable: no
+ widget: spinner
+ unit: %
+
+ - identifier: maxcount
+ title: Maximal number of dust
+ type: integer
+ description: How many dust pieces are on the image
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 400
+ default: 10
+ mutable: no
+ widget: spinner
+
--- /dev/null
+/*
+ * filter_grain.c -- grain filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a>b?a:b)
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+ int h = *height;
+ int w = *width;
+
+ mlt_position in = mlt_filter_get_in( filter );
+ mlt_position out = mlt_filter_get_out( filter );
+ mlt_position time = mlt_frame_get_position( this );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ srand(position*10000);
+
+ int noise = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "noise" );
+ double contrast = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "contrast" )/100.0;
+ double brightness = 127.0 * (mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "brightness" )-100.0)/100.0;
+
+ int x=0,y=0,pix=0;
+ for (x=0;x<w;x++)
+ for(y=0;y<h;y++){
+ uint8_t* pixel=(*image+(y)*w*2+(x)*2);
+ if (*pixel>20){
+ pix= MIN ( MAX ( ( (double)*pixel -127.0 ) * contrast + 127.0 + brightness , 0 ) , 255 ) ;
+ if (noise>0)
+ pix-=(rand()%noise-noise);
+
+ *pixel= MIN ( MAX ( pix , 0 ) , 255 );
+ }
+ //*(pixel+1)= MIN ( MAX ( ( (double)*(pixel+1) -127.0 ) * .5 + 127.0 , 0 ) , 255 ) ;
+ }
+ }
+
+ return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+mlt_filter filter_grain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "noise", "40" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "contrast", "160" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightness", "70" );
+ }
+ return this;
+}
+
--- /dev/null
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: grain
+title: Grain
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+ - Video # this may produce video
+description: Grain over the Image
+icon:
+ filename: oldfilm/grain.svg # relative to $MLT_DATA/modules/
+ content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - need to do some speed improvement.
+
+parameters:
+ - identifier: noise
+ title: Noise
+ type: integer
+ description: Maximal value of noise
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 200
+ default: 40
+ mutable: no
+ widget: spinner
+ unit: %
+
+ - identifier: contrast
+ title: Contrast
+ type: integer
+ description: Adjust contrast for the image
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 400
+ default: 160
+ mutable: no
+ widget: spinner
+
+ - identifier: brightness
+ title: Brightness
+ type: integer
+ description: Adjust brightness for the image
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 400
+ default: 70
+ mutable: no
+ widget: spinner
--- /dev/null
+/*
+ * filter_lines.c -- lines filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+ int h = *height;
+ int w = *width;
+
+ int width_line = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "width" );
+ int num = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "num" );
+ double maxdarker= (double)mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "darker" ) ;
+ double maxlighter=(double)mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "lighter" ) ;
+ //int frame = mlt_properties_get_int( this, "_position" );
+ char buf[256];
+ char typebuf[256];
+
+ mlt_position in = mlt_filter_get_in( filter );
+ mlt_position out = mlt_filter_get_out( filter );
+ mlt_position time = mlt_frame_get_position( this );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ srand(position*10000);
+ if (!width_line)
+ return 0;
+
+ while (num--){
+ int type=(rand()%3)+1;
+ int x1=(double)w*rand()/RAND_MAX;
+ int dx=rand()%width_line;
+ int x=0,y=0;
+ int ystart=rand()%h;
+ int yend=rand()%h;
+
+ sprintf(buf,"line%d",num);
+ sprintf(typebuf,"typeline%d",num);
+ maxlighter+=rand()%30-15;
+ maxdarker+=rand()%30-15;
+
+ if (mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf)==0){
+ mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf,x1);
+ }
+
+ if (mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf)==0 ){
+ mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),typebuf,type);
+ }
+
+
+ x1=mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf);
+ type=mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf);
+ if (position!=mlt_properties_get_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos")){
+ x1+=(rand()%11-5);
+ }
+
+ if (yend<ystart){
+ yend=h;
+ }
+
+ for (x = -dx ; x < dx && dx != 0 ; x++ )
+ for(y=ystart;y<yend;y++)
+ if (x+x1<w && x+x1>0){
+ uint8_t* pixel=(*image+(y)*w*2+(x+x1)*2);
+ double diff=1.0-fabs(x)/dx;
+ switch(type){
+ case 1: //blackline
+ *pixel-=((double)*pixel*diff*maxdarker/100.0);
+ break;
+ case 2: //whiteline
+ *pixel+=((255.0-(double)*pixel)*diff*maxlighter/100.0);
+ break;
+ case 3: //greenline
+ *(pixel+1)-=((*(pixel+1))*diff*maxlighter/100.0);
+ break;
+ }
+
+ }
+ mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf,x1);
+ }
+ mlt_properties_set_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos",position);
+ }
+
+ return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+mlt_filter filter_lines_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "width", "2" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "num", "5" );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "darker" , 40 ) ;
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "lighter" , 40 ) ;
+ }
+ return this;
+}
+
+
--- /dev/null
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: lines
+title: Scratchlines
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+ - Video # this may produce video
+description: Scratchlines over the Picture
+icon:
+ filename: oldfilm/lines.svg # relative to $MLT_DATA/modules/
+ content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - need to do some speed improvement.
+
+parameters:
+ - identifier: width
+ title: Width of line
+ type: integer
+ description: Linewidth in picture
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 2
+ mutable: no
+ widget: spinner
+ unit: pixel
+
+ - identifier: num
+ title: Max number of lines
+ type: integer
+ description: Maximal number of lines in picture
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 5
+ mutable: no
+ widget: spinner
+ unit: lines
+
+ - identifier: darker
+ title: Max darker
+ type: integer
+ description: Make image up to n values darker behind line
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 40
+ mutable: no
+ widget: spinner
+
+ - identifier: lighter
+ title: Max lighter
+ type: integer
+ description: Make image up to n values lighter behind line
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 40
+ mutable: no
+ widget: spinner
+
+
--- /dev/null
+/*
+ * filter_oldfilm.c -- oldfilm filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+ int h = *height;
+ int w = *width;
+
+ int x=0;
+ int y=0;
+
+ mlt_position in = mlt_filter_get_in( filter );
+ mlt_position out = mlt_filter_get_out( filter );
+ mlt_position time = mlt_frame_get_position( this );
+ double position = ( double )( time - in ) / ( double )( out - in + 1 );
+ srand(position*10000);
+
+ int delta = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "delta" );
+ int every = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "every" );
+
+ int bdu = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_up" );
+ int bdd = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_down" );
+ int bevery = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_every" );
+
+ int diffpic=0;
+ if (delta)
+ diffpic=rand()%delta*2-delta;
+
+ int brightdelta=0;
+ if ((bdu+bdd)!=0)
+ brightdelta=rand()%(bdu+bdd)-bdd;
+ if (rand()%100>every)
+ diffpic=0;
+ if (rand()%100>bevery)
+ brightdelta=0;
+ int yend,ydiff;
+ if (diffpic<=0){
+ y=h;
+ yend=0;
+ ydiff=-1;
+ }else{
+ y=0;
+ yend=h;
+ ydiff=1;
+ }
+
+ while(y!=yend){
+ //int newy=y+diffpic;
+ for (x=0;x<w;x++){
+ uint8_t* pic=(*image+y*w*2+x*2);
+ int newy=y+diffpic;
+ if (newy>0 && newy<h ){
+ uint8_t oldval=*(pic+diffpic*w*2);
+ /* frame around
+ int randx=(x<=frameborder)?x:(x+frameborder>w)?w-x:-1;
+ int randy=((newy)<=frameborder)?(newy):((newy)+frameborder>h)?h-(y+diffpic):-1;
+ if (randx>=0 ){
+ oldval=oldval*pow(((double)randx/(double)frameborder),1.5);
+ }
+ if (randy>=0 ){
+ oldval=oldval*pow(((double)randy/(double)frameborder),1.5);
+ }
+ if (randx>=0 && randy>=0){
+ //oldval=oldval*(randx*randy)/500.0;
+ }
+ */
+ if ( ((int) oldval + brightdelta ) >255)
+ *pic=255;
+ else if ( ( (int) oldval+brightdelta ) <0){
+ *pic=0;
+ }else
+ *pic=oldval+brightdelta;
+ *(pic+1)=*(pic+diffpic*w*2+1);
+
+ }else{
+ *pic=0;
+ //*(pic-1)=127;
+ }
+
+ }
+ y+=ydiff;
+ }
+ }
+
+ return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+mlt_filter filter_oldfilm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "delta", "14" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "every", "20" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_up" , "20" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_down" , "30" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_every" , "70" );
+ }
+ return this;
+}
+
--- /dev/null
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: oldfilm
+title: Oldfilm
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+ - Video # this may produce video
+description: Moves the Picture up and down and random brightness change
+icon:
+ filename: oldfilm/oldfilm.svg # relative to $MLT_DATA/modules/
+ content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - need to do some speed improvement.
+
+parameters:
+ - identifier: delta
+ title: Y-Delta
+ type: integer
+ description: Maximum delta value of Up/Down move
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 400
+ default: 14
+ mutable: no
+ widget: spinner
+ unit: pixel
+
+ - identifier: every
+ title: % of picture have a delta
+ type: integer
+ description: n'th % have a Y-Delta in picture
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 20
+ mutable: no
+ widget: spinner
+ unit: %
+
+ - identifier: brightnessdelta_up
+ title: Brightness up
+ type: integer
+ description: Makes image n values lighter
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 20
+ mutable: no
+ widget: spinner
+
+ - identifier: brightnessdelta_down
+ title: Brightness down
+ type: integer
+ description: Makes image n values darker
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 20
+ mutable: no
+ widget: spinner
+
+ - identifier: brightnessdelta_every
+ title: Brightness every
+ type: integer
+ description: Change value only for n/100
+ readonly: no
+ required: yes
+ minimum: 0
+ maximum: 100
+ default: 70
+ mutable: no
+ widget: spinner
+ unit: %
--- /dev/null
+/*
+ * filter_tcolor.c -- tcolor filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a<b?b:a)
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+
+ double over_cr = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cr" )/100.0;
+ double over_cb = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cb" )/100.0;
+
+ int video_width = *width;
+ int video_height = *height;
+
+ int x,y;
+
+ for (y=0;y<video_height;y++){
+ for (x=0;x<video_width;x+=2){
+ uint8_t *pix=(*image+y*video_width*2+x*2+1);
+ uint8_t *pix1=(*image+y*video_width*2+x*2+3);
+ *pix=MIN(MAX( ((double)*pix-127.0)*over_cb+127.0,0),255);
+ *pix1=MIN(MAX( ((double)*pix1-127.0)*over_cr+127.0,0),255);
+ }
+ }
+ // short a, short b, short c, short d
+ // a= gray val pix 1
+ // b: +=blue, -=yellow
+ // c: =gray pix 2
+ // d: +=red,-=green
+ }
+
+ return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+
+mlt_filter filter_tcolor_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cr", "190" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cb", "190" );
+ }
+ return this;
+}
+
+
--- /dev/null
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: tcolor
+title: Technicolor
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+ - Video # this may produce video
+description: Oversaturate the Color in Video, like in old Technicolor movies
+icon:
+filename: oldfilm/tcolor.svg # relative to $MLT_DATA/modules/
+ content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - need to do some speed improvement.
+
+parameters:
+ - identifier: oversaturate_cr # 'argument' is a reserved name for a value supplied to the factory
+ title: Blue/Yellow- axis
+ type: integer
+ description: Adjust factor for Blue/Yellow axis
+ readonly: no
+ required: yes
+ minimum: -400
+ maximum: 400
+ default: 190
+ mutable: no
+ widget: spinner
+
+ - identifier: oversaturate_cb
+ title: Red/Green-axis
+ type: integer
+ description: Adjust factor for Red/Green axis
+ readonly: no
+ required: yes
+ minimum: -400
+ maximum: 400
+ default: 190
+ mutable: no
+ widget: spinner
--- /dev/null
+/*
+ * filter_vignette.c -- vignette filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_geometry.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a<b?b:a)
+
+#define SIGMOD_STEPS 1000
+#define POSITION_VALUE(p,s,e) (s+((double)(e-s)*p ))
+//static double pow2[SIGMOD_STEPS];
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+ mlt_position in = mlt_filter_get_in( filter );
+ //mlt_position out = mlt_filter_get_out( filter );
+ mlt_position time = mlt_frame_get_position( this );
+
+ mlt_geometry geom=mlt_geometry_init();
+ struct mlt_geometry_item_s item;
+ float smooth, radius, cx, cy, opac;
+ char *val=mlt_properties_get(MLT_FILTER_PROPERTIES( filter ), "geometry" );
+ mlt_geometry_parse(geom,val,-1,-1,-1);
+ mlt_geometry_fetch(geom,&item,time-in);
+ smooth=item.x;
+ radius=item.y;
+ cx=item.w;
+ cy=item.h;
+ opac=item.mix;
+ mlt_geometry_close(geom);
+
+ int video_width = *width;
+ int video_height = *height;
+
+ int x,y;
+ int w2=cx,h2=cy;
+ double delta=1.0;
+ double max_opac=opac/100.0;
+
+ for (y=0;y<video_height;y++){
+ int h2_pow2=pow(y-h2,2.0);
+ for (x=0;x<video_width;x++){
+ uint8_t *pix=(*image+y*video_width*2+x*2);
+ int dx=sqrt(h2_pow2+pow(x-w2,2.0));
+
+ if (radius-smooth>dx){ //center, make not darker
+ continue;
+ }
+ else if (radius+smooth<=dx){//max dark after smooth area
+ delta=0.0;
+ }else{
+ //double sigx=5.0-10.0*(double)(dx-radius+smooth)/(2.0*smooth);//smooth >10 inner area, <-10 in dark area
+ //delta=pow2[((int)((sigx+10.0)*SIGMOD_STEPS/20.0))];//sigmoidal
+ delta = ((double)(radius+smooth-dx)/(2.0*smooth));//linear
+ }
+ delta=MAX(max_opac,delta);
+ *pix=(double)(*pix)*delta;
+ *(pix+1)=((double)(*(pix+1)-127.0)*delta)+127.0;
+ }
+ }
+ // short a, short b, short c, short d
+ // a= gray val pix 1
+ // b: +=blue, -=yellow
+ // c: =gray pix 2
+ // d: +=red,-=green
+ }
+
+ return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+
+mlt_filter filter_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ //int i=0;
+ if ( this != NULL )
+ {
+ /*
+ for (i=-SIGMOD_STEPS/2;i<SIGMOD_STEPS/2;i++){
+ pow2[i+SIGMOD_STEPS/2]=1.0/(1.0+pow(2.0,-((double)i)/((double)SIGMOD_STEPS/20.0)));
+ }
+ */
+
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", "80:50%:50%:50%:0" );
+ //mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "end", "" );
+
+ }
+ return this;
+}
+
+
--- /dev/null
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: vignette
+title: Vignette Effect
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+ - Video # this may produce video
+description: |
+ Vigentte around a point with adjustable smooth, radius, position
+ and transparency
+icon:
+filename: oldfilm/vignette.svg # relative to $MLT_DATA/modules/
+ content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+ - need to do some speed improvement.
+
+parameters:
+ - identifier: geometry # 'argument' is a reserved name for a value supplied to the factory
+ title: Geometry Value # the title can be used as a label for the widget
+ type: geometry
+ description: Start position, "smooth":"radius","X"x"Y","maxopac"
+ readonly: no
+ required: yes
+ mutable: no
+ format: %d:%d:%dx%d:%d
+ minimum: 0:0:0:0:0
+ maximum: 100:100:100:100:100
+ default: 80:50%:50%:50%:0
--- /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://web.resource.org/cc/"
+ 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="68.586548"
+ height="57.142868"
+ id="svg2267"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="grai.svg"
+ version="1.0"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.82687501"
+ inkscape:cx="547.34922"
+ inkscape:cy="-51.149976"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1195"
+ inkscape:window-height="742"
+ inkscape:window-x="315"
+ inkscape:window-y="144" />
+ <defs
+ id="defs2269" />
+ <metadata
+ id="metadata2272">
+ <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="Ebene 1"
+ id="layer1"
+ inkscape:groupmode="layer"
+ transform="translate(-49.372635,-144.21402)">
+ <rect
+ style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3308"
+ width="22.857143"
+ height="28.571428"
+ x="49.372635"
+ y="144.21402" />
+ <rect
+ y="144.21404"
+ x="72.199547"
+ height="28.571428"
+ width="22.857143"
+ id="rect3310"
+ style="opacity:0.68085106;fill:#000000;fill-opacity:0.63380283;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="opacity:0.68085106;fill:#000000;fill-opacity:0.10563378;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3312"
+ width="22.857143"
+ height="28.571428"
+ x="95.026451"
+ y="144.21404" />
+ <rect
+ y="172.78546"
+ x="95.102043"
+ height="28.571428"
+ width="22.857143"
+ id="rect3314"
+ style="opacity:0.94680852;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3316"
+ width="22.857143"
+ height="28.571428"
+ x="72.275139"
+ y="172.78546" />
+ <rect
+ y="172.78546"
+ x="49.448231"
+ height="28.571428"
+ width="22.857143"
+ id="rect3318"
+ style="opacity:0.41489366;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g2293"
+ inkscape:groupmode="layer"
+ inkscape:label="fill text"
+ transform="translate(-49.372635,-144.21402)" />
+</svg>
--- /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://web.resource.org/cc/"
+ 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="138.14285"
+ height="275.28571"
+ id="svg2251"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="lin.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2253" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="62.857143"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="586"
+ inkscape:window-x="0"
+ inkscape:window-y="0" />
+ <metadata
+ id="metadata2256">
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-128.07143,-217.57647)">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 180,223.79075 L 180,492.36218"
+ id="path2261" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 265.71429,218.07647 L 265.71429,389.50504"
+ id="path2263" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 128.57143,309.50504 L 128.57143,440.93361"
+ id="path2265" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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="320"
+ height="200"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="old.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="520"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1181"
+ inkscape:window-height="822"
+ inkscape:window-x="402"
+ inkscape:window-y="124" />
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-45.714287,-120.93361)">
+ <rect
+ style="opacity:0.41489366;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2160"
+ width="320"
+ height="200"
+ x="45.714287"
+ y="120.93361" />
+ <rect
+ style="opacity:0.41489366;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2162"
+ width="268.57144"
+ height="171.42857"
+ x="74.285713"
+ y="149.50504" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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="112.63159"
+ height="102.85714"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="tcol.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path4111"
+ 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>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.5843946"
+ inkscape:cx="88.57143"
+ inkscape:cy="77.236043"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1121"
+ inkscape:window-height="586"
+ inkscape:window-x="551"
+ inkscape:window-y="239" />
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-42.857143,-175.21933)">
+ <rect
+ style="opacity:1;fill:#910000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2160"
+ width="52.370373"
+ height="102.85714"
+ x="42.857143"
+ y="175.21933" />
+ <rect
+ y="175.21933"
+ x="94.469757"
+ height="102.85714"
+ width="61.018974"
+ id="rect2162"
+ style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;marker-start:none;marker-end:url(#Arrow1Lend);stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ d="M 66.39894,224.60733 C 123.87029,224.60733 123.87029,224.60733 123.87029,224.60733"
+ id="path3134" />
+ </g>
+</svg>
--- /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://web.resource.org/cc/"
+ 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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="251.42857"
+ height="188.57143"
+ id="svg2182"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+ sodipodi:docname="vig.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2184">
+ <linearGradient
+ id="linearGradient3165">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3167" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.98666668;"
+ offset="1"
+ id="stop3169" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3165"
+ id="radialGradient3171"
+ cx="228.57143"
+ cy="300.93362"
+ fx="228.57143"
+ fy="300.93362"
+ r="77.14286"
+ gradientTransform="matrix(1,0,0,0.9259259,0,22.291383)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="350"
+ inkscape:cy="-176.04888"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="586"
+ inkscape:window-x="0"
+ inkscape:window-y="0" />
+ <metadata
+ id="metadata2187">
+ <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="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-105.71429,-195.21933)">
+ <rect
+ style="opacity:0.95035466;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2190"
+ width="251.42857"
+ height="188.57143"
+ x="105.71429"
+ y="195.21933" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:url(#radialGradient3171);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path2192"
+ sodipodi:cx="228.57143"
+ sodipodi:cy="300.93362"
+ sodipodi:rx="77.14286"
+ sodipodi:ry="71.428574"
+ d="M 305.71429 300.93362 A 77.14286 71.428574 0 1 1 151.42857,300.93362 A 77.14286 71.428574 0 1 1 305.71429 300.93362 z"
+ transform="translate(0,-11.428571)" />
+ </g>
+</svg>
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltplus$(LIBSUF)
+
+OBJS = factory.o \
+ filter_affine.o \
+ filter_charcoal.o \
+ filter_invert.o \
+ filter_sepia.o \
+ transition_affine.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "affine", filter_affine_init );
+ MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init );
+ MLT_REGISTER( filter_type, "invert", filter_invert_init );
+ MLT_REGISTER( filter_type, "sepia", filter_sepia_init );
+ MLT_REGISTER( transition_type, "affine", transition_affine_init );
+}
--- /dev/null
+/*
+ * filter_affine.c -- affine filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the filter
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Get the properties
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the image
+ int error = 0; //mlt_frame_get_image( this, image, format, width, height, 0 );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+ mlt_transition transition = mlt_properties_get_data( properties, "transition", NULL );
+ mlt_frame a_frame = NULL;
+
+ if ( producer == NULL )
+ {
+ char *background = mlt_properties_get( properties, "background" );
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+ producer = mlt_factory_producer( profile, "fezzik", background );
+ mlt_properties_set_data( properties, "producer", producer, 0, (mlt_destructor)mlt_producer_close, NULL );
+ }
+
+ if ( transition == NULL )
+ {
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+ transition = mlt_factory_transition( profile, "affine", NULL );
+ mlt_properties_set_data( properties, "transition", transition, 0, (mlt_destructor)mlt_transition_close, NULL );
+ }
+
+ if ( producer != NULL && transition != NULL )
+ {
+ char *name = mlt_properties_get( properties, "_unique_id" );
+ mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( this ), name );
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this );
+ double consumer_ar = mlt_properties_get_double( frame_properties, "consumer_aspect_ratio" );
+ mlt_properties_set_position( MLT_TRANSITION_PROPERTIES( transition ), "in", mlt_filter_get_in( filter ) );
+ mlt_properties_set_position( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_filter_get_out( filter ) );
+ mlt_producer_seek( producer, position );
+ mlt_frame_set_position( this, position );
+ mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." );
+ mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "transition." );
+ mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &a_frame, 0 );
+ mlt_properties_set( MLT_FRAME_PROPERTIES( a_frame ), "rescale.interp", "nearest" );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame ), "distort", 1 );
+
+ // Special case - aspect_ratio = 0
+ if ( mlt_properties_get_double( frame_properties, "aspect_ratio" ) == 0 )
+ mlt_properties_set_double( frame_properties, "aspect_ratio", consumer_ar );
+ if ( mlt_properties_get_double( MLT_FRAME_PROPERTIES( a_frame ), "aspect_ratio" ) == 0 )
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( a_frame ), "aspect_ratio", consumer_ar );
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( a_frame ), "consumer_aspect_ratio", consumer_ar );
+
+ mlt_transition_process( transition, a_frame, this );
+ mlt_frame_get_image( a_frame, image, format, width, height, writable );
+ mlt_properties_set_data( frame_properties, "affine_frame", a_frame, 0, (mlt_destructor)mlt_frame_close, NULL );
+ mlt_properties_set_data( frame_properties, "image", *image, *width * *height * 2, NULL, NULL );
+ mlt_properties_set_data( frame_properties, "alpha", mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL, NULL );
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get a unique name to store the frame position
+ char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" );
+
+ // Assign the current position to the name
+ mlt_properties_set_position( properties, name, mlt_frame_get_position( frame ) - mlt_filter_get_in( this ) );
+
+ // Push the frame filter
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "background", "colour:black" );
+ }
+ return this;
+}
+
+
--- /dev/null
+/*
+ * filter_charcoal.c -- charcoal filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+static inline int get_Y( uint8_t *pixels, int width, int height, int x, int y )
+{
+ if ( x < 0 || x >= width || y < 0 || y >= height )
+ {
+ return 235;
+ }
+ else
+ {
+ uint8_t *pixel = pixels + y * ( width << 1 ) + ( x << 1 );
+ return *pixel;
+ }
+}
+
+static inline int sqrti( int n )
+{
+ int p = 0;
+ int q = 1;
+ int r = n;
+ int h = 0;
+
+ while( q <= n )
+ q = q << 2;
+
+ while( q != 1 )
+ {
+ q = q >> 2;
+ h = p + q;
+ p = p >> 1;
+ if ( r >= h )
+ {
+ p = p + q;
+ r = r - h;
+ }
+ }
+
+ return p;
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the filter
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Get the image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ // Get the charcoal scatter value
+ int x_scatter = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "x_scatter" );
+ int y_scatter = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "y_scatter" );
+ float scale = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "scale" );
+ float mix = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "mix" );
+ int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" );
+
+ // We'll process pixel by pixel
+ int x = 0;
+ int y = 0;
+
+ // We need to create a new frame as this effect modifies the input
+ uint8_t *temp = mlt_pool_alloc( *width * *height * 2 );
+ uint8_t *p = temp;
+ uint8_t *q = *image;
+
+ // Calculations are carried out on a 3x3 matrix
+ int matrix[ 3 ][ 3 ];
+
+ // Used to carry out the matrix calculations
+ int sum1;
+ int sum2;
+ float sum;
+ int val;
+
+ // Loop for each row
+ for ( y = 0; y < *height; y ++ )
+ {
+ // Loop for each pixel
+ for ( x = 0; x < *width; x ++ )
+ {
+ // Populate the matrix
+ matrix[ 0 ][ 0 ] = get_Y( *image, *width, *height, x - x_scatter, y - y_scatter );
+ matrix[ 0 ][ 1 ] = get_Y( *image, *width, *height, x , y - y_scatter );
+ matrix[ 0 ][ 2 ] = get_Y( *image, *width, *height, x + x_scatter, y - y_scatter );
+ matrix[ 1 ][ 0 ] = get_Y( *image, *width, *height, x - x_scatter, y );
+ matrix[ 1 ][ 2 ] = get_Y( *image, *width, *height, x + x_scatter, y );
+ matrix[ 2 ][ 0 ] = get_Y( *image, *width, *height, x - x_scatter, y + y_scatter );
+ matrix[ 2 ][ 1 ] = get_Y( *image, *width, *height, x , y + y_scatter );
+ matrix[ 2 ][ 2 ] = get_Y( *image, *width, *height, x + x_scatter, y + y_scatter );
+
+ // Do calculations
+ sum1 = (matrix[2][0] - matrix[0][0]) + ( (matrix[2][1] - matrix[0][1]) << 1 ) + (matrix[2][2] - matrix[2][0]);
+ sum2 = (matrix[0][2] - matrix[0][0]) + ( (matrix[1][2] - matrix[1][0]) << 1 ) + (matrix[2][2] - matrix[2][0]);
+ sum = scale * sqrti( sum1 * sum1 + sum2 * sum2 );
+
+ // Assign value
+ *p ++ = !invert ? ( sum >= 16 && sum <= 235 ? 251 - sum : sum < 16 ? 235 : 16 ) :
+ ( sum >= 16 && sum <= 235 ? sum : sum < 16 ? 16 : 235 );
+ q ++;
+ val = 128 + mix * ( *q ++ - 128 );
+ val = val < 16 ? 16 : val > 240 ? 240 : val;
+ *p ++ = val;
+ }
+ }
+
+ // Return the created image
+ *image = temp;
+
+ // Store new and destroy old
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", *image, *width * *height * 2, mlt_pool_release, NULL );
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the frame filter
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "x_scatter", "1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "y_scatter", "1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "scale", "1.5" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "mix", "0" );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_invert.c -- invert filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+static inline int clamp( int v, int l, int u )
+{
+ return v < l ? l : ( v > u ? u : v );
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the image
+ mlt_filter filter = mlt_frame_pop_service( this );
+ int mask = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "alpha" );
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *format == mlt_image_yuv422 )
+ {
+ uint8_t *p = *image;
+ uint8_t *q = *image + *width * *height * 2;
+ uint8_t *r = *image;
+
+ while ( p != q )
+ {
+ *p ++ = clamp( 251 - *r ++, 16, 235 );
+ *p ++ = clamp( 256 - *r ++, 16, 240 );
+ }
+
+ if ( mask )
+ {
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+ int size = *width * *height;
+ memset( alpha, mask, size );
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the frame filter
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ this->process = filter_process;
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_sepia.c -- sepia filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the filter
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Get the image
+ int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+ // Only process if we have no error and a valid colour space
+ if ( error == 0 && *image && *format == mlt_image_yuv422 )
+ {
+ // We modify the whole image
+ uint8_t *p = *image;
+ int h = *height;
+ int uneven = *width % 2;
+ int w = ( *width - uneven ) / 2;
+ int t;
+
+ // Get u and v values
+ int u = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "u" );
+ int v = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "v" );
+
+ // Loop through image
+ while( h -- )
+ {
+ t = w;
+ while( t -- )
+ {
+ p ++;
+ *p ++ = u;
+ p ++;
+ *p ++ = v;
+ }
+ if ( uneven )
+ {
+ p ++;
+ *p ++ = u;
+ }
+ }
+ }
+
+ return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the frame filter
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = filter_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "u", "75" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "v", "150" );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * transition_affine.c -- affine transformations
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_transition.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+/** Calculate real geometry.
+*/
+
+static void geometry_calculate( mlt_transition this, const char *store, struct mlt_geometry_item_s *output, float position )
+{
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
+ int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
+ int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
+ int length = mlt_geometry_get_length( geometry );
+
+ // Allow wrapping
+ if ( !repeat_off && position >= length && length != 0 )
+ {
+ int section = position / length;
+ position -= section * length;
+ if ( !mirror_off && section % 2 == 1 )
+ position = length - position;
+ }
+
+ // Fetch the key for the position
+ mlt_geometry_fetch( geometry, output, position );
+}
+
+
+static mlt_geometry transition_parse_keys( mlt_transition this, const char *name, const char *store, int normalised_width, int normalised_height )
+{
+ // Get the properties of the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // Try to fetch it first
+ mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
+
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+
+ // Determine length and obtain cycle
+ int length = out - in + 1;
+ double cycle = mlt_properties_get_double( properties, "cycle" );
+
+ // Allow a geometry repeat cycle
+ if ( cycle >= 1 )
+ length = cycle;
+ else if ( cycle > 0 )
+ length *= cycle;
+
+ if ( geometry == NULL )
+ {
+ // Get the new style geometry string
+ char *property = mlt_properties_get( properties, name );
+
+ // Create an empty geometries object
+ geometry = mlt_geometry_init( );
+
+ // Parse the geometry if we have one
+ mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
+
+ // Store it
+ mlt_properties_set_data( properties, store, geometry, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+ }
+ else
+ {
+ // Check for updates and refresh if necessary
+ mlt_geometry_refresh( geometry, mlt_properties_get( properties, name ), length, normalised_width, normalised_height );
+ }
+
+ return geometry;
+}
+
+static mlt_geometry composite_calculate( mlt_transition this, struct mlt_geometry_item_s *result, int nw, int nh, float position )
+{
+ // Structures for geometry
+ mlt_geometry start = transition_parse_keys( this, "geometry", "geometries", nw, nh );
+
+ // Do the calculation
+ geometry_calculate( this, "geometries", result, position );
+
+ return start;
+}
+
+static inline float composite_calculate_key( mlt_transition this, const char *name, const char *store, int norm, float position )
+{
+ // Struct for the result
+ struct mlt_geometry_item_s result;
+
+ // Structures for geometry
+ transition_parse_keys( this, name, store, norm, 0 );
+
+ // Do the calculation
+ geometry_calculate( this, store, &result, position );
+
+ return result.x;
+}
+
+typedef struct
+{
+ float matrix[3][3];
+}
+affine_t;
+
+static void affine_init( float this[3][3] )
+{
+ this[0][0] = 1;
+ this[0][1] = 0;
+ this[0][2] = 0;
+ this[1][0] = 0;
+ this[1][1] = 1;
+ this[1][2] = 0;
+ this[2][0] = 0;
+ this[2][1] = 0;
+ this[2][2] = 1;
+}
+
+// Multiply two this affine transform with that
+static void affine_multiply( float this[3][3], float that[3][3] )
+{
+ float output[3][3];
+ int i;
+ int j;
+
+ for ( i = 0; i < 3; i ++ )
+ for ( j = 0; j < 3; j ++ )
+ output[i][j] = this[i][0] * that[j][0] + this[i][1] * that[j][1] + this[i][2] * that[j][2];
+
+ this[0][0] = output[0][0];
+ this[0][1] = output[0][1];
+ this[0][2] = output[0][2];
+ this[1][0] = output[1][0];
+ this[1][1] = output[1][1];
+ this[1][2] = output[1][2];
+ this[2][0] = output[2][0];
+ this[2][1] = output[2][1];
+ this[2][2] = output[2][2];
+}
+
+// Rotate by a given angle
+static void affine_rotate_x( float this[3][3], float angle )
+{
+ float affine[3][3];
+ affine[0][0] = cos( angle * M_PI / 180 );
+ affine[0][1] = 0 - sin( angle * M_PI / 180 );
+ affine[0][2] = 0;
+ affine[1][0] = sin( angle * M_PI / 180 );
+ affine[1][1] = cos( angle * M_PI / 180 );
+ affine[1][2] = 0;
+ affine[2][0] = 0;
+ affine[2][1] = 0;
+ affine[2][2] = 1;
+ affine_multiply( this, affine );
+}
+
+static void affine_rotate_y( float this[3][3], float angle )
+{
+ float affine[3][3];
+ affine[0][0] = cos( angle * M_PI / 180 );
+ affine[0][1] = 0;
+ affine[0][2] = 0 - sin( angle * M_PI / 180 );
+ affine[1][0] = 0;
+ affine[1][1] = 1;
+ affine[1][2] = 0;
+ affine[2][0] = sin( angle * M_PI / 180 );
+ affine[2][1] = 0;
+ affine[2][2] = cos( angle * M_PI / 180 );
+ affine_multiply( this, affine );
+}
+
+static void affine_rotate_z( float this[3][3], float angle )
+{
+ float affine[3][3];
+ affine[0][0] = 1;
+ affine[0][1] = 0;
+ affine[0][2] = 0;
+ affine[1][0] = 0;
+ affine[1][1] = cos( angle * M_PI / 180 );
+ affine[1][2] = sin( angle * M_PI / 180 );
+ affine[2][0] = 0;
+ affine[2][1] = - sin( angle * M_PI / 180 );
+ affine[2][2] = cos( angle * M_PI / 180 );
+ affine_multiply( this, affine );
+}
+
+static void affine_scale( float this[3][3], float sx, float sy )
+{
+ float affine[3][3];
+ affine[0][0] = sx;
+ affine[0][1] = 0;
+ affine[0][2] = 0;
+ affine[1][0] = 0;
+ affine[1][1] = sy;
+ affine[1][2] = 0;
+ affine[2][0] = 0;
+ affine[2][1] = 0;
+ affine[2][2] = 1;
+ affine_multiply( this, affine );
+}
+
+// Shear by a given value
+static void affine_shear( float this[3][3], float shear_x, float shear_y, float shear_z )
+{
+ float affine[3][3];
+ affine[0][0] = 1;
+ affine[0][1] = tan( shear_x * M_PI / 180 );
+ affine[0][2] = 0;
+ affine[1][0] = tan( shear_y * M_PI / 180 );
+ affine[1][1] = 1;
+ affine[1][2] = tan( shear_z * M_PI / 180 );
+ affine[2][0] = 0;
+ affine[2][1] = 0;
+ affine[2][2] = 1;
+ affine_multiply( this, affine );
+}
+
+static void affine_offset( float this[3][3], int x, int y )
+{
+ this[0][2] += x;
+ this[1][2] += y;
+}
+
+// Obtain the mapped x coordinate of the input
+static inline double MapX( float this[3][3], int x, int y )
+{
+ return this[0][0] * x + this[0][1] * y + this[0][2];
+}
+
+// Obtain the mapped y coordinate of the input
+static inline double MapY( float this[3][3], int x, int y )
+{
+ return this[1][0] * x + this[1][1] * y + this[1][2];
+}
+
+static inline double MapZ( float this[3][3], int x, int y )
+{
+ return this[2][0] * x + this[2][1] * y + this[2][2];
+}
+
+#define MAX( x, y ) x > y ? x : y
+#define MIN( x, y ) x < y ? x : y
+
+static void affine_max_output( float this[3][3], float *w, float *h, float dz )
+{
+ int tlx = MapX( this, -720, 576 ) / dz;
+ int tly = MapY( this, -720, 576 ) / dz;
+ int trx = MapX( this, 720, 576 ) / dz;
+ int try = MapY( this, 720, 576 ) / dz;
+ int blx = MapX( this, -720, -576 ) / dz;
+ int bly = MapY( this, -720, -576 ) / dz;
+ int brx = MapX( this, 720, -576 ) / dz;
+ int bry = MapY( this, 720, -576 ) / dz;
+
+ int max_x;
+ int max_y;
+ int min_x;
+ int min_y;
+
+ max_x = MAX( tlx, trx );
+ max_x = MAX( max_x, blx );
+ max_x = MAX( max_x, brx );
+
+ min_x = MIN( tlx, trx );
+ min_x = MIN( min_x, blx );
+ min_x = MIN( min_x, brx );
+
+ max_y = MAX( tly, try );
+ max_y = MAX( max_y, bly );
+ max_y = MAX( max_y, bry );
+
+ min_y = MIN( tly, try );
+ min_y = MIN( min_y, bly );
+ min_y = MIN( min_y, bry );
+
+ *w = ( float )( max_x - min_x + 1 ) / 1440.0;
+ *h = ( float )( max_y - min_y + 1 ) / 1152.0;
+}
+
+#define IN_RANGE( v, r ) ( v >= - r / 2 && v < r / 2 )
+
+static inline void get_affine( affine_t *affine, mlt_transition this, float position )
+{
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ int keyed = mlt_properties_get_int( properties, "keyed" );
+ affine_init( affine->matrix );
+
+ if ( keyed == 0 )
+ {
+ float fix_rotate_x = mlt_properties_get_double( properties, "fix_rotate_x" );
+ float fix_rotate_y = mlt_properties_get_double( properties, "fix_rotate_y" );
+ float fix_rotate_z = mlt_properties_get_double( properties, "fix_rotate_z" );
+ float rotate_x = mlt_properties_get_double( properties, "rotate_x" );
+ float rotate_y = mlt_properties_get_double( properties, "rotate_y" );
+ float rotate_z = mlt_properties_get_double( properties, "rotate_z" );
+ float fix_shear_x = mlt_properties_get_double( properties, "fix_shear_x" );
+ float fix_shear_y = mlt_properties_get_double( properties, "fix_shear_y" );
+ float fix_shear_z = mlt_properties_get_double( properties, "fix_shear_z" );
+ float shear_x = mlt_properties_get_double( properties, "shear_x" );
+ float shear_y = mlt_properties_get_double( properties, "shear_y" );
+ float shear_z = mlt_properties_get_double( properties, "shear_z" );
+ float ox = mlt_properties_get_double( properties, "ox" );
+ float oy = mlt_properties_get_double( properties, "oy" );
+
+ affine_rotate_x( affine->matrix, fix_rotate_x + rotate_x * position );
+ affine_rotate_y( affine->matrix, fix_rotate_y + rotate_y * position );
+ affine_rotate_z( affine->matrix, fix_rotate_z + rotate_z * position );
+ affine_shear( affine->matrix,
+ fix_shear_x + shear_x * position,
+ fix_shear_y + shear_y * position,
+ fix_shear_z + shear_z * position );
+ affine_offset( affine->matrix, ox, oy );
+ }
+ else
+ {
+ float rotate_x = composite_calculate_key( this, "rotate_x", "rotate_x_info", 360, position );
+ float rotate_y = composite_calculate_key( this, "rotate_y", "rotate_y_info", 360, position );
+ float rotate_z = composite_calculate_key( this, "rotate_z", "rotate_z_info", 360, position );
+ float shear_x = composite_calculate_key( this, "shear_x", "shear_x_info", 360, position );
+ float shear_y = composite_calculate_key( this, "shear_y", "shear_y_info", 360, position );
+ float shear_z = composite_calculate_key( this, "shear_z", "shear_z_info", 360, position );
+
+ affine_rotate_x( affine->matrix, rotate_x );
+ affine_rotate_y( affine->matrix, rotate_y );
+ affine_rotate_z( affine->matrix, rotate_z );
+ affine_shear( affine->matrix, shear_x, shear_y, shear_z );
+ }
+}
+
+/** Get the image.
+*/
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Get the b frame from the stack
+ mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+
+ // Get the transition object
+ mlt_transition this = mlt_frame_pop_service( a_frame );
+
+ // Get the properties of the transition
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+ // Get the properties of the a frame
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+ // Get the properties of the b frame
+ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+ // Image, format, width, height and image for the b frame
+ uint8_t *b_image = NULL;
+ mlt_image_format b_format = mlt_image_yuv422;
+ int b_width;
+ int b_height;
+
+ // Get the unique name to retrieve the frame position
+ char *name = mlt_properties_get( properties, "_unique_id" );
+
+ // Assign the current position to the name
+ mlt_position position = mlt_properties_get_position( a_props, name );
+ mlt_position in = mlt_properties_get_position( properties, "in" );
+ mlt_position out = mlt_properties_get_position( properties, "out" );
+ int mirror = mlt_properties_get_position( properties, "mirror" );
+ int length = out - in + 1;
+
+ // Obtain the normalised width and height from the a_frame
+ int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
+ int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
+
+ double consumer_ar = mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) ;
+
+ // Structures for geometry
+ struct mlt_geometry_item_s result;
+
+ if ( mirror && position > length / 2 )
+ position = abs( position - length );
+
+ // Fetch the a frame image
+ mlt_frame_get_image( a_frame, image, format, width, height, 1 );
+
+ // Calculate the region now
+ composite_calculate( this, &result, normalised_width, normalised_height, ( float )position );
+
+ // Fetch the b frame image
+ result.w = ( int )( result.w * *width / normalised_width );
+ result.h = ( int )( result.h * *height / normalised_height );
+ result.x = ( int )( result.x * *width / normalised_width );
+ result.y = ( int )( result.y * *height / normalised_height );
+ //result.w -= ( int )abs( result.w ) % 2;
+ //result.x -= ( int )abs( result.x ) % 2;
+ b_width = result.w;
+ b_height = result.h;
+
+ if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+ mlt_properties_set_double( b_props, "aspect_ratio", consumer_ar );
+
+ if ( !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+ {
+ mlt_properties_set( b_props, "rescale.interp", "nearest" );
+ mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
+ }
+ else
+ {
+ mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
+ mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
+ }
+
+ mlt_properties_set_int( b_props, "distort", mlt_properties_get_int( properties, "distort" ) );
+ mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
+ result.w = b_width;
+ result.h = b_height;
+
+ // Check that both images are of the correct format and process
+ if ( *format == mlt_image_yuv422 && b_format == mlt_image_yuv422 )
+ {
+ register int x, y;
+ register int dx, dy;
+ double dz;
+ float sw, sh;
+
+ // Get values from the transition
+ float scale_x = mlt_properties_get_double( properties, "scale_x" );
+ float scale_y = mlt_properties_get_double( properties, "scale_y" );
+ int scale = mlt_properties_get_int( properties, "scale" );
+
+ uint8_t *p = *image;
+ uint8_t *q = *image;
+
+ int cx = result.x + ( b_width >> 1 );
+ int cy = result.y + ( b_height >> 1 );
+ cx -= cx % 2;
+
+ int lower_x = 0 - cx;
+ int upper_x = *width - cx;
+ int lower_y = 0 - cy;
+ int upper_y = *height - cy;
+
+ int b_stride = b_width << 1;
+ int a_stride = *width << 1;
+ int x_offset = ( int )result.w >> 1;
+ int y_offset = ( int )result.h >> 1;
+
+ uint8_t *alpha = mlt_frame_get_alpha_mask( b_frame );
+ uint8_t *mask = mlt_frame_get_alpha_mask( a_frame );
+ uint8_t *pmask = mask;
+ float mix;
+
+ affine_t affine;
+
+ get_affine( &affine, this, ( float )position );
+
+ q = *image;
+
+ dz = MapZ( affine.matrix, 0, 0 );
+
+ if ( mask == NULL )
+ {
+ mask = mlt_pool_alloc( *width * *height );
+ pmask = mask;
+ memset( mask, 255, *width * *height );
+ }
+
+ if ( ( int )abs( dz * 1000 ) < 25 )
+ goto getout;
+
+ if ( scale )
+ {
+ affine_max_output( affine.matrix, &sw, &sh, dz );
+ affine_scale( affine.matrix, sw, sh );
+ }
+ else if ( scale_x != 0 && scale_y != 0 )
+ {
+ affine_scale( affine.matrix, scale_x, scale_y );
+ }
+
+ if ( alpha == NULL )
+ {
+ for ( y = lower_y; y < upper_y; y ++ )
+ {
+ p = q;
+
+ for ( x = lower_x; x < upper_x; x ++ )
+ {
+ dx = MapX( affine.matrix, x, y ) / dz + x_offset;
+ dy = MapY( affine.matrix, x, y ) / dz + y_offset;
+
+ if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
+ {
+ pmask ++;
+ dx -= dx & 1;
+ *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) );
+ *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
+ }
+ else
+ {
+ p += 2;
+ pmask ++;
+ }
+ }
+
+ q += a_stride;
+ }
+ }
+ else
+ {
+ for ( y = lower_y; y < upper_y; y ++ )
+ {
+ p = q;
+
+ for ( x = lower_x; x < upper_x; x ++ )
+ {
+ dx = MapX( affine.matrix, x, y ) / dz + x_offset;
+ dy = MapY( affine.matrix, x, y ) / dz + y_offset;
+
+ if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
+ {
+ *pmask ++ = *( alpha + dy * b_width + dx );
+ mix = ( float )*( alpha + dy * b_width + dx ) / 255.0;
+ dx -= dx & 1;
+ *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) );
+ p ++;
+ *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
+ p ++;
+ }
+ else
+ {
+ p += 2;
+ pmask ++;
+ }
+ }
+
+ q += a_stride;
+ }
+ }
+
+getout:
+ a_frame->get_alpha_mask = NULL;
+ mlt_properties_set_data( a_props, "alpha", mask, 0, mlt_pool_release, NULL );
+ }
+
+ return 0;
+}
+
+/** Affine transition processing.
+*/
+
+static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
+{
+ // Get a unique name to store the frame position
+ char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+
+ // Assign the current position to the name
+ mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+ mlt_properties_set_position( a_props, name, mlt_frame_get_position( a_frame ) );
+
+ // Push the transition on to the frame
+ mlt_frame_push_service( a_frame, transition );
+
+ // Push the b_frame on to the stack
+ mlt_frame_push_frame( a_frame, b_frame );
+
+ // Push the transition method
+ mlt_frame_push_get_image( a_frame, transition_get_image );
+
+ return a_frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_transition transition = mlt_transition_new( );
+ if ( transition != NULL )
+ {
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sx", 1 );
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sy", 1 );
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
+ mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "geometry", "0,0:100%x100%" );
+ // Inform apps and framework that this is a video only transition
+ mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
+ transition->process = transition_process;
+ }
+ return transition;
+}
--- /dev/null
+include ../../../config.mak
+include config.mak
+
+TARGET = ../libmltqimage$(LIBSUF)
+
+OBJS = factory.o producer_qimage.o
+CPPOBJS = qimage_wrapper.o
+
+CFLAGS += -I../..
+CXXFLAGS += $(CFLAGS) $(QTCXXFLAGS) -Wno-deprecated
+
+LDFLAGS = -L../../framework -lmlt
+LDFLAGS += $(QTLIBS)
+LDFLAGS += -lstdc++
+
+ifdef USE_KDE
+LDFLAGS += -lkio
+endif
+
+SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS) $(CPPOBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $(QTCXXFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend config.h config.mak
+
+clean:
+ rm -f $(OBJS) $(TARGET) $(CPPOBJS)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" = "1" ]
+then
+ cat << EOF
+QImage options:
+
+ --force-qt3 - Force compile against Qt3 if Qt4 is present on the system
+ --qimage-libdir - Location of QT lib directory [/usr/lib/qt4 or /usr/lib/qt3]
+ --qimage-includedir - Location of QT include directory [/usr/include/qt4 or /usr/include/qt3]
+ --kde-libdir - Location of KDE lib directory [/usr/lib]
+ --kde-includedir - Location of KDE include directory [/usr/include/kde]
+
+EOF
+
+else
+ targetos=$(uname -s)
+ case $targetos in
+ MINGW32*)
+ export LIBSUF=.dll
+ ;;
+ Darwin)
+ export LIBSUF=.dylib
+ ;;
+ Linux|FreeBSD)
+ export LIBSUF=.so
+ ;;
+ *)
+ ;;
+ esac
+
+ qimage_includedir=/usr/include/qt4
+ qimage_libdir=/usr/lib/qt4
+
+ if [ ! -d "$qimage_libdir" -o ! -d "$qimage_includedir" ]
+ then
+ qimage_includedir=/usr/include/qt3
+ qimage_libdir=/usr/lib/qt3
+ kde_includedir=/usr/include/kde
+ kde_libdir=/usr/lib
+ if [ "$KDEDIR" != "" ]
+ then
+ kde_includedir="$KDEDIR/include"
+ kde_libdir="$KDEDIR"
+ fi
+ fi
+
+ if [ "$QTDIR" != "" ]
+ then
+ qimage_includedir="$QTDIR/include"
+ qimage_libdir="$QTDIR/lib"
+ fi
+
+ export force_qt3=
+ export qt4_found=
+
+ for i in "$@"
+ do
+ case $i in
+ --qimage-libdir=* ) qimage_libdir="${i#--qimage-libdir=}" ;;
+ --qimage-includedir=* ) qimage_includedir="${i#--qimage-includedir=}" ;;
+ --kde-libdir=* ) kde_libdir="${i#--kde-libdir=}" ;;
+ --kde-includedir=* ) kde_includedir="${i#--kde-includedir=}" ;;
+ --force-qt3 ) force_qt3="true" ;;
+ esac
+ done
+
+ pkg-config --exists 'QtGui >= 4'
+ if [ $? -eq 0 ] && [ "$force_qt3" = "" ]
+ then
+ echo "Qt version 4.x detected, will compile Qt4 qimage producer"
+ qt4_found=true
+ echo "#define USE_QT4" > config.h
+ echo "USE_QT4=1" > config.mak
+ echo QTCXXFLAGS=$(pkg-config --cflags QtGui) >> config.mak
+ echo QTLIBS=$(pkg-config --libs QtGui) >> config.mak
+
+ elif [ -d "$qimage_libdir" -a -d "$qimage_includedir" ]
+ then
+
+ # test if we have a Qt3 or Qt4
+ if [ -f "$qimage_libdir/libQtCore.so" ] || [ -d "$qimage_libdir/QtGui.framework" ] && [ "$force_qt3" = "" ]
+ then
+ echo "Qt version 4.x detected, will compile Qt4 qimage producer"
+ qt4_found=true
+ else
+ echo "Qt version 3.x detected, will compile Qt3 qimage producer"
+ fi
+
+ echo "Include directory: " $qimage_includedir
+
+ echo > config.h
+ echo > config.mak
+ if [ "$qt4_found" != "" ] && [ "$force_qt3" = "" ]
+ then
+ echo "#define USE_QT4" >> config.h
+ echo "USE_QT4=1" >> config.mak
+ if [ -d "$qimage_libdir/QtGui.framework" ]
+ then
+ echo QTCXXFLAGS=$(pkg-config --cflags QtGui) >> config.mak
+ echo QTLIBS=$(pkg-config --libs QtGui) >> config.mak
+ else
+ echo QTCXXFLAGS=-I$qimage_includedir >> config.mak
+ echo QTLIBS=-L$qimage_libdir -lQtGui >> config.mak
+ fi
+ else
+ if [ -d "$kde_includedir" ]
+ then
+ echo "#define USE_KDE" >> config.h
+ echo "USE_KDE=1" >> config.mak
+ echo "#define USE_QT3" >> config.h
+ echo "USE_QT3=1" >> config.mak
+ echo QTCXXFLAGS=-I$qimage_includedir -I$kde_includedir -DQT_THREAD_SUPPORT >> config.mak
+ echo QTLIBS=-L$qimage_libdir -L$kde_libdir/lib -lqt-mt >> config.mak
+ else
+ echo "qimage: KDE environment not found - disabling extra image formats"
+ echo "#define USE_QT3" >> config.h
+ echo "USE_QT3=1" >> config.mak
+ echo QTCXXFLAGS=-I$qimage_includedir -DQT_THREAD_SUPPORT>> config.mak
+ echo QTLIBS=-L$qimage_libdir -lqt-mt >> config.mak
+ fi
+ fi
+ else
+ echo "qimage: QT environment not found - disabling"
+ touch ../disable-qimage
+ fi
+
+fi
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( producer_type, "qimage", producer_qimage_init );
+}
--- /dev/null
+/*
+ * producer_image.c -- a QT/QImage based producer for MLT
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * NB: This module is designed to be functionally equivalent to the
+ * gtk2 image loading module so it can be used as replacement.
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_cache.h>
+#include "qimage_wrapper.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static void load_filenames( producer_qimage this, mlt_properties producer_properties );
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
+{
+ producer_qimage this = calloc( sizeof( struct producer_qimage_s ), 1 );
+ if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+ {
+ mlt_producer producer = &this->parent;
+
+ // Get the properties interface
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+
+ // Callback registration
+#ifdef USE_KDE
+ init_qimage();
+#endif
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Set the default properties
+ mlt_properties_set( properties, "resource", filename );
+ mlt_properties_set_int( properties, "ttl", 25 );
+ mlt_properties_set_int( properties, "aspect_ratio", 1 );
+ mlt_properties_set_int( properties, "progressive", 1 );
+
+ // Validate the resource
+ if ( filename )
+ load_filenames( this, properties );
+ if ( this->count )
+ {
+ mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+ if ( frame )
+ {
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+ pthread_mutex_init( &this->mutex, NULL );
+ mlt_properties_set_data( frame_properties, "producer_qimage", this, 0, NULL, NULL );
+ mlt_frame_set_position( frame, mlt_producer_position( producer ) );
+ mlt_properties_set_position( frame_properties, "qimage_position", mlt_producer_position( producer ) );
+ refresh_qimage( this, frame, 0, 0 );
+ mlt_frame_close( frame );
+ }
+ }
+ if ( this->current_width == 0 )
+ {
+ producer_close( producer );
+ producer = NULL;
+ }
+ return producer;
+ }
+ free( this );
+ return NULL;
+}
+
+static void load_filenames( producer_qimage this, mlt_properties producer_properties )
+{
+ char *filename = mlt_properties_get( producer_properties, "resource" );
+ this->filenames = mlt_properties_new( );
+
+ // Read xml string
+ if ( strstr( filename, "<svg" ) )
+ {
+ // Generate a temporary file for the svg
+ char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
+ int fd = mkstemp( fullname );
+
+ if ( fd > -1 )
+ {
+ // Write the svg into the temp file
+ ssize_t remaining_bytes;
+ char *xml = filename;
+
+ // Strip leading crap
+ while ( xml[0] != '<' )
+ xml++;
+
+ remaining_bytes = strlen( xml );
+ while ( remaining_bytes > 0 )
+ remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
+ close( fd );
+
+ mlt_properties_set( this->filenames, "0", fullname );
+
+ // Teehe - when the producer closes, delete the temp file and the space allo
+ mlt_properties_set_data( producer_properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
+ }
+ }
+ // Obtain filenames
+ else if ( strchr( filename, '%' ) != NULL )
+ {
+ // handle picture sequences
+ int i = mlt_properties_get_int( producer_properties, "begin" );
+ int gap = 0;
+ char full[1024];
+ int keyvalue = 0;
+ char key[ 50 ];
+
+ while ( gap < 100 )
+ {
+ struct stat buf;
+ snprintf( full, 1023, filename, i ++ );
+ if ( stat( full, &buf ) == 0 )
+ {
+ sprintf( key, "%d", keyvalue ++ );
+ mlt_properties_set( this->filenames, key, full );
+ gap = 0;
+ }
+ else
+ {
+ gap ++;
+ }
+ }
+ }
+ else if ( strstr( filename, "/.all." ) != NULL )
+ {
+ char wildcard[ 1024 ];
+ char *dir_name = strdup( filename );
+ char *extension = strrchr( dir_name, '.' );
+
+ *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
+ sprintf( wildcard, "*%s", extension );
+
+ mlt_properties_dir_list( this->filenames, dir_name, wildcard, 1 );
+
+ free( dir_name );
+ }
+ else
+ {
+ mlt_properties_set( this->filenames, "0", filename );
+ }
+
+ this->count = mlt_properties_count( this->filenames );
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer for this frame
+ producer_qimage this = mlt_properties_get_data( properties, "producer_qimage", NULL );
+
+ *width = mlt_properties_get_int( properties, "rescale_width" );
+ *height = mlt_properties_get_int( properties, "rescale_height" );
+
+ // Refresh the image
+ refresh_qimage( this, frame, *width, *height );
+
+ // We need to know the size of the image to clone it
+ int image_size = this->current_width * ( this->current_height + 1 ) * 2;
+ int alpha_size = this->current_width * this->current_height;
+
+ // Get width and height (may have changed during the refresh)
+ *width = mlt_properties_get_int( properties, "width" );
+ *height = mlt_properties_get_int( properties, "height" );
+
+ // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
+ // The fault is not in the design of mlt, but in the implementation of the qimage producer...
+ if ( this->current_image != NULL )
+ {
+ if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
+ {
+ // Clone the image and the alpha
+ uint8_t *image_copy = mlt_pool_alloc( image_size );
+ uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+ memcpy( image_copy, this->current_image, image_size );
+
+ // Copy or default the alpha
+ if ( this->current_alpha )
+ memcpy( alpha_copy, this->current_alpha, alpha_size );
+ else
+ memset( alpha_copy, 255, alpha_size );
+
+ // Now update properties so we free the copy after
+ mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+ // We're going to pass the copy on
+ *buffer = image_copy;
+ }
+ else if ( *format == mlt_image_rgb24a )
+ {
+ // Clone the image and the alpha
+ image_size = *width * ( *height + 1 ) * 4;
+ alpha_size = *width * ( *height + 1 );
+ uint8_t *image_copy = mlt_pool_alloc( image_size );
+ uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+ mlt_convert_yuv422_to_rgb24a(this->current_image, image_copy, (*width)*(*height));
+
+ // Now update properties so we free the copy after
+ mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+ // We're going to pass the copy on
+ *buffer = image_copy;
+ }
+ }
+ else
+ {
+ // TODO: Review all cases of invalid images
+ *buffer = mlt_pool_alloc( 50 * 50 * 2 );
+ mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
+ *width = 50;
+ *height = 50;
+ }
+
+ // Release references and locks
+ pthread_mutex_unlock( &this->mutex );
+ mlt_cache_item_close( this->image_cache );
+ mlt_cache_item_close( this->alpha_cache );
+
+ return 0;
+}
+
+static uint8_t *producer_get_alpha_mask( mlt_frame this )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+ // Return the alpha mask
+ return mlt_properties_get_data( properties, "alpha", NULL );
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Get the real structure for this producer
+ producer_qimage this = producer->child;
+
+ // Fetch the producers properties
+ mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ if ( this->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
+ load_filenames( this, producer_properties );
+
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ if ( *frame != NULL && this->count > 0 )
+ {
+ // Obtain properties of frame and producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Set the producer on the frame properties
+ mlt_properties_set_data( properties, "producer_qimage", this, 0, NULL, NULL );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Ensure that we have a way to obtain the position in the get_image
+ mlt_properties_set_position( properties, "qimage_position", mlt_producer_position( producer ) );
+
+ // Refresh the image
+ refresh_qimage( this, *frame, 0, 0 );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
+
+ // Set alpha call back
+ ( *frame )->get_alpha_mask = producer_get_alpha_mask;
+
+ // Push the get_image method
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ producer_qimage this = parent->child;
+ pthread_mutex_destroy( &this->mutex );
+ parent->close = NULL;
+ mlt_producer_close( parent );
+ mlt_properties_close( this->filenames );
+ free( this );
+}
--- /dev/null
+/*
+ * qimage_wrapper.cpp -- a QT/QImage based producer for MLT
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * NB: This module is designed to be functionally equivalent to the
+ * gtk2 image loading module so it can be used as replacement.
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include "qimage_wrapper.h"
+
+#ifdef USE_QT3
+#include <qimage.h>
+#include <qmutex.h>
+
+#ifdef USE_KDE
+#include <kinstance.h>
+#include <kimageio.h>
+#endif
+
+#endif
+
+
+#ifdef USE_QT4
+#include <QtGui/QImage>
+#include <QtCore/QSysInfo>
+#include <QtCore/QMutex>
+#endif
+
+
+#include <cmath>
+
+extern "C" {
+
+#include <framework/mlt_pool.h>
+#include <framework/mlt_cache.h>
+
+#ifdef USE_KDE
+static KInstance *instance = 0L;
+#endif
+
+static void qimage_delete( void *data )
+{
+ QImage *image = ( QImage * )data;
+ delete image;
+ image = NULL;
+#ifdef USE_KDE
+ if (instance) delete instance;
+ instance = 0L;
+#endif
+}
+
+static QMutex g_mutex;
+
+#ifdef USE_KDE
+void init_qimage()
+{
+ if (!instance) {
+ instance = new KInstance("qimage_prod");
+ KImageIO::registerFormats();
+ }
+}
+#endif
+
+void refresh_qimage( producer_qimage self, mlt_frame frame, int width, int height )
+{
+ // Obtain properties of frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the producer
+ mlt_producer producer = &self->parent;
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // restore QImage
+ pthread_mutex_lock( &self->mutex );
+ mlt_cache_item qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
+ QImage *qimage = static_cast<QImage*>( mlt_cache_item_data( qimage_cache, NULL ) );
+
+ // restore scaled image
+ self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" );
+ self->current_image = static_cast<uint8_t*>( mlt_cache_item_data( self->image_cache, NULL ) );
+
+ // restore alpha channel
+ self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" );
+ self->current_alpha = static_cast<uint8_t*>( mlt_cache_item_data( self->alpha_cache, NULL ) );
+
+ // Check if user wants us to reload the image
+ if ( mlt_properties_get_int( producer_props, "force_reload" ) )
+ {
+ qimage = NULL;
+ self->current_image = NULL;
+ mlt_properties_set_int( producer_props, "force_reload", 0 );
+ }
+
+ // Obtain the cache flag and structure
+ int use_cache = mlt_properties_get_int( producer_props, "cache" );
+ mlt_properties cache = ( mlt_properties )mlt_properties_get_data( producer_props, "_cache", NULL );
+ int update_cache = 0;
+
+ // Get the time to live for each frame
+ double ttl = mlt_properties_get_int( producer_props, "ttl" );
+
+ // Get the original position of this frame
+ mlt_position position = mlt_properties_get_position( properties, "qimage_position" );
+ position += mlt_producer_get_in( producer );
+
+ // Image index
+ int image_idx = ( int )floor( ( double )position / ttl ) % self->count;
+
+ // Key for the cache
+ char image_key[ 10 ];
+ sprintf( image_key, "%d", image_idx );
+
+ g_mutex.lock();
+
+ // Check if the frame is already loaded
+ if ( use_cache )
+ {
+ if ( cache == NULL )
+ {
+ cache = mlt_properties_new( );
+ mlt_properties_set_data( producer_props, "_cache", cache, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ }
+
+ mlt_frame cached = ( mlt_frame )mlt_properties_get_data( cache, image_key, NULL );
+
+ if ( cached )
+ {
+ self->image_idx = image_idx;
+ mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+ self->current_width = mlt_properties_get_int( cached_props, "width" );
+ self->current_height = mlt_properties_get_int( cached_props, "height" );
+ mlt_properties_set_int( producer_props, "_real_width", mlt_properties_get_int( cached_props, "real_width" ) );
+ mlt_properties_set_int( producer_props, "_real_height", mlt_properties_get_int( cached_props, "real_height" ) );
+ self->current_image = ( uint8_t * )mlt_properties_get_data( cached_props, "image", NULL );
+ self->current_alpha = ( uint8_t * )mlt_properties_get_data( cached_props, "alpha", NULL );
+
+ if ( width != 0 && ( width != self->current_width || height != self->current_height ) )
+ self->current_image = NULL;
+ }
+ }
+
+ // optimization for subsequent iterations on single picture
+ if ( width != 0 && ( image_idx != self->image_idx || width != self->current_width || height != self->current_height ) )
+ self->current_image = NULL;
+ if ( image_idx != self->image_idx )
+ qimage = NULL;
+ if ( qimage == NULL && ( width == 0 || self->current_image == NULL ) )
+ {
+ self->current_image = NULL;
+ self->image_idx = image_idx;
+ qimage = new QImage( mlt_properties_get_value( self->filenames, image_idx ) );
+
+ if ( !qimage->isNull( ) )
+ {
+ // Store the width/height of the qimage
+ self->current_width = qimage->width( );
+ self->current_height = qimage->height( );
+
+ // Register qimage for destruction and reuse
+ mlt_cache_item_close( qimage_cache );
+ mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete );
+ qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
+
+ mlt_events_block( producer_props, NULL );
+ mlt_properties_set_int( producer_props, "_real_width", self->current_width );
+ mlt_properties_set_int( producer_props, "_real_height", self->current_height );
+ mlt_events_unblock( producer_props, NULL );
+ }
+ else
+ {
+ delete qimage;
+ qimage = NULL;
+ }
+ }
+
+ // If we have a pixbuf and this request specifies a valid dimension and we haven't already got a cached version...
+ if ( qimage && width > 0 && self->current_image == NULL )
+ {
+ char *interps = mlt_properties_get( properties, "rescale.interp" );
+ int interp = 0;
+
+ // QImage has two scaling modes - we'll toggle between them here
+ if ( strcmp( interps, "tiles" ) == 0 )
+ interp = 1;
+ else if ( strcmp( interps, "hyper" ) == 0 )
+ interp = 1;
+
+#ifdef USE_QT4
+ // Note - the original qimage is already safe and ready for destruction
+ QImage scaled = interp == 0 ? qimage->scaled( QSize( width, height)) : qimage->scaled( QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ QImage temp;
+ bool hasAlpha = scaled.hasAlphaChannel();
+ if (hasAlpha)
+ temp = scaled.convertToFormat(QImage::Format_ARGB32);
+ else
+ temp = scaled.convertToFormat(QImage::Format_RGB888);
+#endif
+
+#ifdef USE_QT3
+ // Note - the original qimage is already safe and ready for destruction
+ QImage scaled = interp == 0 ? qimage->scale( width, height, QImage::ScaleFree ) : qimage->smoothScale( width, height, QImage::ScaleFree );
+ QImage temp = scaled.convertDepth( 32 );
+ bool hasAlpha = true;
+#endif
+
+ // Store width and height
+ self->current_width = width;
+ self->current_height = height;
+
+ // Allocate/define image
+ self->current_image = ( uint8_t * )mlt_pool_alloc( width * ( height + 1 ) * 2 );
+ if ( !use_cache )
+ mlt_cache_item_close( self->image_cache );
+ mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.image", self->current_image, width * ( height + 1 ) * 2, mlt_pool_release );
+ self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" );
+
+ if (!hasAlpha) {
+ mlt_convert_rgb24_to_yuv422( temp.bits(), self->current_width, self->current_height, temp.bytesPerLine(), self->current_image );
+ }
+ else {
+ // Allocate the alpha mask
+ self->current_alpha = ( uint8_t * )mlt_pool_alloc( width * height );
+ if ( !use_cache )
+ mlt_cache_item_close( self->alpha_cache );
+ mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha", self->current_alpha, width * height, mlt_pool_release );
+ self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" );
+
+#ifdef USE_QT4
+ if ( QSysInfo::ByteOrder == QSysInfo::BigEndian )
+ mlt_convert_argb_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine(), self->current_image, self->current_alpha );
+ else
+ mlt_convert_bgr24a_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine( ), self->current_image, self->current_alpha );
+#endif
+
+#ifdef USE_QT3
+ // Convert the image
+ if ( QImage::systemByteOrder( ) == QImage::BigEndian )
+ mlt_convert_argb_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine( ), self->current_image, self->current_alpha );
+ else
+ mlt_convert_bgr24a_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine( ), self->current_image, self->current_alpha );
+#endif
+ }
+
+ // Ensure we update the cache when we need to
+ update_cache = use_cache;
+ }
+
+ // release references no longer needed
+ mlt_cache_item_close( qimage_cache );
+ if ( width == 0 )
+ {
+ pthread_mutex_unlock( &self->mutex );
+ mlt_cache_item_close( self->image_cache );
+ mlt_cache_item_close( self->alpha_cache );
+ }
+
+ // Set width/height of frame
+ mlt_properties_set_int( properties, "width", self->current_width );
+ mlt_properties_set_int( properties, "height", self->current_height );
+ mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+ mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+
+ if ( update_cache )
+ {
+ mlt_frame cached = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+ mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+ mlt_properties_set_int( cached_props, "width", self->current_width );
+ mlt_properties_set_int( cached_props, "height", self->current_height );
+ mlt_properties_set_int( cached_props, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+ mlt_properties_set_int( cached_props, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+ mlt_properties_set_data( cached_props, "image", self->current_image, self->current_width * ( self->current_height + 1 ) * 2, mlt_pool_release, NULL );
+ mlt_properties_set_data( cached_props, "alpha", self->current_alpha, self->current_width * self->current_height, mlt_pool_release, NULL );
+ mlt_properties_set_data( cache, image_key, cached, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ }
+ g_mutex.unlock();
+}
+
+} // extern "C"
--- /dev/null
+/*
+ * qimage_wrapper.h -- a QT/QImage based producer for MLT
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * NB: This module is designed to be functionally equivalent to the
+ * gtk2 image loading module so it can be used as replacement.
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MLT_QIMAGE_WRAPPER
+#define MLT_QIMAGE_WRAPPER
+
+#include <framework/mlt.h>
+
+#include "config.h"
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct producer_qimage_s
+{
+ struct mlt_producer_s parent;
+ mlt_properties filenames;
+ int count;
+ int image_idx;
+ uint8_t *current_image;
+ uint8_t *current_alpha;
+ int current_width;
+ int current_height;
+ mlt_cache_item image_cache;
+ mlt_cache_item alpha_cache;
+ pthread_mutex_t mutex;
+};
+
+typedef struct producer_qimage_s *producer_qimage;
+
+extern void refresh_qimage( producer_qimage, mlt_frame, int width, int height );
+#ifdef USE_KDE
+extern void init_qimage();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltresample$(LIBSUF)
+
+OBJS = factory.o \
+ filter_resample.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags samplerate`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs samplerate`
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ pkg-config samplerate 2> /dev/null
+ disable_samplerate=$?
+
+ if [ "$disable_samplerate" != "0" ]
+ then
+ echo "- libsamplerate not found: disabling"
+ touch ../disable-resample
+ fi
+ exit 0
+fi
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "resample", filter_resample_init );
+}
--- /dev/null
+/*
+ * filter_resample.c -- adjust audio sample frequency
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <samplerate.h>
+#define __USE_ISOC99 1
+#include <math.h>
+
+#define BUFFER_LEN 20480
+#define RESAMPLE_TYPE SRC_SINC_FASTEST
+
+/** Get the audio.
+*/
+
+static int resample_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get the filter service
+ mlt_filter filter = mlt_frame_pop_audio( frame );
+
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the resample information
+ int output_rate = mlt_properties_get_int( filter_properties, "frequency" );
+ SRC_STATE *state = mlt_properties_get_data( filter_properties, "state", NULL );
+ float *input_buffer = mlt_properties_get_data( filter_properties, "input_buffer", NULL );
+ float *output_buffer = mlt_properties_get_data( filter_properties, "output_buffer", NULL );
+ int channels_avail = *channels;
+ SRC_DATA data;
+ int i;
+
+ // If no resample frequency is specified, default to requested value
+ if ( output_rate == 0 )
+ output_rate = *frequency;
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples );
+
+ // Duplicate channels as necessary
+ if ( channels_avail < *channels )
+ {
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+ int j, k = 0;
+
+ // Duplicate the existing channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * channels_avail ) + k ];
+ k = ( k + 1 ) % channels_avail;
+ }
+ }
+
+ // Update the audio buffer now - destroys the old
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ *buffer = new_buffer;
+ }
+ else if ( channels_avail == 6 && *channels == 2 )
+ {
+ // Nasty hack for ac3 5.1 audio - may be a cause of failure?
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+
+ // Drop all but the first *channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ new_buffer[ ( i * *channels ) + 0 ] = (*buffer)[ ( i * channels_avail ) + 2 ];
+ new_buffer[ ( i * *channels ) + 1 ] = (*buffer)[ ( i * channels_avail ) + 3 ];
+ }
+
+ // Update the audio buffer now - destroys the old
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ *buffer = new_buffer;
+ }
+
+ // Return now if no work to do
+ if ( output_rate != *frequency )
+ {
+ float *p = input_buffer;
+ float *end = p + *samples * *channels;
+ int16_t *q = *buffer;
+
+ // Convert to floating point
+ while( p != end )
+ *p ++ = ( float )( *q ++ ) / 32768.0;
+
+ // Resample
+ data.data_in = input_buffer;
+ data.data_out = output_buffer;
+ data.src_ratio = ( float ) output_rate / ( float ) *frequency;
+ data.input_frames = *samples;
+ data.output_frames = BUFFER_LEN / *channels;
+ data.end_of_input = 0;
+ i = src_process( state, &data );
+ if ( i == 0 )
+ {
+ if ( data.output_frames_gen > *samples )
+ {
+ *buffer = mlt_pool_realloc( *buffer, data.output_frames_gen * *channels * sizeof( int16_t ) );
+ mlt_properties_set_data( properties, "audio", *buffer, *channels * data.output_frames_gen * 2, mlt_pool_release, NULL );
+ }
+
+ *samples = data.output_frames_gen;
+ *frequency = output_rate;
+
+ p = output_buffer;
+ q = *buffer;
+ end = p + *samples * *channels;
+
+ // Convert from floating back to signed 16bit
+ while( p != end )
+ {
+ if ( *p > 1.0 )
+ *p = 1.0;
+ if ( *p < -1.0 )
+ *p = -1.0;
+ if ( *p > 0 )
+ *q ++ = 32767 * *p ++;
+ else
+ *q ++ = 32768 * *p ++;
+ }
+ }
+ else
+ fprintf( stderr, "resample_get_audio: %s %d,%d,%d\n", src_strerror( i ), *frequency, *samples, output_rate );
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ if ( mlt_frame_is_test_audio( frame ) == 0 )
+ {
+ mlt_frame_push_audio( frame, this );
+ mlt_frame_push_audio( frame, resample_get_audio );
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ int error;
+ SRC_STATE *state = src_new( RESAMPLE_TYPE, 2 /* channels */, &error );
+ if ( error == 0 )
+ {
+ void *input_buffer = mlt_pool_alloc( BUFFER_LEN );
+ void *output_buffer = mlt_pool_alloc( BUFFER_LEN );
+ this->process = filter_process;
+ if ( arg != NULL )
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "frequency", atoi( arg ) );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", 2 );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "state", state, 0, (mlt_destructor)src_delete, NULL );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "input_buffer", input_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "output_buffer", output_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+ }
+ else
+ {
+ fprintf( stderr, "filter_resample_init: %s\n", src_strerror( error ) );
+ }
+ }
+ return this;
+}
--- /dev/null
+include ../../../config.mak
+
+include config.mak
+
+TARGET = ../libmltsdl$(LIBSUF)
+
+OBJS = factory.o \
+ consumer_sdl.o \
+ consumer_sdl_preview.o \
+ consumer_sdl_still.o
+
+ifeq ($(targetos),Darwin)
+CFLAGS += -ObjC
+LDFLAGS += -lobjc -framework Foundation
+else
+LDFLAGS += -lX11
+endif
+
+CFLAGS += -I../..
+CFLAGS += `sdl-config --cflags`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `sdl-config --libs`
+
+ifeq ($(WITH_SDL_IMAGE),1)
+OBJS += producer_sdl_image.o
+CFLAGS += -DWITH_SDL_IMAGE
+LDFLAGS += -lSDL_image
+endif
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ sdl-config --version > /dev/null 2>&1
+ disable_sdl=$?
+
+ if [ "$disable_sdl" = "0" ]
+ then
+ echo > config.mak
+ image=`sdl-config --prefix`/include/SDL/SDL_image.h
+ if [ -f "$image" ]
+ then
+ echo "WITH_SDL_IMAGE=1" >> config.mak
+ fi
+ else
+ echo "- sdl development libs not found: disabling"
+ touch ../disable-sdl
+ fi
+ exit 0
+fi
+
--- /dev/null
+/*
+ * consumer_sdl.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_filter.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+#include <sys/time.h>
+
+/** This classes definition.
+*/
+
+typedef struct consumer_sdl_s *consumer_sdl;
+
+struct consumer_sdl_s
+{
+ struct mlt_consumer_s parent;
+ mlt_properties properties;
+ mlt_deque queue;
+ pthread_t thread;
+ int joined;
+ int running;
+ uint8_t audio_buffer[ 4096 * 10 ];
+ int audio_avail;
+ pthread_mutex_t audio_mutex;
+ pthread_cond_t audio_cond;
+ pthread_mutex_t video_mutex;
+ pthread_cond_t video_cond;
+ int window_width;
+ int window_height;
+ int previous_width;
+ int previous_height;
+ int width;
+ int height;
+ int playing;
+ int sdl_flags;
+ SDL_Surface *sdl_screen;
+ SDL_Overlay *sdl_overlay;
+ SDL_Rect rect;
+ uint8_t *buffer;
+ int bpp;
+ int filtered;
+};
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_stop( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer parent );
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+static int consumer_get_dimensions( int *width, int *height );
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+
+/** This is what will be called by the factory - anything can be passed in
+ via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_sdl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create the consumer object
+ consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
+
+ // If no malloc'd and consumer init ok
+ if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
+ {
+ // Create the queue
+ this->queue = mlt_deque_init( );
+
+ // Get the parent consumer object
+ mlt_consumer parent = &this->parent;
+
+ // We have stuff to clean up, so override the close method
+ parent->close = consumer_close;
+
+ // get a handle on properties
+ mlt_service service = MLT_CONSUMER_SERVICE( parent );
+ this->properties = MLT_SERVICE_PROPERTIES( service );
+
+ // Set the default volume
+ mlt_properties_set_double( this->properties, "volume", 1.0 );
+
+ // This is the initialisation of the consumer
+ pthread_mutex_init( &this->audio_mutex, NULL );
+ pthread_cond_init( &this->audio_cond, NULL);
+ pthread_mutex_init( &this->video_mutex, NULL );
+ pthread_cond_init( &this->video_cond, NULL);
+
+ // Default scaler (for now we'll use nearest)
+ mlt_properties_set( this->properties, "rescale", "nearest" );
+
+ // Default buffer for low latency
+ mlt_properties_set_int( this->properties, "buffer", 1 );
+
+ // Default progressive true
+ mlt_properties_set_int( this->properties, "progressive", 0 );
+
+ // Default audio buffer
+ mlt_properties_set_int( this->properties, "audio_buffer", 512 );
+
+ // Ensure we don't join on a non-running object
+ this->joined = 1;
+
+ // process actual param
+ if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 )
+ {
+ this->width = mlt_properties_get_int( this->properties, "width" );
+ this->height = mlt_properties_get_int( this->properties, "height" );
+ }
+
+ // Set the sdl flags
+ this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF;
+
+ // Allow thread to be started/stopped
+ parent->start = consumer_start;
+ parent->stop = consumer_stop;
+ parent->is_stopped = consumer_is_stopped;
+
+ // Register specific events
+ mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );
+
+ // Return the consumer produced
+ return parent;
+ }
+
+ // malloc or consumer init failed
+ free( this );
+
+ // Indicate failure
+ return NULL;
+}
+
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( SDL_Event * )args[ 0 ] );
+}
+
+int consumer_start( mlt_consumer parent )
+{
+ consumer_sdl this = parent->child;
+
+ if ( !this->running )
+ {
+ int video_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "video_off" );
+ int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
+ int display_off = video_off | preview_off;
+ int audio_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" );
+ int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
+
+ consumer_stop( parent );
+
+ this->running = 1;
+ this->joined = 0;
+
+ if ( mlt_properties_get_int( this->properties, "width" ) > 0 )
+ this->width = mlt_properties_get_int( this->properties, "width" );
+ if ( mlt_properties_get_int( this->properties, "height" ) > 0 )
+ this->height = mlt_properties_get_int( this->properties, "height" );
+
+ this->bpp = mlt_properties_get_int( this->properties, "bpp" );
+
+ // Attach a colour space converter
+ if ( preview_off && !this->filtered )
+ {
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( parent ) );
+ mlt_filter filter = mlt_factory_filter( profile, "avcolour_space", NULL );
+ if ( filter )
+ {
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "forced", mlt_image_yuv422 );
+ mlt_service_attach( MLT_CONSUMER_SERVICE( parent ), filter );
+ mlt_filter_close( filter );
+ }
+ this->filtered = 1;
+ }
+
+ if ( sdl_started == 0 && display_off == 0 )
+ {
+ if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 )
+ {
+ fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
+ return -1;
+ }
+
+ SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+ SDL_EnableUNICODE( 1 );
+ }
+ else if ( display_off == 0 )
+ {
+ this->sdl_screen = SDL_GetVideoSurface( );
+ }
+
+ if ( audio_off == 0 )
+ SDL_InitSubSystem( SDL_INIT_AUDIO );
+
+ // Default window size
+ double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
+ this->window_width = ( double )this->height * display_ratio;
+ this->window_height = this->height;
+
+ if ( this->sdl_screen == NULL && display_off == 0 )
+ {
+ if ( mlt_properties_get_int( this->properties, "fullscreen" ) )
+ {
+ const SDL_VideoInfo *vi = SDL_GetVideoInfo();
+ this->window_width = vi->current_w;
+ this->window_height = vi->current_h;
+ this->sdl_flags |= SDL_FULLSCREEN;
+ SDL_ShowCursor( SDL_DISABLE );
+ }
+ this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+ }
+
+ pthread_create( &this->thread, NULL, consumer_thread, this );
+ }
+
+ return 0;
+}
+
+int consumer_stop( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_sdl this = parent->child;
+
+ if ( this->joined == 0 )
+ {
+ // Kill the thread and clean up
+ this->joined = 1;
+ this->running = 0;
+ if ( this->thread )
+ pthread_join( this->thread, NULL );
+
+ // internal cleanup
+ if ( this->sdl_overlay != NULL )
+ SDL_FreeYUVOverlay( this->sdl_overlay );
+ this->sdl_overlay = NULL;
+
+ if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" ) )
+ {
+ pthread_mutex_lock( &this->audio_mutex );
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+ SDL_QuitSubSystem( SDL_INIT_AUDIO );
+ }
+
+ if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" ) == 0 )
+ SDL_Quit( );
+
+ this->sdl_screen = NULL;
+ }
+
+ return 0;
+}
+
+int consumer_is_stopped( mlt_consumer parent )
+{
+ consumer_sdl this = parent->child;
+ return !this->running;
+}
+
+static int sdl_lock_display( )
+{
+ SDL_Surface *screen = SDL_GetVideoSurface( );
+ return screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 );
+}
+
+static void sdl_unlock_display( )
+{
+ SDL_Surface *screen = SDL_GetVideoSurface( );
+ if ( screen != NULL && SDL_MUSTLOCK( screen ) )
+ SDL_UnlockSurface( screen );
+}
+
+static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
+{
+ consumer_sdl this = udata;
+
+ // Get the volume
+ double volume = mlt_properties_get_double( this->properties, "volume" );
+
+ pthread_mutex_lock( &this->audio_mutex );
+
+ // Block until audio received
+ while ( this->running && len > this->audio_avail )
+ pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+
+ if ( this->audio_avail >= len )
+ {
+ // Place in the audio buffer
+ if ( volume != 1.0 )
+ SDL_MixAudio( stream, this->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) );
+ else
+ memcpy( stream, this->audio_buffer, len );
+
+ // Remove len from the audio available
+ this->audio_avail -= len;
+
+ // Remove the samples
+ memmove( this->audio_buffer, this->audio_buffer + len, this->audio_avail );
+ }
+ else
+ {
+ // Just to be safe, wipe the stream first
+ memset( stream, 0, len );
+
+ // Mix the audio
+ SDL_MixAudio( stream, this->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) );
+
+ // No audio left
+ this->audio_avail = 0;
+ }
+
+ // We're definitely playing now
+ this->playing = 1;
+
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+}
+
+static int consumer_play_audio( consumer_sdl this, mlt_frame frame, int init_audio, int *duration )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+ mlt_audio_format afmt = mlt_audio_pcm;
+
+ // Set the preferred params of the test card signal
+ int channels = mlt_properties_get_int( properties, "channels" );
+ int frequency = mlt_properties_get_int( properties, "frequency" );
+ static int counter = 0;
+
+ int samples = mlt_sample_calculator( mlt_properties_get_double( this->properties, "fps" ), frequency, counter++ );
+
+ int16_t *pcm;
+ int bytes;
+
+ mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+ *duration = ( ( samples * 1000 ) / frequency );
+
+ if ( mlt_properties_get_int( properties, "audio_off" ) )
+ {
+ this->playing = 1;
+ init_audio = 1;
+ return init_audio;
+ }
+
+ if ( init_audio == 1 )
+ {
+ SDL_AudioSpec request;
+ SDL_AudioSpec got;
+
+ int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" );
+
+ // specify audio format
+ memset( &request, 0, sizeof( SDL_AudioSpec ) );
+ this->playing = 0;
+ request.freq = frequency;
+ request.format = AUDIO_S16SYS;
+ request.channels = channels;
+ request.samples = audio_buffer;
+ request.callback = sdl_fill_audio;
+ request.userdata = (void *)this;
+ if ( SDL_OpenAudio( &request, &got ) != 0 )
+ {
+ fprintf( stderr, "SDL failed to open audio: %s\n", SDL_GetError() );
+ init_audio = 2;
+ }
+ else if ( got.size != 0 )
+ {
+ SDL_PauseAudio( 0 );
+ init_audio = 0;
+ }
+ }
+
+ if ( init_audio == 0 )
+ {
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+ bytes = ( samples * channels * 2 );
+ pthread_mutex_lock( &this->audio_mutex );
+ while ( this->running && bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) )
+ pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+ if ( this->running )
+ {
+ if ( mlt_properties_get_double( properties, "_speed" ) == 1 )
+ memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes );
+ else
+ memset( &this->audio_buffer[ this->audio_avail ], 0, bytes );
+ this->audio_avail += bytes;
+ }
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+ }
+ else
+ {
+ this->playing = 1;
+ }
+
+ return init_audio;
+}
+
+static int consumer_play_video( consumer_sdl this, mlt_frame frame )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+
+ mlt_image_format vfmt = mlt_image_yuv422;
+ int width = this->width, height = this->height;
+ uint8_t *image;
+ int changed = 0;
+
+ int video_off = mlt_properties_get_int( properties, "video_off" );
+ int preview_off = mlt_properties_get_int( properties, "preview_off" );
+ mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" );
+ int display_off = video_off | preview_off;
+
+ if ( this->running && display_off == 0 )
+ {
+ // Get the image, width and height
+ mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
+ mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+
+ // Handle events
+ if ( this->sdl_screen != NULL )
+ {
+ SDL_Event event;
+
+ sdl_lock_display( );
+ changed = consumer_get_dimensions( &this->window_width, &this->window_height );
+ sdl_unlock_display( );
+
+ while ( SDL_PollEvent( &event ) )
+ {
+ mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL );
+
+ switch( event.type )
+ {
+ case SDL_VIDEORESIZE:
+ this->window_width = event.resize.w;
+ this->window_height = event.resize.h;
+ changed = 1;
+ break;
+ case SDL_QUIT:
+ this->running = 0;
+ break;
+ case SDL_KEYDOWN:
+ {
+ mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL );
+ char keyboard[ 2 ] = " ";
+ void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL );
+ if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
+ {
+ keyboard[ 0 ] = ( char )event.key.keysym.unicode;
+ callback( producer, keyboard );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ sdl_lock_display();
+
+ if ( width != this->width || height != this->height )
+ {
+ if ( this->sdl_overlay != NULL )
+ SDL_FreeYUVOverlay( this->sdl_overlay );
+ this->sdl_overlay = NULL;
+ }
+
+ if ( this->running && ( this->sdl_screen == NULL || changed ) )
+ {
+ // Force an overlay recreation
+ if ( this->sdl_overlay != NULL )
+ SDL_FreeYUVOverlay( this->sdl_overlay );
+ this->sdl_overlay = NULL;
+
+ // open SDL window with video overlay, if possible
+ this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, this->bpp, this->sdl_flags );
+ if ( consumer_get_dimensions( &this->window_width, &this->window_height ) )
+ this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, this->bpp, this->sdl_flags );
+
+ uint32_t color = mlt_properties_get_int( this->properties, "window_background" );
+ SDL_FillRect( this->sdl_screen, NULL, color >> 8 );
+ SDL_Flip( this->sdl_screen );
+ }
+
+ if ( this->running )
+ {
+ // Determine window's new display aspect ratio
+ double this_aspect = ( double )this->window_width / this->window_height;
+
+ // Get the display aspect ratio
+ double display_ratio = mlt_properties_get_double( properties, "display_ratio" );
+
+ // Determine frame's display aspect ratio
+ double frame_aspect = mlt_frame_get_aspect_ratio( frame ) * width / height;
+
+ // Store the width and height received
+ this->width = width;
+ this->height = height;
+
+ // If using hardware scaler
+ if ( mlt_properties_get( properties, "rescale" ) != NULL &&
+ !strcmp( mlt_properties_get( properties, "rescale" ), "none" ) )
+ {
+ // Use hardware scaler to normalise display aspect ratio
+ this->rect.w = frame_aspect / this_aspect * this->window_width;
+ this->rect.h = this->window_height;
+ if ( this->rect.w > this->window_width )
+ {
+ this->rect.w = this->window_width;
+ this->rect.h = this_aspect / frame_aspect * this->window_height;
+ }
+ }
+ // Special case optimisation to negate odd effect of sample aspect ratio
+ // not corresponding exactly with image resolution.
+ else if ( (int)( this_aspect * 1000 ) == (int)( display_ratio * 1000 ) )
+ {
+ this->rect.w = this->window_width;
+ this->rect.h = this->window_height;
+ }
+ // Use hardware scaler to normalise sample aspect ratio
+ else if ( this->window_height * display_ratio > this->window_width )
+ {
+ this->rect.w = this->window_width;
+ this->rect.h = this->window_width / display_ratio;
+ }
+ else
+ {
+ this->rect.w = this->window_height * display_ratio;
+ this->rect.h = this->window_height;
+ }
+
+ this->rect.x = ( this->window_width - this->rect.w ) / 2;
+ this->rect.y = ( this->window_height - this->rect.h ) / 2;
+ this->rect.x -= this->rect.x % 2;
+
+ mlt_properties_set_int( this->properties, "rect_x", this->rect.x );
+ mlt_properties_set_int( this->properties, "rect_y", this->rect.y );
+ mlt_properties_set_int( this->properties, "rect_w", this->rect.w );
+ mlt_properties_set_int( this->properties, "rect_h", this->rect.h );
+
+ SDL_SetClipRect( this->sdl_screen, &this->rect );
+ }
+
+ if ( this->running && this->sdl_screen != NULL && this->sdl_overlay == NULL )
+ {
+ SDL_SetClipRect( this->sdl_screen, &this->rect );
+ this->sdl_overlay = SDL_CreateYUVOverlay( width, height, SDL_YUY2_OVERLAY, this->sdl_screen );
+ }
+
+ if ( this->running && this->sdl_screen != NULL && this->sdl_overlay != NULL )
+ {
+ this->buffer = this->sdl_overlay->pixels[ 0 ];
+ if ( SDL_LockYUVOverlay( this->sdl_overlay ) >= 0 )
+ {
+ if ( image != NULL )
+ memcpy( this->buffer, image, width * height * 2 );
+ SDL_UnlockYUVOverlay( this->sdl_overlay );
+ SDL_DisplayYUVOverlay( this->sdl_overlay, &this->sdl_screen->clip_rect );
+ }
+ }
+
+ sdl_unlock_display();
+ }
+ else if ( this->running )
+ {
+ vfmt = preview_format == mlt_image_none ? mlt_image_rgb24a : preview_format;
+ if ( !video_off )
+ mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
+ mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+ }
+
+ return 0;
+}
+
+static void *video_thread( void *arg )
+{
+ // Identify the arg
+ consumer_sdl this = arg;
+
+ // Obtain time of thread start
+ struct timeval now;
+ int64_t start = 0;
+ int64_t elapsed = 0;
+ struct timespec tm;
+ mlt_frame next = NULL;
+ mlt_properties properties = NULL;
+ double speed = 0;
+
+ // Get real time flag
+ int real_time = mlt_properties_get_int( this->properties, "real_time" );
+
+ // Get the current time
+ gettimeofday( &now, NULL );
+
+ // Determine start time
+ start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec;
+
+ while ( this->running )
+ {
+ // Pop the next frame
+ pthread_mutex_lock( &this->video_mutex );
+ next = mlt_deque_pop_front( this->queue );
+ while ( next == NULL && this->running )
+ {
+ pthread_cond_wait( &this->video_cond, &this->video_mutex );
+ next = mlt_deque_pop_front( this->queue );
+ }
+ pthread_mutex_unlock( &this->video_mutex );
+
+ if ( !this->running || next == NULL ) break;
+
+ // Get the properties
+ properties = MLT_FRAME_PROPERTIES( next );
+
+ // Get the speed of the frame
+ speed = mlt_properties_get_double( properties, "_speed" );
+
+ // Get the current time
+ gettimeofday( &now, NULL );
+
+ // Get the elapsed time
+ elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start;
+
+ // See if we have to delay the display of the current frame
+ if ( mlt_properties_get_int( properties, "rendered" ) == 1 && this->running )
+ {
+ // Obtain the scheduled playout time
+ int64_t scheduled = mlt_properties_get_int( properties, "playtime" );
+
+ // Determine the difference between the elapsed time and the scheduled playout time
+ int64_t difference = scheduled - elapsed;
+
+ // Smooth playback a bit
+ if ( real_time && ( difference > 20000 && speed == 1.0 ) )
+ {
+ tm.tv_sec = difference / 1000000;
+ tm.tv_nsec = ( difference % 1000000 ) * 500;
+ nanosleep( &tm, NULL );
+ }
+
+ // Show current frame if not too old
+ if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( this->queue ) < 2 ) )
+ consumer_play_video( this, next );
+
+ // If the queue is empty, recalculate start to allow build up again
+ if ( real_time && ( mlt_deque_count( this->queue ) == 0 && speed == 1.0 ) )
+ {
+ gettimeofday( &now, NULL );
+ start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000;
+ }
+ }
+
+ // This frame can now be closed
+ mlt_frame_close( next );
+ next = NULL;
+ }
+
+ if ( next != NULL )
+ mlt_frame_close( next );
+
+ mlt_consumer_stopped( &this->parent );
+
+ return NULL;
+}
+
+/** Threaded wrapper for pipe.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Identify the arg
+ consumer_sdl this = arg;
+
+ // Get the consumer
+ mlt_consumer consumer = &this->parent;
+
+ // Convenience functionality
+ int terminate_on_pause = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause" );
+ int terminated = 0;
+
+ // Video thread
+ pthread_t thread;
+
+ // internal intialization
+ int init_audio = 1;
+ int init_video = 1;
+ mlt_frame frame = NULL;
+ mlt_properties properties = NULL;
+ int duration = 0;
+ int64_t playtime = 0;
+ struct timespec tm = { 0, 100000 };
+
+ // Loop until told not to
+ while( !terminated && this->running )
+ {
+ // Get a frame from the attached producer
+ frame = mlt_consumer_rt_frame( consumer );
+
+ // Check for termination
+ if ( terminate_on_pause && frame != NULL )
+ terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
+
+ // Ensure that we have a frame
+ if ( frame != NULL )
+ {
+ // Get the frame properties
+ properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Play audio
+ init_audio = consumer_play_audio( this, frame, init_audio, &duration );
+
+ // Determine the start time now
+ if ( this->playing && init_video )
+ {
+ // Create the video thread
+ pthread_create( &thread, NULL, video_thread, this );
+
+ // Video doesn't need to be initialised any more
+ init_video = 0;
+ }
+
+ // Set playtime for this frame
+ mlt_properties_set_int( properties, "playtime", playtime );
+
+ while ( this->running && mlt_deque_count( this->queue ) > 15 )
+ nanosleep( &tm, NULL );
+
+ // Push this frame to the back of the queue
+ pthread_mutex_lock( &this->video_mutex );
+ mlt_deque_push_back( this->queue, frame );
+ pthread_cond_broadcast( &this->video_cond );
+ pthread_mutex_unlock( &this->video_mutex );
+
+ // Calculate the next playtime
+ playtime += ( duration * 1000 );
+ }
+ }
+
+ this->running = 0;
+
+ // Kill the video thread
+ if ( init_video == 0 )
+ {
+ pthread_mutex_lock( &this->video_mutex );
+ pthread_cond_broadcast( &this->video_cond );
+ pthread_mutex_unlock( &this->video_mutex );
+ pthread_join( thread, NULL );
+ }
+
+ while( mlt_deque_count( this->queue ) )
+ mlt_frame_close( mlt_deque_pop_back( this->queue ) );
+
+ this->sdl_screen = NULL;
+ this->audio_avail = 0;
+
+ return NULL;
+}
+
+static int consumer_get_dimensions( int *width, int *height )
+{
+ int changed = 0;
+
+ // SDL windows manager structure
+ SDL_SysWMinfo wm;
+
+ // Specify the SDL Version
+ SDL_VERSION( &wm.version );
+
+ // Lock the display
+ //sdl_lock_display();
+
+#ifndef __DARWIN__
+ // Get the wm structure
+ if ( SDL_GetWMInfo( &wm ) == 1 )
+ {
+ // Check that we have the X11 wm
+ if ( wm.subsystem == SDL_SYSWM_X11 )
+ {
+ // Get the SDL window
+ Window window = wm.info.x11.window;
+
+ // Get the display session
+ Display *display = wm.info.x11.display;
+
+ // Get the window attributes
+ XWindowAttributes attr;
+ XGetWindowAttributes( display, window, &attr );
+
+ // Determine whether window has changed
+ changed = *width != attr.width || *height != attr.height;
+
+ // Return width and height
+ *width = attr.width;
+ *height = attr.height;
+ }
+ }
+#endif
+
+ // Unlock the display
+ //sdl_unlock_display();
+
+ return changed;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_sdl this = parent->child;
+
+ // Stop the consumer
+ ///mlt_consumer_stop( parent );
+
+ // Now clean up the rest
+ mlt_consumer_close( parent );
+
+ // Close the queue
+ mlt_deque_close( this->queue );
+
+ // Destroy mutexes
+ pthread_mutex_destroy( &this->audio_mutex );
+ pthread_cond_destroy( &this->audio_cond );
+
+ // Finally clean up this
+ free( this );
+}
--- /dev/null
+/*
+ * Purpose: A dummy thread object to inform Cocoa that it needs to be thread safe.
+ * Author: Zachary Drew
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface DummyThread : NSObject
+- init;
+- (void)startThread:(id)arg;
+@end
+
+@implementation DummyThread
+- init
+{
+ [super init];
+ return self;
+}
+- (void)startThread:(id)arg
+{
+ return;
+}
+@end
--- /dev/null
+/*
+ * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_producer.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+
+typedef struct consumer_sdl_s *consumer_sdl;
+
+struct consumer_sdl_s
+{
+ struct mlt_consumer_s parent;
+ mlt_consumer active;
+ int ignore_change;
+ mlt_consumer play;
+ mlt_consumer still;
+ pthread_t thread;
+ int joined;
+ int running;
+ int sdl_flags;
+ double last_speed;
+
+ pthread_cond_t refresh_cond;
+ pthread_mutex_t refresh_mutex;
+ int refresh_count;
+};
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_stop( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer parent );
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+static void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer this, mlt_frame frame );
+static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer this, SDL_Event *event );
+static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer this, char *name );
+
+mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
+ if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
+ {
+ // Get the parent consumer object
+ mlt_consumer parent = &this->parent;
+
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
+
+ // Get the width and height
+ int width = mlt_properties_get_int( properties, "width" );
+ int height = mlt_properties_get_int( properties, "height" );
+
+ // Process actual param
+ if ( arg == NULL || sscanf( arg, "%dx%d", &width, &height ) == 2 )
+ {
+ mlt_properties_set_int( properties, "width", width );
+ mlt_properties_set_int( properties, "height", height );
+ }
+
+ // Create child consumers
+ this->play = mlt_factory_consumer( profile, "sdl", arg );
+ this->still = mlt_factory_consumer( profile, "sdl_still", arg );
+ mlt_properties_set( MLT_CONSUMER_PROPERTIES( parent ), "real_time", "0" );
+ mlt_properties_set( MLT_CONSUMER_PROPERTIES( parent ), "rescale", "nearest" );
+ parent->close = consumer_close;
+ parent->start = consumer_start;
+ parent->stop = consumer_stop;
+ parent->is_stopped = consumer_is_stopped;
+ this->joined = 1;
+ mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->play ), this, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
+ mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->still ), this, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
+ mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->play ), this, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
+ mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->still ), this, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
+ pthread_cond_init( &this->refresh_cond, NULL );
+ pthread_mutex_init( &this->refresh_mutex, NULL );
+ mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), this, "property-changed", ( mlt_listener )consumer_refresh_cb );
+ return parent;
+ }
+ free( this );
+ return NULL;
+}
+
+void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame )
+{
+ consumer_sdl this = parent->child;
+ this->last_speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
+ mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-frame-show", frame, NULL );
+}
+
+static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer parent, SDL_Event *event )
+{
+ mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-sdl-event", event, NULL );
+}
+
+static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name )
+{
+ if ( !strcmp( name, "refresh" ) )
+ {
+ consumer_sdl this = parent->child;
+ pthread_mutex_lock( &this->refresh_mutex );
+ this->refresh_count = this->refresh_count <= 0 ? 1 : this->refresh_count ++;
+ pthread_cond_broadcast( &this->refresh_cond );
+ pthread_mutex_unlock( &this->refresh_mutex );
+ }
+}
+
+static int consumer_start( mlt_consumer parent )
+{
+ consumer_sdl this = parent->child;
+
+ if ( !this->running )
+ {
+ // properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
+ mlt_properties play = MLT_CONSUMER_PROPERTIES( this->play );
+ mlt_properties still = MLT_CONSUMER_PROPERTIES( this->still );
+
+ char *window_id = mlt_properties_get( properties, "window_id" );
+ char *audio_driver = mlt_properties_get( properties, "audio_driver" );
+ char *video_driver = mlt_properties_get( properties, "video_driver" );
+ char *audio_device = mlt_properties_get( properties, "audio_device" );
+ char *output_display = mlt_properties_get( properties, "output_display" );
+ int progressive = mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" );
+
+ consumer_stop( parent );
+
+ this->running = 1;
+ this->joined = 0;
+ this->last_speed = 1;
+
+ if ( output_display != NULL )
+ setenv( "DISPLAY", output_display, 1 );
+
+ if ( window_id != NULL )
+ setenv( "SDL_WINDOWID", window_id, 1 );
+
+ if ( video_driver != NULL )
+ setenv( "SDL_VIDEODRIVER", video_driver, 1 );
+
+ if ( audio_driver != NULL )
+ setenv( "SDL_AUDIODRIVER", audio_driver, 1 );
+
+ if ( audio_device != NULL )
+ setenv( "AUDIODEV", audio_device, 1 );
+
+ if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 )
+ {
+ fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
+ return -1;
+ }
+
+ SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+ SDL_EnableUNICODE( 1 );
+
+ // Pass properties down
+ mlt_properties_set_data( play, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
+ mlt_properties_set_data( still, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
+ mlt_properties_set_data( play, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
+ mlt_properties_set_data( still, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
+
+ mlt_properties_set_int( play, "progressive", progressive );
+ mlt_properties_set_int( still, "progressive", progressive );
+
+ mlt_properties_pass_list( play, properties, "resize,rescale,width,height,aspect_ratio,display_ratio,volume" );
+ mlt_properties_pass_list( still, properties, "resize,rescale,width,height,aspect_ratio,display_ratio" );
+ mlt_properties_pass_list( play, properties, "deinterlace_method" );
+ mlt_properties_pass_list( still, properties, "deinterlace_method" );
+ mlt_properties_pass_list( play, properties, "preview_off,preview_format,window_background" );
+ mlt_properties_pass_list( still, properties, "preview_off,preview_format,window_background" );
+
+ mlt_properties_pass( play, properties, "play." );
+ mlt_properties_pass( still, properties, "still." );
+
+ mlt_properties_set_data( play, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
+ mlt_properties_set_data( still, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
+ mlt_properties_set_data( play, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
+ mlt_properties_set_data( still, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
+
+ mlt_properties_set_int( play, "put_mode", 1 );
+ mlt_properties_set_int( still, "put_mode", 1 );
+
+ // Start the still producer just to initialise the gui
+ mlt_consumer_start( this->still );
+ this->active = this->still;
+
+ // Inform child consumers that we control the sdl
+ mlt_properties_set_int( play, "sdl_started", 1 );
+ mlt_properties_set_int( still, "sdl_started", 1 );
+
+ pthread_create( &this->thread, NULL, consumer_thread, this );
+ }
+
+ return 0;
+}
+
+static int consumer_stop( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_sdl this = parent->child;
+
+ if ( this->joined == 0 )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
+ int app_locked = mlt_properties_get_int( properties, "app_locked" );
+ void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
+ void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
+
+ if ( app_locked && unlock ) unlock( );
+
+ // Kill the thread and clean up
+ this->running = 0;
+
+ pthread_mutex_lock( &this->refresh_mutex );
+ pthread_cond_broadcast( &this->refresh_cond );
+ pthread_mutex_unlock( &this->refresh_mutex );
+
+ pthread_join( this->thread, NULL );
+ this->joined = 1;
+
+ if ( app_locked && lock ) lock( );
+
+ SDL_Quit( );
+ }
+
+ return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer parent )
+{
+ consumer_sdl this = parent->child;
+ return !this->running;
+}
+
+static void *consumer_thread( void *arg )
+{
+ // Identify the arg
+ consumer_sdl this = arg;
+
+ // Get the consumer
+ mlt_consumer consumer = &this->parent;
+
+ // Get the properties
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+
+ // internal intialization
+ int first = 1;
+ mlt_frame frame = NULL;
+ int last_position = -1;
+
+ // Determine if the application is dealing with the preview
+ int preview_off = mlt_properties_get_int( properties, "preview_off" );
+
+ this->refresh_count = 0;
+
+ // Loop until told not to
+ while( this->running )
+ {
+ // Get a frame from the attached producer
+ frame = mlt_consumer_get_frame( consumer );
+
+ // Ensure that we have a frame
+ if ( this->running && frame != NULL )
+ {
+ // Get the speed of the frame
+ double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
+
+ // Lock during the operation
+ mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
+
+ // Get refresh request for the current frame
+ int refresh = mlt_properties_get_int( properties, "refresh" );
+
+ // Decrement refresh and clear changed
+ mlt_events_block( properties, properties );
+ mlt_properties_set_int( properties, "refresh", 0 );
+ mlt_events_unblock( properties, properties );
+
+ // Unlock after the operation
+ mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
+
+ // Set the changed property on this frame for the benefit of still
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh );
+
+ // Make sure the recipient knows that this frame isn't really rendered
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 );
+
+ // Optimisation to reduce latency
+ if ( speed == 1.0 )
+ {
+ if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
+ mlt_consumer_purge( this->play );
+ last_position = mlt_frame_get_position( frame );
+ }
+ else
+ {
+ //mlt_consumer_purge( this->play );
+ last_position = -1;
+ }
+
+ // If we're not the first frame and both consumers are stopped, then stop ourselves
+ if ( !first && mlt_consumer_is_stopped( this->play ) && mlt_consumer_is_stopped( this->still ) )
+ {
+ this->running = 0;
+ mlt_frame_close( frame );
+ }
+ // Allow a little grace time before switching consumers on speed changes
+ else if ( this->ignore_change -- > 0 && this->active != NULL && !mlt_consumer_is_stopped( this->active ) )
+ {
+ mlt_consumer_put_frame( this->active, frame );
+ }
+ // If we aren't playing normally, then use the still
+ else if ( speed != 1 )
+ {
+ if ( !mlt_consumer_is_stopped( this->play ) )
+ mlt_consumer_stop( this->play );
+ if ( mlt_consumer_is_stopped( this->still ) )
+ {
+ this->last_speed = speed;
+ this->active = this->still;
+ this->ignore_change = 0;
+ mlt_consumer_start( this->still );
+ }
+ mlt_consumer_put_frame( this->still, frame );
+ }
+ // Otherwise use the normal player
+ else
+ {
+ if ( !mlt_consumer_is_stopped( this->still ) )
+ mlt_consumer_stop( this->still );
+ if ( mlt_consumer_is_stopped( this->play ) )
+ {
+ this->last_speed = speed;
+ this->active = this->play;
+ this->ignore_change = 25;
+ mlt_consumer_start( this->play );
+ }
+ mlt_consumer_put_frame( this->play, frame );
+ }
+
+ // Copy the rectangle info from the active consumer
+ if ( this->running && preview_off == 0 )
+ {
+ mlt_properties active = MLT_CONSUMER_PROPERTIES( this->active );
+ mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
+ mlt_properties_set_int( properties, "rect_x", mlt_properties_get_int( active, "rect_x" ) );
+ mlt_properties_set_int( properties, "rect_y", mlt_properties_get_int( active, "rect_y" ) );
+ mlt_properties_set_int( properties, "rect_w", mlt_properties_get_int( active, "rect_w" ) );
+ mlt_properties_set_int( properties, "rect_h", mlt_properties_get_int( active, "rect_h" ) );
+ mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
+ }
+
+ if ( this->active == this->still )
+ {
+ pthread_mutex_lock( &this->refresh_mutex );
+ if ( this->running && speed == 0 && this->refresh_count <= 0 )
+ pthread_cond_wait( &this->refresh_cond, &this->refresh_mutex );
+ this->refresh_count --;
+ pthread_mutex_unlock( &this->refresh_mutex );
+ }
+
+ // We are definitely not waiting on the first frame any more
+ first = 0;
+ }
+ else
+ {
+ if ( frame ) mlt_frame_close( frame );
+ mlt_consumer_put_frame( this->active, NULL );
+ this->running = 0;
+ }
+ }
+
+ if ( this->play ) mlt_consumer_stop( this->play );
+ if ( this->still ) mlt_consumer_stop( this->still );
+
+ return NULL;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_sdl this = parent->child;
+
+ // Stop the consumer
+ mlt_consumer_stop( parent );
+
+ // Close the child consumers
+ mlt_consumer_close( this->play );
+ mlt_consumer_close( this->still );
+
+ // Now clean up the rest
+ mlt_consumer_close( parent );
+
+ // Finally clean up this
+ free( this );
+}
--- /dev/null
+/*
+ * consumer_sdl_still.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_filter.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+#include <sys/time.h>
+
+/** This classes definition.
+*/
+
+typedef struct consumer_sdl_s *consumer_sdl;
+
+struct consumer_sdl_s
+{
+ struct mlt_consumer_s parent;
+ mlt_properties properties;
+ pthread_t thread;
+ int joined;
+ int running;
+ int window_width;
+ int window_height;
+ int width;
+ int height;
+ int playing;
+ int sdl_flags;
+ SDL_Surface *sdl_screen;
+ SDL_Rect rect;
+ uint8_t *buffer;
+ int last_position;
+ mlt_producer last_producer;
+ int filtered;
+};
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_stop( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer parent );
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+static int consumer_get_dimensions( int *width, int *height );
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+
+/** This is what will be called by the factory - anything can be passed in
+ via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_sdl_still_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create the consumer object
+ consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
+
+ // If no malloc'd and consumer init ok
+ if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
+ {
+ // Get the parent consumer object
+ mlt_consumer parent = &this->parent;
+
+ // get a handle on properties
+ mlt_service service = MLT_CONSUMER_SERVICE( parent );
+ this->properties = MLT_SERVICE_PROPERTIES( service );
+
+ // We have stuff to clean up, so override the close method
+ parent->close = consumer_close;
+
+ // Default scaler (for now we'll use nearest)
+ mlt_properties_set( this->properties, "rescale", "nearest" );
+
+ // We're always going to run this in non-realtime mode
+ mlt_properties_set( this->properties, "real_time", "0" );
+
+ // Default progressive true
+ mlt_properties_set_int( this->properties, "progressive", 1 );
+
+ // Ensure we don't join on a non-running object
+ this->joined = 1;
+
+ // process actual param
+ if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 )
+ {
+ this->width = mlt_properties_get_int( this->properties, "width" );
+ this->height = mlt_properties_get_int( this->properties, "height" );
+ }
+ else
+ {
+ mlt_properties_set_int( this->properties, "width", this->width );
+ mlt_properties_set_int( this->properties, "height", this->height );
+ }
+
+ // Set the sdl flags
+ this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF;
+
+ // Allow thread to be started/stopped
+ parent->start = consumer_start;
+ parent->stop = consumer_stop;
+ parent->is_stopped = consumer_is_stopped;
+
+ // Register specific events
+ mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );
+
+ // Return the consumer produced
+ return parent;
+ }
+
+ // malloc or consumer init failed
+ free( this );
+
+ // Indicate failure
+ return NULL;
+}
+
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+ if ( listener != NULL )
+ listener( owner, this, ( SDL_Event * )args[ 0 ] );
+}
+
+static int consumer_start( mlt_consumer parent )
+{
+ consumer_sdl this = parent->child;
+
+ if ( !this->running )
+ {
+ int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
+ int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
+
+ // Attach a colour space converter
+ if ( !this->filtered )
+ {
+ mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( parent ) );
+ mlt_filter filter = mlt_factory_filter( profile, "avcolour_space", NULL );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "forced", mlt_image_yuv422 );
+ mlt_service_attach( MLT_CONSUMER_SERVICE( parent ), filter );
+ mlt_filter_close( filter );
+ this->filtered = 1;
+ }
+
+ consumer_stop( parent );
+
+ this->last_position = -1;
+ this->running = 1;
+ this->joined = 0;
+
+ // Allow the user to force resizing to window size
+ this->width = mlt_properties_get_int( this->properties, "width" );
+ this->height = mlt_properties_get_int( this->properties, "height" );
+
+ // Default window size
+ double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
+ this->window_width = ( double )this->height * display_ratio;
+ this->window_height = this->height;
+
+ if ( sdl_started == 0 && preview_off == 0 )
+ {
+ if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 )
+ {
+ fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
+ return -1;
+ }
+
+ SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+ SDL_EnableUNICODE( 1 );
+ }
+ else if ( preview_off == 0 )
+ {
+ if ( SDL_GetVideoSurface( ) != NULL )
+ {
+ this->sdl_screen = SDL_GetVideoSurface( );
+ }
+ }
+
+ if ( this->sdl_screen == NULL && preview_off == 0 )
+ this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+
+ pthread_create( &this->thread, NULL, consumer_thread, this );
+ }
+
+ return 0;
+}
+
+static int consumer_stop( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_sdl this = parent->child;
+
+ if ( this->joined == 0 )
+ {
+ int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
+ int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
+
+ // Kill the thread and clean up
+ this->running = 0;
+
+ pthread_join( this->thread, NULL );
+ this->joined = 1;
+
+ if ( sdl_started == 0 && preview_off == 0 )
+ SDL_Quit( );
+
+ this->sdl_screen = NULL;
+ }
+
+ return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer parent )
+{
+ consumer_sdl this = parent->child;
+ return !this->running;
+}
+
+static int sdl_lock_display( )
+{
+ SDL_Surface *screen = SDL_GetVideoSurface( );
+ return screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 );
+}
+
+static void sdl_unlock_display( )
+{
+ SDL_Surface *screen = SDL_GetVideoSurface( );
+ if ( screen != NULL && SDL_MUSTLOCK( screen ) )
+ SDL_UnlockSurface( screen );
+}
+
+static inline void display_1( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+ // Generate the affine transform scaling values
+ if ( rect.w == 0 || rect.h == 0 ) return;
+ int scale_width = ( width << 16 ) / rect.w;
+ int scale_height = ( height << 16 ) / rect.h;
+ int stride = width * 3;
+ int x, y, row_index;
+ uint8_t *q, *row;
+
+ // Constants defined for clarity and optimisation
+ int scanlength = screen->pitch;
+ uint8_t *start = ( uint8_t * )screen->pixels + rect.y * scanlength + rect.x;
+ uint8_t *p;
+
+ // Iterate through the screen using a very basic scaling algorithm
+ for ( y = 0; y < rect.h; y ++ )
+ {
+ p = start;
+ row_index = ( 32768 + scale_height * y ) >> 16;
+ row = image + stride * row_index;
+ for ( x = 0; x < rect.w; x ++ )
+ {
+ q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+ *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+ }
+ start += scanlength;
+ }
+}
+
+static inline void display_2( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+ // Generate the affine transform scaling values
+ if ( rect.w == 0 || rect.h == 0 ) return;
+ int scale_width = ( width << 16 ) / rect.w;
+ int scale_height = ( height << 16 ) / rect.h;
+ int stride = width * 3;
+ int x, y, row_index;
+ uint8_t *q, *row;
+
+ // Constants defined for clarity and optimisation
+ int scanlength = screen->pitch / 2;
+ uint16_t *start = ( uint16_t * )screen->pixels + rect.y * scanlength + rect.x;
+ uint16_t *p;
+
+ // Iterate through the screen using a very basic scaling algorithm
+ for ( y = 0; y < rect.h; y ++ )
+ {
+ p = start;
+ row_index = ( 32768 + scale_height * y ) >> 16;
+ row = image + stride * row_index;
+ for ( x = 0; x < rect.w; x ++ )
+ {
+ q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+ *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+ }
+ start += scanlength;
+ }
+}
+
+static inline void display_3( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+ // Generate the affine transform scaling values
+ if ( rect.w == 0 || rect.h == 0 ) return;
+ int scale_width = ( width << 16 ) / rect.w;
+ int scale_height = ( height << 16 ) / rect.h;
+ int stride = width * 3;
+ int x, y, row_index;
+ uint8_t *q, *row;
+
+ // Constants defined for clarity and optimisation
+ int scanlength = screen->pitch;
+ uint8_t *start = ( uint8_t * )screen->pixels + rect.y * scanlength + rect.x;
+ uint8_t *p;
+ uint32_t pixel;
+
+ // Iterate through the screen using a very basic scaling algorithm
+ for ( y = 0; y < rect.h; y ++ )
+ {
+ p = start;
+ row_index = ( 32768 + scale_height * y ) >> 16;
+ row = image + stride * row_index;
+ for ( x = 0; x < rect.w; x ++ )
+ {
+ q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+ pixel = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+ *p ++ = (pixel & 0xFF0000) >> 16;
+ *p ++ = (pixel & 0x00FF00) >> 8;
+ *p ++ = (pixel & 0x0000FF);
+ }
+ start += scanlength;
+ }
+}
+
+static inline void display_4( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+ // Generate the affine transform scaling values
+ if ( rect.w == 0 || rect.h == 0 ) return;
+ int scale_width = ( width << 16 ) / rect.w;
+ int scale_height = ( height << 16 ) / rect.h;
+ int stride = width * 3;
+ int x, y, row_index;
+ uint8_t *q, *row;
+
+ // Constants defined for clarity and optimisation
+ int scanlength = screen->pitch / 4;
+ uint32_t *start = ( uint32_t * )screen->pixels + rect.y * scanlength + rect.x;
+ uint32_t *p;
+
+ // Iterate through the screen using a very basic scaling algorithm
+ for ( y = 0; y < rect.h; y ++ )
+ {
+ p = start;
+ row_index = ( 32768 + scale_height * y ) >> 16;
+ row = image + stride * row_index;
+ for ( x = 0; x < rect.w; x ++ )
+ {
+ q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+ *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+ }
+ start += scanlength;
+ }
+}
+
+static int consumer_play_video( consumer_sdl this, mlt_frame frame )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+
+ mlt_image_format vfmt = mlt_image_rgb24;
+ int height = this->height;
+ int width = this->width;
+ uint8_t *image = NULL;
+ int changed = 0;
+ double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
+
+ void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
+ void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
+
+ if ( lock != NULL ) lock( );
+
+ sdl_lock_display();
+
+ // Handle events
+ if ( this->sdl_screen != NULL )
+ {
+ SDL_Event event;
+
+ changed = consumer_get_dimensions( &this->window_width, &this->window_height );
+
+ while ( SDL_PollEvent( &event ) )
+ {
+ mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL );
+
+ switch( event.type )
+ {
+ case SDL_VIDEORESIZE:
+ this->window_width = event.resize.w;
+ this->window_height = event.resize.h;
+ changed = 1;
+ break;
+ case SDL_QUIT:
+ this->running = 0;
+ break;
+ case SDL_KEYDOWN:
+ {
+ mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL );
+ char keyboard[ 2 ] = " ";
+ void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL );
+ if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
+ {
+ keyboard[ 0 ] = ( char )event.key.keysym.unicode;
+ callback( producer, keyboard );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if ( this->sdl_screen == NULL || changed )
+ {
+ // open SDL window
+ this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+ if ( consumer_get_dimensions( &this->window_width, &this->window_height ) )
+ this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+ uint32_t color = mlt_properties_get_int( this->properties, "window_background" );
+ SDL_FillRect( this->sdl_screen, NULL, color >> 8 );
+ changed = 1;
+ }
+ else
+ {
+ changed = 1;
+ }
+
+ if ( changed == 0 &&
+ this->last_position == mlt_frame_get_position( frame ) &&
+ this->last_producer == mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer", NULL ) )
+ {
+ sdl_unlock_display( );
+ if ( unlock != NULL ) unlock( );
+ return 0;
+ }
+
+ // Update last frame shown info
+ this->last_position = mlt_frame_get_position( frame );
+ this->last_producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer", NULL );
+
+ // Get the image, width and height
+ mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+ mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+
+ if ( image != NULL )
+ {
+ char *rescale = mlt_properties_get( properties, "rescale" );
+ if ( rescale != NULL && strcmp( rescale, "none" ) )
+ {
+ double this_aspect = display_ratio / ( ( double )this->window_width / ( double )this->window_height );
+ this->rect.w = this_aspect * this->window_width;
+ this->rect.h = this->window_height;
+ if ( this->rect.w > this->window_width )
+ {
+ this->rect.w = this->window_width;
+ this->rect.h = ( 1.0 / this_aspect ) * this->window_height;
+ }
+ }
+ else
+ {
+ double frame_aspect = mlt_frame_get_aspect_ratio( frame ) * width / height;
+ this->rect.w = frame_aspect * this->window_height;
+ this->rect.h = this->window_height;
+ if ( this->rect.w > this->window_width )
+ {
+ this->rect.w = this->window_width;
+ this->rect.h = ( 1.0 / frame_aspect ) * this->window_width;
+ }
+ }
+
+ this->rect.x = ( this->window_width - this->rect.w ) / 2;
+ this->rect.y = ( this->window_height - this->rect.h ) / 2;
+
+ mlt_properties_set_int( this->properties, "rect_x", this->rect.x );
+ mlt_properties_set_int( this->properties, "rect_y", this->rect.y );
+ mlt_properties_set_int( this->properties, "rect_w", this->rect.w );
+ mlt_properties_set_int( this->properties, "rect_h", this->rect.h );
+ }
+
+ if ( !mlt_consumer_is_stopped( &this->parent ) && SDL_GetVideoSurface( ) != NULL && this->sdl_screen != NULL && this->sdl_screen->pixels != NULL )
+ {
+ switch( this->sdl_screen->format->BytesPerPixel )
+ {
+ case 1:
+ display_1( this->sdl_screen, this->rect, image, width, height );
+ break;
+ case 2:
+ display_2( this->sdl_screen, this->rect, image, width, height );
+ break;
+ case 3:
+ display_3( this->sdl_screen, this->rect, image, width, height );
+ break;
+ case 4:
+ display_4( this->sdl_screen, this->rect, image, width, height );
+ break;
+ default:
+ fprintf( stderr, "Unsupported video depth %d\n", this->sdl_screen->format->BytesPerPixel );
+ break;
+ }
+
+ // Flip it into sight
+ SDL_Flip( this->sdl_screen );
+ }
+
+ sdl_unlock_display();
+
+ if ( unlock != NULL ) unlock( );
+
+ return 1;
+}
+
+/** Threaded wrapper for pipe.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Identify the arg
+ consumer_sdl this = arg;
+
+ // Get the consumer
+ mlt_consumer consumer = &this->parent;
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+
+ // internal intialization
+ mlt_frame frame = NULL;
+ mlt_image_format vfmt = mlt_image_rgb24a;
+ int height = this->height;
+ int width = this->width;
+ uint8_t *image = NULL;
+
+ // Allow the hosting app to provide the preview
+ int preview_off = mlt_properties_get_int( properties, "preview_off" );
+ mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" );
+
+ // Check if a specific colour space has been requested
+ if ( preview_off && preview_format != mlt_image_none )
+ vfmt = preview_format;
+
+ // Loop until told not to
+ while( this->running )
+ {
+ // Get a frame from the attached producer
+ frame = mlt_consumer_rt_frame( consumer );
+
+ // Ensure that we have a frame
+ if ( this->running && frame != NULL )
+ {
+ if ( preview_off == 0 )
+ {
+ consumer_play_video( this, frame );
+ }
+ else
+ {
+ mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
+ mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+ }
+ mlt_frame_close( frame );
+ }
+ else
+ {
+ if ( frame ) mlt_frame_close( frame );
+ this->running = 0;
+ }
+ }
+
+ return NULL;
+}
+
+static int consumer_get_dimensions( int *width, int *height )
+{
+ int changed = 0;
+
+ // SDL windows manager structure
+ SDL_SysWMinfo wm;
+
+ // Specify the SDL Version
+ SDL_VERSION( &wm.version );
+
+ // Get the wm structure
+ if ( SDL_GetWMInfo( &wm ) == 1 )
+ {
+#ifndef __DARWIN__
+ // Check that we have the X11 wm
+ if ( wm.subsystem == SDL_SYSWM_X11 )
+ {
+ // Get the SDL window
+ Window window = wm.info.x11.window;
+
+ // Get the display session
+ Display *display = wm.info.x11.display;
+
+ // Get the window attributes
+ XWindowAttributes attr;
+ XGetWindowAttributes( display, window, &attr );
+
+ // Determine whether window has changed
+ changed = *width != attr.width || *height != attr.height;
+
+ // Return width and height
+ *width = attr.width;
+ *height = attr.height;
+ }
+#endif
+ }
+
+ return changed;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_sdl this = parent->child;
+
+ // Stop the consumer
+ mlt_consumer_stop( parent );
+
+ // Now clean up the rest
+ mlt_consumer_close( parent );
+
+ // Finally clean up this
+ free( this );
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_sdl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_consumer consumer_sdl_still_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+#ifdef WITH_SDL_IMAGE
+extern mlt_producer producer_sdl_image_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+#endif
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( consumer_type, "sdl", consumer_sdl_init );
+ MLT_REGISTER( consumer_type, "sdl_preview", consumer_sdl_preview_init );
+ MLT_REGISTER( consumer_type, "sdl_still", consumer_sdl_still_init );
+#ifdef WITH_SDL_IMAGE
+ MLT_REGISTER( producer_type, "sdl_image", producer_sdl_image_init );
+#endif
+}
--- /dev/null
+/*
+ * producer_sdl_image.c -- Image loader which wraps SDL_image
+ * Copyright (C) 2005 Visual Media FX
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_pool.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <math.h>
+
+#include <SDL_image.h>
+
+static int producer_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+ SDL_Surface *surface = mlt_properties_get_data( properties, "surface", NULL );
+ SDL_Surface *converted = NULL;
+ uint8_t *alpha;
+
+ *width = surface->w;
+ *height = surface->h;
+ *format = mlt_image_yuv422;
+ *image = mlt_pool_alloc( *width * *height * 2 );
+ alpha = mlt_pool_alloc( *width * *height );
+
+ if ( surface->format->BitsPerPixel != 32 && surface->format->BitsPerPixel != 24 )
+ {
+ SDL_PixelFormat fmt;
+ fmt.BitsPerPixel = 24;
+ fmt.BytesPerPixel = 3;
+ fmt.Rshift = 16;
+ fmt.Gshift = 8;
+ fmt.Bshift = 0;
+ fmt.Rmask = 0xff << 16;
+ fmt.Gmask = 0xff << 8;
+ fmt.Bmask = 0xff;
+ converted = SDL_ConvertSurface( surface, &fmt, 0 );
+ }
+
+ switch( surface->format->BitsPerPixel )
+ {
+ case 32:
+ mlt_convert_rgb24a_to_yuv422( surface->pixels, *width, *height, surface->pitch, *image, alpha );
+ break;
+ case 24:
+ mlt_convert_rgb24_to_yuv422( surface->pixels, *width, *height, surface->pitch, *image );
+ memset( alpha, 255, *width * *height );
+ break;
+ default:
+ mlt_convert_rgb24_to_yuv422( converted->pixels, *width, *height, converted->pitch, *image );
+ memset( alpha, 255, *width * *height );
+ break;
+ }
+
+ if ( converted )
+ SDL_FreeSurface( converted );
+
+ // Update the frame
+ mlt_properties_set_data( properties, "image", *image, *width * *height * 2, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "alpha", alpha, *width * *height, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "width", *width );
+ mlt_properties_set_int( properties, "height", *height );
+
+ return 0;
+}
+
+static int filter_files( const struct dirent *de )
+{
+ return de->d_name[ 0 ] != '.';
+}
+
+static mlt_properties parse_file_names( char *resource )
+{
+ mlt_properties properties = mlt_properties_new( );
+
+ if ( strstr( resource, "/.all." ) != NULL )
+ {
+ char *dir_name = strdup( resource );
+ char *extension = strrchr( resource, '.' );
+ *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
+ char fullname[ 1024 ];
+ strcpy( fullname, dir_name );
+ struct dirent **de = NULL;
+ int n = scandir( fullname, &de, filter_files, alphasort );
+ int i;
+ struct stat info;
+
+ for (i = 0; i < n; i++ )
+ {
+ snprintf( fullname, 1023, "%s%s", dir_name, de[i]->d_name );
+ if ( strstr( fullname, extension ) && lstat( fullname, &info ) == 0 &&
+ ( S_ISREG( info.st_mode ) || info.st_mode | S_IXUSR ) )
+ {
+ char temp[ 20 ];
+ sprintf( temp, "%d", i );
+ mlt_properties_set( properties, temp, fullname );
+ }
+ free( de[ i ] );
+ }
+
+ free( de );
+ free( dir_name );
+ }
+ else
+ {
+ mlt_properties_set( properties, "0", resource );
+ }
+
+ return properties;
+}
+
+static SDL_Surface *load_image( mlt_producer producer )
+{
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+ char *resource = mlt_properties_get( properties, "resource" );
+ char *last_resource = mlt_properties_get( properties, "_last_resource" );
+ int image_idx = 0;
+ char *this_resource = NULL;
+ double ttl = mlt_properties_get_int( properties, "ttl" );
+ mlt_position position = mlt_producer_position( producer );
+ SDL_Surface *surface = mlt_properties_get_data( properties, "_surface", NULL );
+ mlt_properties filenames = mlt_properties_get_data( properties, "_filenames", NULL );
+
+ if ( filenames == NULL )
+ {
+ filenames = parse_file_names( resource );
+ mlt_properties_set_data( properties, "_surface", surface, 0, ( mlt_destructor )SDL_FreeSurface, 0 );
+ }
+
+ if ( mlt_properties_count( filenames ) )
+ {
+ image_idx = ( int )floor( ( double )position / ttl ) % mlt_properties_count( filenames );
+ this_resource = mlt_properties_get_value( filenames, image_idx );
+
+ if ( surface == NULL || last_resource == NULL || strcmp( last_resource, this_resource ) )
+ {
+ surface = IMG_Load( this_resource );
+ if ( surface != NULL )
+ {
+ surface->refcount ++;
+ mlt_properties_set_data( properties, "_surface", surface, 0, ( mlt_destructor )SDL_FreeSurface, 0 );
+ mlt_properties_set( properties, "_last_resource", this_resource );
+ mlt_properties_set_int( properties, "_real_width", surface->w );
+ mlt_properties_set_int( properties, "_real_height", surface->h );
+ }
+ }
+ else if ( surface != NULL )
+ {
+ surface->refcount ++;
+ }
+ }
+
+ return surface;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Generate a frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ if ( *frame != NULL )
+ {
+ // Create the surface for the current image
+ SDL_Surface *surface = load_image( producer );
+
+ if ( surface != NULL )
+ {
+ // Obtain properties of frame and producer
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Obtain properties of producer
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Set producer-specific frame properties
+ mlt_properties_set_int( properties, "progressive", 1 );
+ mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+ mlt_properties_set_data( properties, "surface", surface, 0, ( mlt_destructor )SDL_FreeSurface, NULL );
+ mlt_properties_set_int( properties, "real_width", surface->w );
+ mlt_properties_set_int( properties, "real_height", surface->h );
+
+ // Push the get_image method
+ mlt_frame_push_get_image( *frame, producer_get_image );
+ }
+ }
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer producer )
+{
+ producer->close = NULL;
+ mlt_producer_close( producer );
+ free( producer );
+}
+
+mlt_producer producer_sdl_image_init( mlt_profile profile, mlt_service_type type, const char *id, char *file )
+{
+ mlt_producer producer = calloc( 1, sizeof( struct mlt_producer_s ) );
+ if ( producer != NULL && mlt_producer_init( producer, NULL ) == 0 )
+ {
+ // Get the properties interface
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Callback registration
+ producer->get_frame = producer_get_frame;
+ producer->close = ( mlt_destructor )producer_close;
+
+ // Set the default properties
+ mlt_properties_set( properties, "resource", file );
+ mlt_properties_set( properties, "_resource", "" );
+ mlt_properties_set_double( properties, "aspect_ratio", 1 );
+ mlt_properties_set_int( properties, "ttl", 25 );
+ mlt_properties_set_int( properties, "progressive", 1 );
+
+ // Validate the resource
+ SDL_Surface *surface = NULL;
+ if ( file && ( surface = load_image( producer ) ) )
+ {
+ SDL_FreeSurface( surface );
+ mlt_properties_set_data( properties, "_surface", NULL, 0, NULL, NULL );
+ }
+ else
+ {
+ producer_close( producer );
+ producer = NULL;
+ }
+ return producer;
+ }
+ free( producer );
+ return NULL;
+}
+
+
--- /dev/null
+include ../../../config.mak
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+include config.mak
+
+TARGET = ../libmltsox$(LIBSUF)
+
+OBJS = factory.o \
+ filter_sox.o
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+ # Determine how to lookup dependencies of executable for OS
+ targetos=$(uname -s)
+ case $targetos in
+ Darwin)
+ LDD="otool -L"
+ ;;
+ Linux|FreeBSD)
+ LDD="ldd"
+ ;;
+ *)
+ ;;
+ esac
+
+ pkg-config sox
+ if [ $? -eq 0 ]
+ then
+ disable_sox=0
+ echo "CFLAGS += $(pkg-config --cflags sox) -I$(pkg-config --variable=prefix sox)" > config.mak
+ echo "LDFLAGS += $(pkg-config --libs sox)" >> config.mak
+ [ $(pkg-config --modversion sox | cut -d. -f1) -gt 13 ] && echo "CFLAGS += -DSOX14" >> config.mak
+ else
+ which libst-config > /dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ disable_sox=0
+
+ # determine if we need libsndfile
+ $LDD $(which sox) | grep libsndfile > /dev/null
+ [ $? -eq 0 ] && libsndfile="-lsndfile"
+
+ # determine if we need libsamplerate
+ $LDD $(which sox) | grep libsamplerate > /dev/null
+ [ $? -eq 0 ] && libsamplerate="-lsamplerate"
+
+ echo "CFLAGS += $(libst-config --cflags) -I../.." > config.mak
+ echo "LDFLAGS += -lst $(libst-config --libs) $libsndfile $libsamplerate" >> config.mak
+ else
+ sox --version 2> /dev/null | grep 'v14.' > /dev/null
+ disable_sox=$?
+ if [ $disable_sox -eq 0 ]
+ then
+ LIBDIR=lib
+ bits=$(uname -m)
+ case $bits in
+ x86_64)
+ [ -d /usr/lib/lib64 ] && export LIBDIR=lib64 || export LIBDIR=lib
+ ;;
+ *)
+ export LIBDIR=lib
+ ;;
+ esac
+
+ sox=$(which sox)
+ # chop sox
+ soxdir=$(dirname $sox)
+ # chop bin
+ soxdir=$(dirname $soxdir)
+
+ # determine if we need libsamplerate
+ $LDD "$sox" | grep libsamplerate > /dev/null
+ [ $? -eq 0 ] && libsamplerate="-lsamplerate"
+
+ # determine if we need libsfx
+ $LDD $(which sox) | grep libsfx > /dev/null
+ [ $? -eq 0 ] && libsfx="-lsfx"
+
+ echo "CFLAGS += -DSOX14 -I$soxdir/include" > config.mak
+ echo "LDFLAGS += -L$soxdir/$LIBDIR -lsox $libsfx $libsamplerate" >> config.mak
+ fi
+ fi
+ fi
+
+ if [ "$disable_sox" != "0" ]
+ then
+ echo "- sox not found: disabling"
+ touch ../disable-sox
+ fi
+
+ exit 0
+fi
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_sox_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "sox", filter_sox_init );
+}
--- /dev/null
+/*
+ * filter_sox.c -- apply any number of SOX effects using libst
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_tokeniser.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+// TODO: does not support multiple effects with SoX v14.1.0+
+
+#ifdef SOX14
+# include <sox.h>
+# define ST_EOF SOX_EOF
+# define ST_SUCCESS SOX_SUCCESS
+# define st_sample_t sox_sample_t
+# define eff_t sox_effect_t*
+# define ST_LIB_VERSION_CODE SOX_LIB_VERSION_CODE
+# define ST_LIB_VERSION SOX_LIB_VERSION
+# if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,2,0))
+# define st_size_t size_t
+# else
+# define st_size_t sox_size_t
+# endif
+# define ST_SIGNED_WORD_TO_SAMPLE(d,clips) SOX_SIGNED_16BIT_TO_SAMPLE(d,clips)
+# if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+# define ST_SSIZE_MIN SOX_SAMPLE_MIN
+# else
+# define ST_SSIZE_MIN SOX_SSIZE_MIN
+# endif
+# define ST_SAMPLE_TO_SIGNED_WORD(d,clips) SOX_SAMPLE_TO_SIGNED_16BIT(d,clips)
+#else
+# include <st.h>
+#endif
+
+#define BUFFER_LEN 8192
+#define AMPLITUDE_NORM 0.2511886431509580 /* -12dBFS */
+#define AMPLITUDE_MIN 0.00001
+
+/** Compute the mean of a set of doubles skipping unset values flagged as -1
+*/
+static inline double mean( double *buf, int count )
+{
+ double mean = 0;
+ int i;
+ int j = 0;
+
+ for ( i = 0; i < count; i++ )
+ {
+ if ( buf[ i ] != -1.0 )
+ {
+ mean += buf[ i ];
+ j ++;
+ }
+ }
+ if ( j > 0 )
+ mean /= j;
+
+ return mean;
+}
+
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+static void delete_effect( eff_t effp )
+{
+ free( effp->priv );
+ free( (void*)effp->in_encoding );
+ free( effp );
+}
+#endif
+
+/** Create an effect state instance for a channels
+*/
+static int create_effect( mlt_filter this, char *value, int count, int channel, int frequency )
+{
+ mlt_tokeniser tokeniser = mlt_tokeniser_init();
+ char id[ 256 ];
+ int error = 1;
+
+ // Tokenise the effect specification
+ mlt_tokeniser_parse_new( tokeniser, value, " " );
+ if ( tokeniser->count < 1 )
+ return error;
+
+ // Locate the effect
+ mlt_destructor effect_destructor = mlt_pool_release;
+#ifdef SOX14
+ //fprintf(stderr, "%s: effect %s count %d\n", __FUNCTION__, tokeniser->tokens[0], tokeniser->count );
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+ sox_effect_handler_t const *eff_handle = sox_find_effect( tokeniser->tokens[0] );
+ if (eff_handle == NULL ) return error;
+ eff_t eff = sox_create_effect( eff_handle );
+ effect_destructor = ( mlt_destructor ) delete_effect;
+ sox_encodinginfo_t *enc = calloc( 1, sizeof( sox_encodinginfo_t ) );
+ enc->encoding = SOX_ENCODING_SIGN2;
+ enc->bits_per_sample = 16;
+ eff->in_encoding = eff->out_encoding = enc;
+#else
+ eff_t eff = mlt_pool_alloc( sizeof( sox_effect_t ) );
+ sox_create_effect( eff, sox_find_effect( tokeniser->tokens[0] ) );
+#endif
+ int opt_count = tokeniser->count - 1;
+#else
+ eff_t eff = mlt_pool_alloc( sizeof( struct st_effect ) );
+ int opt_count = st_geteffect_opt( eff, tokeniser->count, tokeniser->tokens );
+#endif
+
+ // If valid effect
+ if ( opt_count != ST_EOF )
+ {
+ // Supply the effect parameters
+#ifdef SOX14
+ if ( ( * eff->handler.getopts )( eff, opt_count, &tokeniser->tokens[ tokeniser->count > 1 ? 1 : 0 ] ) == ST_SUCCESS )
+#else
+ if ( ( * eff->h->getopts )( eff, opt_count, &tokeniser->tokens[ tokeniser->count - opt_count ] ) == ST_SUCCESS )
+#endif
+ {
+ // Set the sox signal parameters
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+ eff->in_signal.rate = frequency;
+ eff->out_signal.rate = frequency;
+ eff->in_signal.channels = 1;
+ eff->out_signal.channels = 1;
+ eff->in_signal.precision = 16;
+ eff->out_signal.precision = 16;
+ eff->in_signal.length = 0;
+ eff->out_signal.length = 0;
+#else
+ eff->ininfo.rate = frequency;
+ eff->outinfo.rate = frequency;
+ eff->ininfo.channels = 1;
+ eff->outinfo.channels = 1;
+#endif
+
+ // Start the effect
+#ifdef SOX14
+ if ( ( * eff->handler.start )( eff ) == ST_SUCCESS )
+#else
+ if ( ( * eff->h->start )( eff ) == ST_SUCCESS )
+#endif
+ {
+ // Construct id
+ sprintf( id, "_effect_%d_%d", count, channel );
+
+ // Save the effect state
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), id, eff, 0, effect_destructor, NULL );
+ error = 0;
+ }
+ }
+ }
+ // Some error occurred so delete the temp effect state
+ if ( error == 1 )
+ effect_destructor( eff );
+
+ mlt_tokeniser_close( tokeniser );
+
+ return error;
+}
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties of the frame
+ mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Get the filter service
+ mlt_filter filter = mlt_frame_pop_audio( frame );
+
+ // Get the filter properties
+ mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+ // Get the properties
+ st_sample_t *input_buffer = mlt_properties_get_data( filter_properties, "input_buffer", NULL );
+ st_sample_t *output_buffer = mlt_properties_get_data( filter_properties, "output_buffer", NULL );
+ int channels_avail = *channels;
+ int i; // channel
+ int count = mlt_properties_get_int( filter_properties, "_effect_count" );
+
+ // Get the producer's audio
+ mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples );
+
+ // Duplicate channels as necessary
+ if ( channels_avail < *channels )
+ {
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+ int j, k = 0;
+
+ // Duplicate the existing channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ for ( j = 0; j < *channels; j++ )
+ {
+ new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * channels_avail ) + k ];
+ k = ( k + 1 ) % channels_avail;
+ }
+ }
+
+ // Update the audio buffer now - destroys the old
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ *buffer = new_buffer;
+ }
+ else if ( channels_avail == 6 && *channels == 2 )
+ {
+ // Nasty hack for ac3 5.1 audio - may be a cause of failure?
+ int size = *channels * *samples * sizeof( int16_t );
+ int16_t *new_buffer = mlt_pool_alloc( size );
+
+ // Drop all but the first *channels
+ for ( i = 0; i < *samples; i++ )
+ {
+ new_buffer[ ( i * *channels ) + 0 ] = (*buffer)[ ( i * channels_avail ) + 2 ];
+ new_buffer[ ( i * *channels ) + 1 ] = (*buffer)[ ( i * channels_avail ) + 3 ];
+ }
+
+ // Update the audio buffer now - destroys the old
+ mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+ *buffer = new_buffer;
+ }
+
+ // Even though some effects are multi-channel aware, it is not reliable
+ // We must maintain a separate effect state for each channel
+ for ( i = 0; i < *channels; i++ )
+ {
+ char id[ 256 ];
+ sprintf( id, "_effect_0_%d", i );
+
+ // Get an existing effect state
+ eff_t e = mlt_properties_get_data( filter_properties, id, NULL );
+
+ // Validate the existing effect state
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+ if ( e != NULL && ( e->in_signal.rate != *frequency ||
+ e->out_signal.rate != *frequency ) )
+#else
+ if ( e != NULL && ( e->ininfo.rate != *frequency ||
+ e->outinfo.rate != *frequency ) )
+#endif
+ e = NULL;
+
+ // (Re)Create the effect state
+ if ( e == NULL )
+ {
+ int j = 0;
+
+ // Reset the count
+ count = 0;
+
+ // Loop over all properties
+ for ( j = 0; j < mlt_properties_count( filter_properties ); j ++ )
+ {
+ // Get the name of this property
+ char *name = mlt_properties_get_name( filter_properties, j );
+
+ // If the name does not contain a . and matches effect
+ if ( !strncmp( name, "effect", 6 ) )
+ {
+ // Get the effect specification
+ char *value = mlt_properties_get( filter_properties, name );
+
+ // Create an instance
+ if ( create_effect( filter, value, count, i, *frequency ) == 0 )
+ count ++;
+ }
+ }
+
+ // Save the number of filters
+ mlt_properties_set_int( filter_properties, "_effect_count", count );
+
+ }
+ if ( *samples > 0 && count > 0 )
+ {
+ st_sample_t *p = input_buffer;
+ st_sample_t *end = p + *samples;
+ int16_t *q = *buffer + i;
+ st_size_t isamp = *samples;
+ st_size_t osamp = *samples;
+ double rms = 0;
+ int j;
+ char *normalise = mlt_properties_get( filter_properties, "normalise" );
+ double normalised_gain = 1.0;
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(13,0,0))
+ st_sample_t dummy_clipped_count = 0;
+#endif
+
+ // Convert to sox encoding
+ while( p != end )
+ {
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(13,0,0))
+ *p = ST_SIGNED_WORD_TO_SAMPLE( *q, dummy_clipped_count );
+#else
+ *p = ST_SIGNED_WORD_TO_SAMPLE( *q );
+#endif
+ // Compute rms amplitude while we are accessing each sample
+ rms += ( double )*p * ( double )*p;
+
+ p ++;
+ q += *channels;
+ }
+
+ // Compute final rms amplitude
+ rms = sqrt( rms / *samples / ST_SSIZE_MIN / ST_SSIZE_MIN );
+
+ if ( normalise )
+ {
+ int window = mlt_properties_get_int( filter_properties, "window" );
+ double *smooth_buffer = mlt_properties_get_data( filter_properties, "smooth_buffer", NULL );
+ double max_gain = mlt_properties_get_double( filter_properties, "max_gain" );
+
+ // Default the maximum gain factor to 20dBFS
+ if ( max_gain == 0 )
+ max_gain = 10.0;
+
+ // The smoothing buffer prevents radical shifts in the gain level
+ if ( window > 0 && smooth_buffer != NULL )
+ {
+ int smooth_index = mlt_properties_get_int( filter_properties, "_smooth_index" );
+ smooth_buffer[ smooth_index ] = rms;
+
+ // Ignore very small values that adversely affect the mean
+ if ( rms > AMPLITUDE_MIN )
+ mlt_properties_set_int( filter_properties, "_smooth_index", ( smooth_index + 1 ) % window );
+
+ // Smoothing is really just a mean over the past N values
+ normalised_gain = AMPLITUDE_NORM / mean( smooth_buffer, window );
+ }
+ else if ( rms > 0 )
+ {
+ // Determine gain to apply as current amplitude
+ normalised_gain = AMPLITUDE_NORM / rms;
+ }
+
+ //printf("filter_sox: rms %.3f gain %.3f\n", rms, normalised_gain );
+
+ // Govern the maximum gain
+ if ( normalised_gain > max_gain )
+ normalised_gain = max_gain;
+ }
+
+ // For each effect
+ for ( j = 0; j < count; j++ )
+ {
+ sprintf( id, "_effect_%d_%d", j, i );
+ e = mlt_properties_get_data( filter_properties, id, NULL );
+
+ // We better have this guy
+ if ( e != NULL )
+ {
+ float saved_gain = 1.0;
+
+ // XXX: hack to apply the normalised gain level to the vol effect
+#ifdef SOX14
+ if ( normalise && strcmp( e->handler.name, "vol" ) == 0 )
+#else
+ if ( normalise && strcmp( e->name, "vol" ) == 0 )
+#endif
+ {
+ float *f = ( float * )( e->priv );
+ saved_gain = *f;
+ *f = saved_gain * normalised_gain;
+ }
+
+ // Apply the effect
+#ifdef SOX14
+ if ( ( * e->handler.flow )( e, input_buffer, output_buffer, &isamp, &osamp ) == ST_SUCCESS )
+#else
+ if ( ( * e->h->flow )( e, input_buffer, output_buffer, &isamp, &osamp ) == ST_SUCCESS )
+#endif
+ {
+ // Swap input and output buffer pointers for subsequent effects
+ p = input_buffer;
+ input_buffer = output_buffer;
+ output_buffer = p;
+ }
+
+ // XXX: hack to restore the original vol gain to prevent accumulation
+#ifdef SOX14
+ if ( normalise && strcmp( e->handler.name, "vol" ) == 0 )
+#else
+ if ( normalise && strcmp( e->name, "vol" ) == 0 )
+#endif
+ {
+ float *f = ( float * )( e->priv );
+ *f = saved_gain;
+ }
+ }
+ }
+
+ // Convert back to signed 16bit
+ p = input_buffer;
+ q = *buffer + i;
+ end = p + *samples;
+ while ( p != end )
+ {
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(13,0,0))
+ *q = ST_SAMPLE_TO_SIGNED_WORD( *p ++, dummy_clipped_count );
+#else
+ *q = ST_SAMPLE_TO_SIGNED_WORD( *p ++ );
+#endif
+ q += *channels;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ if ( mlt_frame_is_test_audio( frame ) == 0 )
+ {
+ // Add the filter to the frame
+ mlt_frame_push_audio( frame, this );
+ mlt_frame_push_audio( frame, filter_get_audio );
+
+ // Parse the window property and allocate smoothing buffer if needed
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+ int window = mlt_properties_get_int( properties, "window" );
+ if ( mlt_properties_get( properties, "smooth_buffer" ) == NULL && window > 1 )
+ {
+ // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalisation
+ double *smooth_buffer = (double*) calloc( window, sizeof( double ) );
+ int i;
+ for ( i = 0; i < window; i++ )
+ smooth_buffer[ i ] = -1.0;
+ mlt_properties_set_data( properties, "smooth_buffer", smooth_buffer, 0, free, NULL );
+ }
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_sox_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ void *input_buffer = mlt_pool_alloc( BUFFER_LEN );
+ void *output_buffer = mlt_pool_alloc( BUFFER_LEN );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+ this->process = filter_process;
+
+ if ( arg != NULL )
+ mlt_properties_set( properties, "effect", arg );
+ mlt_properties_set_data( properties, "input_buffer", input_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+ mlt_properties_set_data( properties, "output_buffer", output_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "window", 75 );
+ }
+ return this;
+}
+
+// What to do when a libst internal failure occurs
+void cleanup(void){}
+
+// Is there a build problem with my sox-devel package?
+#ifndef gsm_create
+void gsm_create(void){}
+#endif
+#ifndef gsm_decode
+void gsm_decode(void){}
+#endif
+#ifndef gdm_encode
+void gsm_encode(void){}
+#endif
+#ifndef gsm_destroy
+void gsm_destroy(void){}
+#endif
+#ifndef gsm_option
+void gsm_option(void){}
+#endif
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltvalerie$(LIBSUF)
+
+OBJS = factory.o \
+ consumer_valerie.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../valerie -lvalerie
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * consumer_valerie.c -- pushes a service via valerie
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@telenet.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <valerie/valerie.h>
+#include <valerie/valerie_remote.h>
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+
+static int consumer_is_stopped( mlt_consumer this );
+static int consumer_start( mlt_consumer this );
+
+/** This is what will be called by the factory
+*/
+
+mlt_consumer consumer_valerie_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create the consumer object
+ mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
+
+ // If no malloc'd and consumer init ok
+ if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
+ {
+ if ( arg != NULL && strchr( arg, ':' ) )
+ {
+ char *temp = NULL;
+ int port = atoi( strchr( arg, ':' ) + 1 );
+ mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "server", arg );
+ temp = mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "server" );
+ *( strchr( temp, ':' ) ) = '\0';
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "port", port );
+ }
+ else
+ {
+ mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "server", arg == NULL ? "localhost" : arg );
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "port", 5250 );
+ }
+
+ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "unit", 0 );
+ mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "command", "append" );
+
+ // Allow thread to be started/stopped
+ this->start = consumer_start;
+ this->is_stopped = consumer_is_stopped;
+
+ // Return the consumer produced
+ return this;
+ }
+
+ // malloc or consumer init failed
+ free( this );
+
+ // Indicate failure
+ return NULL;
+}
+
+static int consumer_start( mlt_consumer this )
+{
+ // Get the producer service
+ mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
+
+ // Get the properties object
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+ // Get all the properties now
+ char *server = mlt_properties_get( properties, "server" );
+ int port = mlt_properties_get_int( properties, "port" );
+ char *cmd = mlt_properties_get( properties, "command" );
+ int unit = mlt_properties_get_int( properties, "unit" );
+ char *title = mlt_properties_get( properties, "title" );
+ char command[ 2048 ];
+
+ // If this is a reuse, then a valerie object will exist
+ valerie connection = mlt_properties_get_data( properties, "connection", NULL );
+
+ // Special case - we can get a doc too...
+ char *doc = mlt_properties_get( properties, "westley" );
+
+ // Set the title if provided
+ if ( service != NULL )
+ {
+ if ( title != NULL )
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", title );
+ else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
+ title = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" );
+ }
+
+ strcpy( command, cmd == NULL ? "" : cmd );
+ if ( strstr( command, "title=" ) == NULL && title != NULL )
+ {
+ strcat( command, " title=\"" );
+ strcat( command, title );
+ strcat( command, "\"" );
+ }
+
+ if ( service != NULL || doc != NULL )
+ {
+ // Initiate the connection if required
+ if ( connection == NULL )
+ {
+ valerie_parser parser = valerie_parser_init_remote( server, port );
+ connection = valerie_init( parser );
+ if ( valerie_connect( connection ) == valerie_ok )
+ {
+ mlt_properties_set_data( properties, "connection", connection, 0, ( mlt_destructor )valerie_close, NULL );
+ mlt_properties_set_data( properties, "parser", parser, 0, ( mlt_destructor )valerie_parser_close, NULL );
+ }
+ else
+ {
+ fprintf( stderr, "Unable to connect to the server at %s:%d\n", server, port );
+ mlt_properties_set_int( properties, "_error", 1 );
+ valerie_close( connection );
+ valerie_parser_close( parser );
+ connection = NULL;
+ }
+ }
+
+ // If we have connection, push the service over
+ if ( connection != NULL )
+ {
+ if ( doc == NULL )
+ {
+ int error;
+
+ // Push the service
+ error = valerie_unit_push( connection, unit, command, service );
+
+ // Report error
+ if ( error != valerie_ok )
+ fprintf( stderr, "Push failed on %s:%d %s u%d (%d)\n", server, port, command, unit, error );
+ }
+ else
+ {
+ // Push the service
+ int error = valerie_unit_receive( connection, unit, command, doc );
+
+ // Report error
+ if ( error != valerie_ok )
+ fprintf( stderr, "Send failed on %s:%d %s u%d (%d)\n", server, port, command, unit, error );
+ }
+ }
+ }
+
+ mlt_consumer_stop( this );
+ mlt_consumer_stopped( this );
+
+ return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+ return 1;
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_valerie_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( consumer_type, "valerie", consumer_valerie_init );
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltvmfx$(LIBSUF)
+
+OBJS = factory.o \
+ filter_chroma.o \
+ filter_chroma_hold.o \
+ filter_mono.o \
+ filter_shape.o \
+ producer_pgm.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@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 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_chroma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_chroma_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_shape_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_pgm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "chroma", filter_chroma_init );
+ MLT_REGISTER( filter_type, "chroma_hold", filter_chroma_hold_init );
+ MLT_REGISTER( filter_type, "threshold", filter_mono_init );
+ MLT_REGISTER( filter_type, "shape", filter_shape_init );
+ MLT_REGISTER( producer_type, "pgm", producer_pgm_init );
+}
--- /dev/null
+/*
+ * filter_chroma.c -- Maps a chroma key to the alpha channel
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <stdlib.h>
+#include <math.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+static inline int in_range( uint8_t v, uint8_t c, int var )
+{
+ return ( ( int )v >= c - var ) && ( ( int )v <= c + var );
+}
+
+static inline uint8_t alpha_value( uint8_t a, uint8_t *p, uint8_t u, uint8_t v, int var, int odd )
+{
+ if ( odd == 0 )
+ return ( in_range( *( p + 1 ), u, var ) && in_range( *( p + 3 ), v, var ) ) ? 0 : a;
+ else
+ return ( in_range( ( *( p + 1 ) + *( p + 5 ) ) / 2, u, var ) && in_range( ( *( p + 3 ) + *( p + 7 ) ) / 2, v, var ) ) ? 0 : a;
+}
+
+/** Get the images and map the chroma to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_filter this = mlt_frame_pop_service( frame );
+ int variance = 200 * mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "variance" );
+ int32_t key_val = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "key" );
+ uint8_t r = ( key_val >> 24 ) & 0xff;
+ uint8_t g = ( key_val >> 16 ) & 0xff;
+ uint8_t b = ( key_val >> 8 ) & 0xff;
+ uint8_t y, u, v;
+
+ RGB2YUV( r, g, b, y, u, v );
+
+ if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 )
+ {
+ uint8_t *alpha = mlt_frame_get_alpha_mask( frame );
+ uint8_t *p = *image;
+ int size = *width * *height / 2;
+ while ( size -- )
+ {
+ *alpha = alpha_value( *alpha, p, u, v, variance, 0 );
+ alpha ++;
+ *alpha = alpha_value( *alpha, p, u, v, variance, 1 );
+ alpha ++;
+ p += 4;
+ }
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_service( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_chroma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "key", arg == NULL ? "0x0000ff00" : arg );
+ mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "variance", 0.15 );
+ this->process = filter_process;
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_chroma.c -- Maps a chroma key to the alpha channel
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <stdlib.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+static inline int in_range( uint8_t v, uint8_t c, int var )
+{
+ return ( ( int )v >= c - var ) && ( ( int )v <= c + var );
+}
+
+static inline uint8_t alpha_value( uint8_t a, uint8_t *p, uint8_t u, uint8_t v, int var, int odd )
+{
+ if ( odd == 0 )
+ return ( in_range( *( p + 1 ), u, var ) && in_range( *( p + 3 ), v, var ) ) ? 0 : a;
+ else
+ return ( in_range( ( *( p + 1 ) + *( p + 5 ) ) / 2, u, var ) && in_range( ( *( p + 3 ) + *( p + 7 ) ) / 2, v, var ) ) ? 0 : a;
+}
+
+/** Get the images and map the chroma to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_filter this = mlt_frame_pop_service( frame );
+ int variance = 200 * mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "variance" );
+ int32_t key_val = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "key" );
+ uint8_t r = ( key_val >> 24 ) & 0xff;
+ uint8_t g = ( key_val >> 16 ) & 0xff;
+ uint8_t b = ( key_val >> 8 ) & 0xff;
+ uint8_t y, u, v;
+
+ RGB2YUV( r, g, b, y, u, v );
+
+ if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 )
+ {
+ uint8_t alpha = 0;
+ uint8_t *p = *image;
+ int size = *width * *height / 2;
+ while ( size -- )
+ {
+ alpha = alpha_value( 255, p, u, v, variance, 0 );
+ if ( alpha )
+ *( p + 1 )= 128;
+ alpha = alpha_value( 255, p, u, v, variance, 1 );
+ if ( alpha )
+ *( p + 3 ) = 128;
+ p += 4;
+ }
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_service( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_chroma_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "key", arg == NULL ? "0xc0000000" : arg );
+ mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "variance", 0.15 );
+ this->process = filter_process;
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_mono.c -- Arbitrary alpha channel shaping
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include <string.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+/** Get the images and apply the luminance of the mask to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int use_alpha = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) );
+ int midpoint = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) );
+ int invert = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) );
+
+ // Render the frame
+ if ( mlt_frame_get_image( this, image, format, width, height, writable ) == 0 )
+ {
+ uint8_t *p = *image;
+ uint8_t A = invert? 235 : 16;
+ uint8_t B = invert? 16 : 235;
+ int size = *width * *height;
+
+ if ( !use_alpha )
+ {
+ while( size -- )
+ {
+ if ( *p < midpoint )
+ *p ++ = A;
+ else
+ *p ++ = B;
+ *p ++ = 128;
+ }
+ }
+ else
+ {
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+ while( size -- )
+ {
+ if ( *alpha ++ < midpoint )
+ *p ++ = A;
+ else
+ *p ++ = B;
+ *p ++ = 128;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ int midpoint = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "midpoint" );
+ int use_alpha = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "use_alpha" );
+ int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "invert" );
+ mlt_deque_push_back_int( MLT_FRAME_IMAGE_STACK( frame ), invert );
+ mlt_deque_push_back_int( MLT_FRAME_IMAGE_STACK( frame ), midpoint );
+ mlt_deque_push_back_int( MLT_FRAME_IMAGE_STACK( frame ), use_alpha );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "midpoint", 128 );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "use_alpha", 0 );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "invert", 0 );
+ this->process = filter_process;
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_shape.c -- Arbitrary alpha channel shaping
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 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.
+ */
+
+#include <framework/mlt.h>
+#include <string.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+inline double smoothstep( const double e1, const double e2, const double a )
+{
+ if ( a < e1 ) return 0.0;
+ if ( a > e2 ) return 1.0;
+ double v = ( a - e1 ) / ( e2 - e1 );
+ return ( v * v * ( 3 - 2 * v ) );
+}
+
+/** Get the images and apply the luminance of the mask to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Fetch the data from the stack (mix, mask, filter)
+ double mix = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( this ) );
+ mlt_frame mask = mlt_frame_pop_service( this );
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Obtain the constants
+ double softness = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "softness" );
+ int use_luminance = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "use_luminance" );
+ int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" ) * 255;
+
+ // Render the frame
+ if ( mlt_frame_get_image( this, image, format, width, height, writable ) == 0 && ( !use_luminance || ( int )mix != 1 ) )
+ {
+ // Get the alpha mask of the source
+ uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+
+ // Obtain a scaled/distorted mask to match
+ uint8_t *mask_img = NULL;
+ mlt_image_format mask_fmt = mlt_image_yuv422;
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( mask ), "distort", 1 );
+ mlt_properties_pass_list( MLT_FRAME_PROPERTIES( mask ), MLT_FRAME_PROPERTIES( this ), "deinterlace,deinterlace_method,rescale.interp" );
+
+ if ( mlt_frame_get_image( mask, &mask_img, &mask_fmt, width, height, 0 ) == 0 )
+ {
+ int size = *width * *height;
+ uint8_t *p = alpha;
+ double a = 0;
+ double b = 0;
+ if ( !use_luminance )
+ {
+ uint8_t *q = mlt_frame_get_alpha_mask( mask );
+ while( size -- )
+ {
+ a = ( double )*q ++ / 255.0;
+ b = 1.0 - smoothstep( a, a + softness, mix );
+ *p = ( uint8_t )( *p * b ) ^ invert;
+ p ++;
+ }
+ }
+ else if ( ( int )mix != 1 )
+ {
+ uint8_t *q = mask_img;
+ // Ensure softness tends to zero has mix tends to 1
+ softness *= ( 1.0 - mix );
+ while( size -- )
+ {
+ a = ( ( double )*q - 16 ) / 235.0;
+ b = smoothstep( a, a + softness, mix );
+ *p = ( uint8_t )( *p * b ) ^ invert;
+ p ++;
+ q += 2;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Obtain the shape instance
+ char *resource = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "resource" );
+ char *last_resource = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_resource" );
+ mlt_producer producer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "instance", NULL );
+
+ // Get the key framed values
+ mlt_geometry alpha = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "_alpha", NULL );
+ char *alpha_data = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "mix" );
+ double alpha_mix = 0.0;
+
+ // Calculate the position and length
+ int position = mlt_frame_get_position( frame ) - mlt_filter_get_in( this );
+ int in = mlt_filter_get_in( this );
+ int out = mlt_filter_get_out( this );
+ int length;
+
+ // Special case for attached filters - in/out come through on the frame
+ if ( out == 0 )
+ {
+ in = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "in" );
+ out = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "out" );
+ position -= in;
+ }
+
+ // Duration of the shape
+ length = out - in + 1;
+
+ // If we haven't created the instance or it's changed
+ if ( producer == NULL || strcmp( resource, last_resource ) )
+ {
+ char temp[ 512 ];
+ char *extension = strrchr( resource, '.' );
+
+ // Store the last resource now
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "_resource", resource );
+
+ // This is a hack - the idea is that we can indirectly reference the
+ // luma modules pgm or png images by a short cut like %luma01.pgm - we then replace
+ // the % with the full path to the image and use it if it exists, if not, check for
+ // the file ending in a .png, and failing that, default to a fade in
+ if ( strchr( resource, '%' ) )
+ {
+ FILE *test;
+ sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 );
+ test = fopen( temp, "r" );
+
+ if ( test == NULL )
+ {
+ strcat( temp, ".png" );
+ test = fopen( temp, "r" );
+ }
+
+ if ( test )
+ fclose( test );
+ else
+ strcpy( temp, "colour:0x00000080" );
+
+ resource = temp;
+ extension = strrchr( resource, '.' );
+ }
+
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+ producer = mlt_factory_producer( profile, NULL, resource );
+ if ( producer != NULL )
+ mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "instance", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ }
+
+ // Construct the geometry item if needed, otherwise refresh it
+ if ( alpha == NULL )
+ {
+ alpha = mlt_geometry_init( );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "_alpha", alpha, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+ mlt_geometry_parse( alpha, alpha_data, length, 100, 100 );
+ }
+ else
+ {
+ mlt_geometry_refresh( alpha, alpha_data, length, 100, 100 );
+ }
+
+ // We may still not have a producer in which case, we do nothing
+ if ( producer != NULL )
+ {
+ mlt_frame mask = NULL;
+ struct mlt_geometry_item_s item;
+ mlt_geometry_fetch( alpha, &item, position );
+ alpha_mix = item.x;
+ mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), MLT_FILTER_PROPERTIES( this ), "producer." );
+ mlt_producer_seek( producer, position );
+ if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &mask, 0 ) == 0 )
+ {
+ char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" );
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), name, mask, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ mlt_frame_push_service( frame, this );
+ mlt_frame_push_service( frame, mask );
+ mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( frame ), alpha_mix / 100.0 );
+ mlt_frame_push_get_image( frame, filter_get_image );
+ if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "audio_match" ) )
+ {
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "meta.mixdown", 1 );
+ mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "meta.volume", alpha_mix / 100.0 );
+ }
+ }
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_shape_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "resource", arg );
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "mix", "100" );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "audio_match", 1 );
+ mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "invert", 0 );
+ mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "softness", 0.1 );
+ this->process = filter_process;
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * producer_pgm.c -- PGM producer
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@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 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.
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int read_pgm( char *name, uint8_t **image, int *width, int *height, int *maxval );
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_pgm_init( mlt_profile profile, mlt_service_type type, const char *id, char *resource )
+{
+ mlt_producer this = NULL;
+ uint8_t *image = NULL;
+ int width = 0;
+ int height = 0;
+ int maxval = 0;
+
+ if ( read_pgm( resource, &image, &width, &height, &maxval ) == 0 )
+ {
+ this = calloc( 1, sizeof( struct mlt_producer_s ) );
+ if ( this != NULL && mlt_producer_init( this, NULL ) == 0 )
+ {
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+ this->get_frame = producer_get_frame;
+ this->close = ( mlt_destructor )producer_close;
+ mlt_properties_set( properties, "resource", resource );
+ mlt_properties_set_data( properties, "image", image, 0, mlt_pool_release, NULL );
+ mlt_properties_set_int( properties, "real_width", width );
+ mlt_properties_set_int( properties, "real_height", height );
+ }
+ else
+ {
+ mlt_pool_release( image );
+ free( this );
+ this = NULL;
+ }
+ }
+
+ return this;
+}
+
+/** Load the PGM file.
+*/
+
+static int read_pgm( char *name, uint8_t **image, int *width, int *height, int *maxval )
+{
+ uint8_t *input = NULL;
+ int error = 0;
+ FILE *f = fopen( name, "r" );
+ char data[ 512 ];
+
+ // Initialise
+ *image = NULL;
+ *width = 0;
+ *height = 0;
+ *maxval = 0;
+
+ // Get the magic code
+ if ( f != NULL && fgets( data, 511, f ) != NULL && data[ 0 ] == 'P' && data[ 1 ] == '5' )
+ {
+ char *p = data + 2;
+ int i = 0;
+ int val = 0;
+
+ // PGM Header parser (probably needs to be strengthened)
+ for ( i = 0; !error && i < 3; i ++ )
+ {
+ if ( *p != '\0' && *p != '\n' )
+ val = strtol( p, &p, 10 );
+ else
+ p = NULL;
+
+ while ( error == 0 && p == NULL )
+ {
+ if ( fgets( data, 511, f ) == NULL )
+ error = 1;
+ else if ( data[ 0 ] != '#' )
+ val = strtol( data, &p, 10 );
+ }
+
+ switch( i )
+ {
+ case 0: *width = val; break;
+ case 1: *height = val; break;
+ case 2: *maxval = val; break;
+ }
+ }
+
+ if ( !error )
+ {
+ // Determine if this is one or two bytes per pixel
+ int bpp = *maxval > 255 ? 2 : 1;
+ int size = *width * *height * bpp;
+ uint8_t *p;
+
+ // Allocate temporary storage for the data and the image
+ input = mlt_pool_alloc( *width * *height * bpp );
+ *image = mlt_pool_alloc( *width * *height * sizeof( uint8_t ) * 2 );
+ p = *image;
+
+ error = *image == NULL || input == NULL;
+
+ if ( !error )
+ {
+ // Read the raw data
+ error = fread( input, *width * *height * bpp, 1, f ) != 1;
+
+ if ( !error )
+ {
+ // Convert to yuv422 (very lossy - need to extend this to allow 16 bit alpha out)
+ for ( i = 0; i < size; i += bpp )
+ {
+ *p ++ = 16 + ( input[ i ] * 219 ) / 255;
+ *p ++ = 128;
+ }
+ }
+ }
+ }
+
+ if ( error )
+ mlt_pool_release( *image );
+ mlt_pool_release( input );
+ }
+ else
+ {
+ error = 1;
+ }
+
+ if ( f != NULL )
+ fclose( f );
+
+ return error;
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+ mlt_producer producer = mlt_frame_pop_service( this );
+ int real_width = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "real_width" );
+ int real_height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "real_height" );
+ int size = real_width * real_height;
+ uint8_t *image = mlt_pool_alloc( size * 2 );
+ uint8_t *source = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( producer ), "image", NULL );
+
+ mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", image, size * 2, mlt_pool_release, NULL );
+
+ *width = real_width;
+ *height = real_height;
+ *format = mlt_image_yuv422;
+ *buffer = image;
+
+ if ( image != NULL && source != NULL )
+ memcpy( image, source, size * 2 );
+
+ return 0;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+ // Construct a test frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+ // Get the frames properties
+ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+ // Pass the data on the frame properties
+ mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( producer ), "real_width,real_height" );
+ mlt_properties_set_int( properties, "has_image", 1 );
+ mlt_properties_set_int( properties, "progressive", 1 );
+ mlt_properties_set_double( properties, "aspect_ratio", 1 );
+
+ // Push the image callback
+ mlt_frame_push_service( *frame, producer );
+ mlt_frame_push_get_image( *frame, producer_get_image );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( producer );
+
+ return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+ parent->close = NULL;
+ mlt_producer_close( parent );
+ free( parent );
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltvorbis$(LIBSUF)
+
+OBJS = factory.o \
+ producer_vorbis.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags vorbis`
+CFLAGS += `pkg-config --cflags vorbisfile`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs vorbis`
+LDFLAGS += `pkg-config --libs vorbisfile`
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ pkg-config vorbisfile 2> /dev/null
+ disable_vorbis=$?
+
+ if [ "$disable_vorbis" != "0" ]
+ then
+ echo "- ogg vorbis not found: disabling"
+ touch ../disable-vorbis
+ fi
+ exit 0
+fi
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_vorbis_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( producer_type, "vorbis", producer_vorbis_init );
+}
--- /dev/null
+/*
+ * producer_vorbis.c -- vorbis producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// MLT Header files
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+
+// vorbis Header files
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+// System header files
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+// Forward references.
+static int producer_open( mlt_producer this, mlt_profile profile, char *file );
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+
+/** Structure for metadata reading
+*/
+
+typedef struct _sw_metadata sw_metadata;
+
+struct _sw_metadata {
+ char * name;
+ char * content;
+};
+
+static sw_metadata *vorbis_metadata_from_str (char * str)
+{
+ sw_metadata * meta = NULL;
+ int i;
+
+ for (i = 0; str[i]; i++) {
+ str[i] = tolower(str[i]);
+ if (str[i] == '=') {
+ str[i] = '\0';
+ meta = malloc (sizeof (sw_metadata));
+ meta->name = malloc( strlen(str) + 18 );
+ sprintf(meta->name, "meta.attr.%s.markup", str);
+ meta->content = strdup (&str[i+1]);
+ break;
+ }
+ }
+ return meta;
+}
+
+/** Constructor for libvorbis.
+*/
+
+mlt_producer producer_vorbis_init( mlt_profile profile, mlt_service_type type, const char *id, char *file )
+{
+ mlt_producer this = NULL;
+
+ // Check that we have a non-NULL argument
+ if ( file != NULL )
+ {
+ // Construct the producer
+ this = calloc( 1, sizeof( struct mlt_producer_s ) );
+
+ // Initialise it
+ if ( mlt_producer_init( this, NULL ) == 0 )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Set the resource property (required for all producers)
+ mlt_properties_set( properties, "resource", file );
+
+ // Register our get_frame implementation
+ this->get_frame = producer_get_frame;
+
+ // Open the file
+ if ( producer_open( this, profile, file ) != 0 )
+ {
+ // Clean up
+ mlt_producer_close( this );
+ this = NULL;
+ }
+ }
+ }
+
+ return this;
+}
+
+/** Destuctor for ogg files.
+*/
+
+static void producer_file_close( void *file )
+{
+ if ( file != NULL )
+ {
+ // Close the ogg vorbis structure
+ ov_clear( file );
+
+ // Free the memory
+ free( file );
+ }
+}
+
+/** Open the file.
+*/
+
+static int producer_open( mlt_producer this, mlt_profile profile, char *file )
+{
+ // FILE pointer for file
+ FILE *input = fopen( file, "r" );
+
+ // Error code to return
+ int error = input == NULL;
+
+ // Continue if file is open
+ if ( error == 0 )
+ {
+ // OggVorbis file structure
+ OggVorbis_File *ov = calloc( 1, sizeof( OggVorbis_File ) );
+
+ // Attempt to open the stream
+ error = ov == NULL || ov_open( input, ov, NULL, 0 ) != 0;
+
+ // Assign to producer properties if successful
+ if ( error == 0 )
+ {
+ // Get the properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Assign the ov structure
+ mlt_properties_set_data( properties, "ogg_vorbis_file", ov, 0, producer_file_close, NULL );
+
+ // Read metadata
+ sw_metadata * metadata = NULL;
+ char **ptr = ov_comment(ov, -1)->user_comments;
+ while(*ptr) {
+ metadata = vorbis_metadata_from_str (*ptr);
+ if (metadata != NULL)
+ mlt_properties_set(properties, metadata->name, metadata->content);
+ ++ptr;
+ }
+
+ if ( ov_seekable( ov ) )
+ {
+ // Get the length of the file
+ double length = ov_time_total( ov, -1 );
+
+ // We will treat everything with the producer fps
+ double fps = mlt_profile_fps( profile );
+
+ // Set out and length of file
+ mlt_properties_set_position( properties, "out", ( length * fps ) - 1 );
+ mlt_properties_set_position( properties, "length", ( length * fps ) );
+
+ // Get the vorbis info
+ vorbis_info *vi = ov_info( ov, -1 );
+ mlt_properties_set_int( properties, "frequency", (int) vi->rate );
+ mlt_properties_set_int( properties, "channels", vi->channels );
+
+ // Set some media metadata
+ mlt_properties_set_int( properties, "meta.media.nb_streams", 1 );
+ mlt_properties_set_int( properties, "audio_index", 0 );
+ mlt_properties_set( properties, "meta.media.0.stream.type", "audio" );
+ mlt_properties_set( properties, "meta.media.0.codec.name", "vorbis" );
+ mlt_properties_set( properties, "meta.media.0.codec.long_name", "Vorbis" );
+ }
+ }
+ else
+ {
+ // Clean up
+ free( ov );
+
+ // Must close input file when open fails
+ fclose( input );
+ }
+ }
+
+ return error;
+}
+
+/** Convert a frame position to a time code.
+*/
+
+static double producer_time_of_frame( mlt_producer this, mlt_position position )
+{
+ return ( double )position / mlt_producer_get_fps( this );
+}
+
+/** Get the audio from a frame.
+*/
+
+static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ // Get the properties from the frame
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+ // Obtain the frame number of this frame
+ mlt_position position = mlt_properties_get_position( frame_properties, "vorbis_position" );
+
+ // Get the producer
+ mlt_producer this = mlt_frame_pop_audio( frame );
+
+ // Get the producer properties
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+ // Get the ogg vorbis file
+ OggVorbis_File *ov = mlt_properties_get_data( properties, "ogg_vorbis_file", NULL );
+
+ // Obtain the expected frame numer
+ mlt_position expected = mlt_properties_get_position( properties, "audio_expected" );
+
+ // Get the fps for this producer
+ double fps = mlt_producer_get_fps( this );
+
+ // Get the vorbis info
+ vorbis_info *vi = ov_info( ov, -1 );
+
+ // Obtain the audio buffer
+ int16_t *audio_buffer = mlt_properties_get_data( properties, "audio_buffer", NULL );
+
+ // Get amount of audio used
+ int audio_used = mlt_properties_get_int( properties, "audio_used" );
+
+ // Number of frames to ignore (for ffwd)
+ int ignore = 0;
+
+ // Flag for paused (silence)
+ int paused = 0;
+
+ // Check for audio buffer and create if necessary
+ if ( audio_buffer == NULL )
+ {
+ // Allocate the audio buffer
+ audio_buffer = mlt_pool_alloc( 131072 * sizeof( int16_t ) );
+
+ // And store it on properties for reuse
+ mlt_properties_set_data( properties, "audio_buffer", audio_buffer, 0, mlt_pool_release, NULL );
+ }
+
+ // Seek if necessary
+ if ( position != expected )
+ {
+ if ( position + 1 == expected )
+ {
+ // We're paused - silence required
+ paused = 1;
+ }
+ else if ( position > expected && ( position - expected ) < 250 )
+ {
+ // Fast forward - seeking is inefficient for small distances - just ignore following frames
+ ignore = position - expected;
+ }
+ else
+ {
+ // Seek to the required position
+ ov_time_seek( ov, producer_time_of_frame( this, position ) );
+ expected = position;
+ audio_used = 0;
+ }
+ }
+
+ // Return info in frame
+ *frequency = vi->rate;
+ *channels = vi->channels;
+
+ // Get the audio if required
+ if ( !paused )
+ {
+ // Bitstream section
+ int current_section;
+
+ // Get the number of samples for the current frame
+ *samples = mlt_sample_calculator( fps, *frequency, expected ++ );
+
+ while( *samples > audio_used )
+ {
+ // Read the samples
+ int bytes = ov_read( ov, ( char * )( &audio_buffer[ audio_used * 2 ] ), 4096, 0, 2, 1, ¤t_section );
+
+ // Break if error or eof
+ if ( bytes <= 0 )
+ break;
+
+ // Increment number of samples used
+ audio_used += bytes / ( sizeof( int16_t ) * *channels );
+
+ // Handle ignore
+ while ( ignore && audio_used >= *samples )
+ {
+ ignore --;
+ audio_used -= *samples;
+ memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * sizeof( int16_t ) );
+ *samples = mlt_sample_calculator( fps, *frequency, expected ++ );
+ }
+ }
+
+ // Now handle the audio if we have enough
+ if ( audio_used >= *samples )
+ {
+ *buffer = mlt_pool_alloc( *samples * *channels * sizeof( int16_t ) );
+ memcpy( *buffer, audio_buffer, *samples * *channels * sizeof( int16_t ) );
+ audio_used -= *samples;
+ memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * *channels * sizeof( int16_t ) );
+ mlt_properties_set_data( frame_properties, "audio", *buffer, 0, mlt_pool_release, NULL );
+ }
+ else
+ {
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+ audio_used = 0;
+ }
+
+ // Store the number of audio samples still available
+ mlt_properties_set_int( properties, "audio_used", audio_used );
+ }
+ else
+ {
+ // Get silence and don't touch the context
+ *samples = mlt_sample_calculator( fps, *frequency, position );
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+ }
+
+ // Regardless of speed, we expect to get the next frame (cos we ain't too bright)
+ mlt_properties_set_position( properties, "audio_expected", position + 1 );
+
+ return 0;
+}
+
+/** Our get frame implementation.
+*/
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+ // Create an empty frame
+ *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+ // Update timecode on the frame we're creating
+ mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+ // Set the position of this producer
+ mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );
+ mlt_properties_set_position( frame_properties, "vorbis_position", mlt_producer_frame( this ) );
+
+ // Set up the audio
+ mlt_frame_push_audio( *frame, this );
+ mlt_frame_push_audio( *frame, producer_get_audio );
+
+ // Pass audio properties to the frame
+ mlt_properties_pass_list( frame_properties, MLT_PRODUCER_PROPERTIES( this ), "frequency, channels" );
+
+ // Calculate the next timecode
+ mlt_producer_prepare_next( this );
+
+ return 0;
+}
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltwestley$(LIBSUF)
+
+OBJS = factory.o \
+ consumer_westley.o \
+ producer_westley.o
+
+CFLAGS += -I../..
+CFLAGS += `xml2-config --cflags`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `xml2-config --libs`
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+ install -m 644 westley.dtd "$(DESTDIR)$(prefix)/share/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+ which xml2-config > /dev/null 2>&1
+ disable_xml2=$?
+
+ if [ "$disable_xml2" != "0" ]
+ then
+ echo "- xml2 not found: disabling westley modules"
+ touch ../disable-westley
+ fi
+ exit 0
+fi
+
--- /dev/null
+/*
+ * consumer_westley.c -- a libxml2 serialiser of mlt service networks
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <libxml/tree.h>
+
+#define ID_SIZE 128
+
+#define _x (const xmlChar*)
+#define _s (const char*)
+
+// This maintains counters for adding ids to elements
+struct serialise_context_s
+{
+ mlt_properties id_map;
+ int producer_count;
+ int multitrack_count;
+ int playlist_count;
+ int tractor_count;
+ int filter_count;
+ int transition_count;
+ int pass;
+ mlt_properties hide_map;
+ char *root;
+ char *store;
+};
+typedef struct serialise_context_s* serialise_context;
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer this );
+static void serialise_service( serialise_context context, mlt_service service, xmlNode *node );
+
+typedef enum
+{
+ westley_existing,
+ westley_producer,
+ westley_multitrack,
+ westley_playlist,
+ westley_tractor,
+ westley_filter,
+ westley_transition
+}
+westley_type;
+
+/** Create or retrieve an id associated to this service.
+*/
+
+static char *westley_get_id( serialise_context context, mlt_service service, westley_type type )
+{
+ char *id = NULL;
+ int i = 0;
+ mlt_properties map = context->id_map;
+
+ // Search the map for the service
+ for ( i = 0; i < mlt_properties_count( map ); i ++ )
+ if ( mlt_properties_get_data_at( map, i, NULL ) == service )
+ break;
+
+ // If the service is not in the map, and the type indicates a new id is needed...
+ if ( i >= mlt_properties_count( map ) && type != westley_existing )
+ {
+ // Attempt to reuse existing id
+ id = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "id" );
+
+ // If no id, or the id is used in the map (for another service), then
+ // create a new one.
+ if ( id == NULL || mlt_properties_get_data( map, id, NULL ) != NULL )
+ {
+ char temp[ ID_SIZE ];
+ do
+ {
+ switch( type )
+ {
+ case westley_producer:
+ sprintf( temp, "producer%d", context->producer_count ++ );
+ break;
+ case westley_multitrack:
+ sprintf( temp, "multitrack%d", context->multitrack_count ++ );
+ break;
+ case westley_playlist:
+ sprintf( temp, "playlist%d", context->playlist_count ++ );
+ break;
+ case westley_tractor:
+ sprintf( temp, "tractor%d", context->tractor_count ++ );
+ break;
+ case westley_filter:
+ sprintf( temp, "filter%d", context->filter_count ++ );
+ break;
+ case westley_transition:
+ sprintf( temp, "transition%d", context->transition_count ++ );
+ break;
+ case westley_existing:
+ // Never gets here
+ break;
+ }
+ }
+ while( mlt_properties_get_data( map, temp, NULL ) != NULL );
+
+ // Set the data at the generated name
+ mlt_properties_set_data( map, temp, service, 0, NULL, NULL );
+
+ // Get the pointer to the name (i is the end of the list)
+ id = mlt_properties_get_name( map, i );
+ }
+ else
+ {
+ // Store the existing id in the map
+ mlt_properties_set_data( map, id, service, 0, NULL, NULL );
+ }
+ }
+ else if ( type == westley_existing )
+ {
+ id = mlt_properties_get_name( map, i );
+ }
+
+ return id;
+}
+
+/** This is what will be called by the factory - anything can be passed in
+ via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_westley_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ // Create the consumer object
+ mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
+
+ // If no malloc'd and consumer init ok
+ if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
+ {
+ // Allow thread to be started/stopped
+ this->start = consumer_start;
+ this->is_stopped = consumer_is_stopped;
+
+ mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "resource", arg );
+
+ // Return the consumer produced
+ return this;
+ }
+
+ // malloc or consumer init failed
+ free( this );
+
+ // Indicate failure
+ return NULL;
+}
+
+static void serialise_properties( serialise_context context, mlt_properties properties, xmlNode *node )
+{
+ int i;
+ xmlNode *p;
+
+ // Enumerate the properties
+ for ( i = 0; i < mlt_properties_count( properties ); i++ )
+ {
+ char *name = mlt_properties_get_name( properties, i );
+ if ( name != NULL &&
+ name[ 0 ] != '_' &&
+ mlt_properties_get_value( properties, i ) != NULL &&
+ strcmp( name, "westley" ) != 0 &&
+ strcmp( name, "in" ) != 0 &&
+ strcmp( name, "out" ) != 0 &&
+ strcmp( name, "id" ) != 0 &&
+ strcmp( name, "title" ) != 0 &&
+ strcmp( name, "root" ) != 0 &&
+ strcmp( name, "width" ) != 0 &&
+ strcmp( name, "height" ) != 0 )
+ {
+ char *value = mlt_properties_get_value( properties, i );
+ if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
+ value += strlen( context->root ) + 1;
+ p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
+ xmlNewProp( p, _x("name"), _x(name) );
+ }
+ }
+}
+
+static void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node, const char *store )
+{
+ int i;
+ xmlNode *p;
+
+ // Enumerate the properties
+ for ( i = 0; store != NULL && i < mlt_properties_count( properties ); i++ )
+ {
+ char *name = mlt_properties_get_name( properties, i );
+ if ( !strncmp( name, store, strlen( store ) ) )
+ {
+ char *value = mlt_properties_get_value( properties, i );
+ if ( value != NULL )
+ {
+ if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
+ value += strlen( context->root ) + 1;
+ p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
+ xmlNewProp( p, _x("name"), _x(name) );
+ }
+ }
+ }
+}
+
+static inline void serialise_service_filters( serialise_context context, mlt_service service, xmlNode *node )
+{
+ int i;
+ xmlNode *p;
+ mlt_filter filter = NULL;
+
+ // Enumerate the filters
+ for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ if ( mlt_properties_get_int( properties, "_fezzik" ) == 0 )
+ {
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, MLT_FILTER_SERVICE( filter ), westley_filter );
+ if ( id != NULL )
+ {
+ int in = mlt_properties_get_position( properties, "in" );
+ int out = mlt_properties_get_position( properties, "out" );
+ p = xmlNewChild( node, NULL, _x("filter"), NULL );
+ xmlNewProp( p, _x("id"), _x(id) );
+ if ( mlt_properties_get( properties, "title" ) )
+ xmlNewProp( p, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+ if ( in != 0 || out != 0 )
+ {
+ char temp[ 20 ];
+ sprintf( temp, "%d", in );
+ xmlNewProp( p, _x("in"), _x(temp) );
+ sprintf( temp, "%d", out );
+ xmlNewProp( p, _x("out"), _x(temp) );
+ }
+ serialise_properties( context, properties, p );
+ serialise_service_filters( context, MLT_FILTER_SERVICE( filter ), p );
+ }
+ }
+ }
+}
+
+static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
+{
+ xmlNode *child = node;
+ mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( MLT_PRODUCER( service ) ) );
+
+ if ( context->pass == 0 )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, parent, westley_producer );
+ if ( id == NULL )
+ return;
+
+ child = xmlNewChild( node, NULL, _x("producer"), NULL );
+
+ // Set the id
+ xmlNewProp( child, _x("id"), _x(id) );
+ if ( mlt_properties_get( properties, "title" ) )
+ xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+ xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+ xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+ serialise_properties( context, properties, child );
+ serialise_service_filters( context, service, child );
+
+ // Add producer to the map
+ mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
+ }
+ else
+ {
+ char *id = westley_get_id( context, parent, westley_existing );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ xmlNewProp( node, _x("parent"), _x(id) );
+ xmlNewProp( node, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+ xmlNewProp( node, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+ }
+}
+
+static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node );
+
+static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
+{
+ int i;
+
+ if ( context->pass == 0 )
+ {
+ // Iterate over the tracks to collect the producers
+ for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
+ {
+ mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) );
+ serialise_service( context, MLT_SERVICE( producer ), node );
+ }
+ }
+ else
+ {
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, service, westley_multitrack );
+ if ( id == NULL )
+ return;
+
+ // Serialise the tracks
+ for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
+ {
+ xmlNode *track = xmlNewChild( node, NULL, _x("track"), NULL );
+ int hide = 0;
+ mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( producer ) );
+
+ char *id = westley_get_id( context, MLT_SERVICE( parent ), westley_existing );
+ xmlNewProp( track, _x("producer"), _x(id) );
+ if ( mlt_producer_is_cut( producer ) )
+ {
+ xmlNewProp( track, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+ xmlNewProp( track, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+ serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store );
+ serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." );
+ serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track );
+ }
+
+ hide = mlt_properties_get_int( context->hide_map, id );
+ if ( hide )
+ xmlNewProp( track, _x("hide"), _x( hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) ) );
+ }
+ serialise_service_filters( context, service, node );
+ }
+}
+
+static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
+{
+ int i;
+ xmlNode *child = node;
+ mlt_playlist_clip_info info;
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( context->pass == 0 )
+ {
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, service, westley_playlist );
+ if ( id == NULL )
+ return;
+
+ // Iterate over the playlist entries to collect the producers
+ for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
+ {
+ if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
+ {
+ if ( info.producer != NULL )
+ {
+ mlt_producer producer = mlt_producer_cut_parent( info.producer );
+ char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
+ char *resource_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource" );
+ if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
+ serialise_playlist( context, MLT_SERVICE( producer ), node );
+ else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
+ serialise_service( context, MLT_SERVICE( producer ), node );
+ }
+ }
+ }
+
+ child = xmlNewChild( node, NULL, _x("playlist"), NULL );
+
+ // Set the id
+ xmlNewProp( child, _x("id"), _x(id) );
+ if ( mlt_properties_get( properties, "title" ) )
+ xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+
+ // Store application specific properties
+ serialise_store_properties( context, properties, child, context->store );
+ serialise_store_properties( context, properties, child, "meta." );
+
+ // Add producer to the map
+ mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
+
+ // Iterate over the playlist entries
+ for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
+ {
+ if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
+ {
+ mlt_producer producer = mlt_producer_cut_parent( info.producer );
+ char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
+ if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
+ {
+ char length[ 20 ];
+ length[ 19 ] = '\0';
+ xmlNode *entry = xmlNewChild( child, NULL, _x("blank"), NULL );
+ snprintf( length, 19, "%d", (int)info.frame_count );
+ xmlNewProp( entry, _x("length"), _x(length) );
+ }
+ else
+ {
+ char temp[ 20 ];
+ xmlNode *entry = xmlNewChild( child, NULL, _x("entry"), NULL );
+ id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
+ xmlNewProp( entry, _x("producer"), _x(id) );
+ sprintf( temp, "%d", (int)info.frame_in );
+ xmlNewProp( entry, _x("in"), _x(temp) );
+ sprintf( temp, "%d", (int)info.frame_out );
+ xmlNewProp( entry, _x("out"), _x(temp) );
+ if ( info.repeat > 1 )
+ {
+ sprintf( temp, "%d", info.repeat );
+ xmlNewProp( entry, _x("repeat"), _x(temp) );
+ }
+ if ( mlt_producer_is_cut( info.cut ) )
+ {
+ serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store );
+ serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." );
+ serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry );
+ }
+ }
+ }
+ }
+
+ serialise_service_filters( context, service, child );
+ }
+ else if ( xmlStrcmp( node->name, _x("tractor") ) != 0 )
+ {
+ char *id = westley_get_id( context, service, westley_existing );
+ xmlNewProp( node, _x("producer"), _x(id) );
+ }
+}
+
+static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
+{
+ xmlNode *child = node;
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( context->pass == 0 )
+ {
+ // Recurse on connected producer
+ serialise_service( context, mlt_service_producer( service ), node );
+ }
+ else
+ {
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, service, westley_tractor );
+ if ( id == NULL )
+ return;
+
+ child = xmlNewChild( node, NULL, _x("tractor"), NULL );
+
+ // Set the id
+ xmlNewProp( child, _x("id"), _x(id) );
+ if ( mlt_properties_get( properties, "title" ) )
+ xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+ if ( mlt_properties_get( properties, "global_feed" ) )
+ xmlNewProp( child, _x("global_feed"), _x(mlt_properties_get( properties, "global_feed" )) );
+ xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+ xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+
+ // Store application specific properties
+ serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store );
+ serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." );
+
+ // Recurse on connected producer
+ serialise_service( context, mlt_service_producer( service ), child );
+ serialise_service_filters( context, service, child );
+ }
+}
+
+static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
+{
+ xmlNode *child = node;
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ // Recurse on connected producer
+ serialise_service( context, mlt_service_producer( service ), node );
+
+ if ( context->pass == 1 )
+ {
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, service, westley_filter );
+ if ( id == NULL )
+ return;
+
+ child = xmlNewChild( node, NULL, _x("filter"), NULL );
+
+ // Set the id
+ xmlNewProp( child, _x("id"), _x(id) );
+ if ( mlt_properties_get( properties, "title" ) )
+ xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+ xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+ xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+
+ serialise_properties( context, properties, child );
+ serialise_service_filters( context, service, child );
+ }
+}
+
+static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
+{
+ xmlNode *child = node;
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ // Recurse on connected producer
+ serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
+
+ if ( context->pass == 1 )
+ {
+ // Get a new id - if already allocated, do nothing
+ char *id = westley_get_id( context, service, westley_transition );
+ if ( id == NULL )
+ return;
+
+ child = xmlNewChild( node, NULL, _x("transition"), NULL );
+
+ // Set the id
+ xmlNewProp( child, _x("id"), _x(id) );
+ if ( mlt_properties_get( properties, "title" ) )
+ xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+ xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+ xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+
+ serialise_properties( context, properties, child );
+ serialise_service_filters( context, service, child );
+ }
+}
+
+static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
+{
+ // Iterate over consumer/producer connections
+ while ( service != NULL )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ char *mlt_type = mlt_properties_get( properties, "mlt_type" );
+
+ // Tell about the producer
+ if ( strcmp( mlt_type, "producer" ) == 0 )
+ {
+ char *mlt_service = mlt_properties_get( properties, "mlt_service" );
+ if ( mlt_properties_get( properties, "westley" ) == NULL && ( mlt_service != NULL && !strcmp( mlt_service, "tractor" ) ) )
+ {
+ context->pass = 0;
+ serialise_tractor( context, service, node );
+ context->pass = 1;
+ serialise_tractor( context, service, node );
+ context->pass = 0;
+ break;
+ }
+ else
+ {
+ serialise_producer( context, service, node );
+ }
+ if ( mlt_properties_get( properties, "westley" ) != NULL )
+ break;
+ }
+
+ // Tell about the framework container producers
+ else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
+ {
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ // Recurse on multitrack's tracks
+ if ( strcmp( resource, "<multitrack>" ) == 0 )
+ {
+ serialise_multitrack( context, service, node );
+ break;
+ }
+
+ // Recurse on playlist's clips
+ else if ( strcmp( resource, "<playlist>" ) == 0 )
+ {
+ serialise_playlist( context, service, node );
+ }
+
+ // Recurse on tractor's producer
+ else if ( strcmp( resource, "<tractor>" ) == 0 )
+ {
+ context->pass = 0;
+ serialise_tractor( context, service, node );
+ context->pass = 1;
+ serialise_tractor( context, service, node );
+ context->pass = 0;
+ break;
+ }
+
+ // Treat it as a normal producer
+ else
+ {
+ serialise_producer( context, service, node );
+ }
+ }
+
+ // Tell about a filter
+ else if ( strcmp( mlt_type, "filter" ) == 0 )
+ {
+ serialise_filter( context, service, node );
+ break;
+ }
+
+ // Tell about a transition
+ else if ( strcmp( mlt_type, "transition" ) == 0 )
+ {
+ serialise_transition( context, service, node );
+ break;
+ }
+
+ // Get the next connected service
+ service = mlt_service_producer( service );
+ }
+}
+
+xmlDocPtr westley_make_doc( mlt_consumer consumer, mlt_service service )
+{
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ xmlDocPtr doc = xmlNewDoc( _x("1.0") );
+ xmlNodePtr root = xmlNewNode( NULL, _x("westley") );
+ struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
+
+ xmlDocSetRootElement( doc, root );
+
+ // If we have root, then deal with it now
+ if ( mlt_properties_get( properties, "root" ) != NULL )
+ {
+ xmlNewProp( root, _x("root"), _x(mlt_properties_get( properties, "root" )) );
+ context->root = strdup( mlt_properties_get( properties, "root" ) );
+ }
+ else
+ {
+ context->root = strdup( "" );
+ }
+
+ // Assign the additional 'storage' pattern for properties
+ context->store = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "store" );
+
+ // Assign a title property
+ if ( mlt_properties_get( properties, "title" ) != NULL )
+ xmlNewProp( root, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+ mlt_properties_set_int( properties, "global_feed", 1 );
+
+ // Construct the context maps
+ context->id_map = mlt_properties_new();
+ context->hide_map = mlt_properties_new();
+
+ // Ensure producer is a framework producer
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "mlt_type", "mlt_producer" );
+
+ // In pass one, we serialise the end producers and playlists,
+ // adding them to a map keyed by address.
+ serialise_service( context, service, root );
+
+ // In pass two, we serialise the tractor and reference the
+ // producers and playlists
+ context->pass++;
+ serialise_service( context, service, root );
+
+ // Cleanup resource
+ mlt_properties_close( context->id_map );
+ mlt_properties_close( context->hide_map );
+ free( context->root );
+ free( context );
+
+ return doc;
+}
+
+static int consumer_start( mlt_consumer this )
+{
+ xmlDocPtr doc = NULL;
+
+ // Get the producer service
+ mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
+ if ( service != NULL )
+ {
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ // Set the title if provided
+ if ( mlt_properties_get( properties, "title" ) )
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", mlt_properties_get( properties, "title" ) );
+ else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
+
+ // Check for a root on the consumer properties and pass to service
+ if ( mlt_properties_get( properties, "root" ) )
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", mlt_properties_get( properties, "root" ) );
+
+ // Specify roots in other cases...
+ if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
+ {
+ // Get the current working directory
+ char *cwd = getcwd( NULL, 0 );
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", cwd );
+ free( cwd );
+ }
+
+ // Make the document
+ doc = westley_make_doc( this, service );
+
+ // Handle the output
+ if ( resource == NULL || !strcmp( resource, "" ) )
+ {
+ xmlDocFormatDump( stdout, doc, 1 );
+ }
+ else if ( strchr( resource, '.' ) == NULL )
+ {
+ xmlChar *buffer = NULL;
+ int length = 0;
+ xmlDocDumpMemoryEnc( doc, &buffer, &length, "utf-8" );
+ mlt_properties_set( properties, resource, _s(buffer) );
+ xmlFree( buffer );
+ }
+ else
+ {
+ xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 );
+ }
+
+ // Close the document
+ xmlFreeDoc( doc );
+ }
+
+ mlt_consumer_stop( this );
+
+ mlt_consumer_stopped( this );
+
+ return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+ return 1;
+}
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_westley_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_westley_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( consumer_type, "westley", consumer_westley_init );
+ MLT_REGISTER( producer_type, "westley", producer_westley_init );
+ MLT_REGISTER( producer_type, "westley-xml", producer_westley_init );
+}
--- /dev/null
+/*
+ * producer_westley.c -- a libxml2 parser of mlt service networks
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// TODO: destroy unreferenced producers (they are currently destroyed
+// when the returned producer is closed).
+
+#include <framework/mlt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
+#include <libxml/tree.h>
+
+#define STACK_SIZE 1000
+#define BRANCH_SIG_LEN 4000
+
+#define _x (const xmlChar*)
+#define _s (const char*)
+
+#undef DEBUG
+#ifdef DEBUG
+extern xmlDocPtr westley_make_doc( mlt_service service );
+#endif
+
+enum service_type
+{
+ mlt_invalid_type,
+ mlt_unknown_type,
+ mlt_producer_type,
+ mlt_playlist_type,
+ mlt_entry_type,
+ mlt_tractor_type,
+ mlt_multitrack_type,
+ mlt_filter_type,
+ mlt_transition_type,
+ mlt_consumer_type,
+ mlt_field_type,
+ mlt_services_type,
+ mlt_dummy_filter_type,
+ mlt_dummy_transition_type,
+ mlt_dummy_producer_type,
+};
+
+struct deserialise_context_s
+{
+ enum service_type stack_types[ STACK_SIZE ];
+ mlt_service stack_service[ STACK_SIZE ];
+ int stack_service_size;
+ mlt_properties producer_map;
+ mlt_properties destructors;
+ char *property;
+ int is_value;
+ xmlDocPtr value_doc;
+ xmlNodePtr stack_node[ STACK_SIZE ];
+ int stack_node_size;
+ xmlDocPtr entity_doc;
+ int entity_is_replace;
+ int depth;
+ int branch[ STACK_SIZE ];
+ const xmlChar *publicId;
+ const xmlChar *systemId;
+ mlt_properties params;
+ mlt_profile profile;
+};
+typedef struct deserialise_context_s *deserialise_context;
+
+/** Convert the numerical current branch address to a dot-delimited string.
+*/
+static char *serialise_branch( deserialise_context this, char *s )
+{
+ int i;
+
+ s[0] = 0;
+ for ( i = 0; i < this->depth; i++ )
+ {
+ int len = strlen( s );
+ snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] );
+ }
+ return s;
+}
+
+/** Push a service.
+*/
+
+static int context_push_service( deserialise_context this, mlt_service that, enum service_type type )
+{
+ int ret = this->stack_service_size >= STACK_SIZE - 1;
+ if ( ret == 0 )
+ {
+ this->stack_service[ this->stack_service_size ] = that;
+ this->stack_types[ this->stack_service_size++ ] = type;
+
+ // Record the tree branch on which this service lives
+ if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_westley_branch" ) == NULL )
+ {
+ char s[ BRANCH_SIG_LEN ];
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_westley_branch", serialise_branch( this, s ) );
+ }
+ }
+ return ret;
+}
+
+/** Pop a service.
+*/
+
+static mlt_service context_pop_service( deserialise_context this, enum service_type *type )
+{
+ mlt_service result = NULL;
+ if ( this->stack_service_size > 0 )
+ {
+ result = this->stack_service[ -- this->stack_service_size ];
+ if ( type != NULL )
+ *type = this->stack_types[ this->stack_service_size ];
+ }
+ return result;
+}
+
+/** Push a node.
+*/
+
+static int context_push_node( deserialise_context this, xmlNodePtr node )
+{
+ int ret = this->stack_node_size >= STACK_SIZE - 1;
+ if ( ret == 0 )
+ this->stack_node[ this->stack_node_size ++ ] = node;
+ return ret;
+}
+
+/** Pop a node.
+*/
+
+static xmlNodePtr context_pop_node( deserialise_context this )
+{
+ xmlNodePtr result = NULL;
+ if ( this->stack_node_size > 0 )
+ result = this->stack_node[ -- this->stack_node_size ];
+ return result;
+}
+
+
+// Set the destructor on a new service
+static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
+{
+ int registered = mlt_properties_get_int( properties, "registered" );
+ char *key = mlt_properties_get( properties, "registered" );
+ mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
+ mlt_properties_set_int( properties, "registered", ++ registered );
+}
+
+
+// Prepend the property value with the document root
+static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name )
+{
+ char *resource = mlt_properties_get( properties, name );
+ if ( resource != NULL && resource[0] )
+ {
+ // Qualify file name properties
+ char *root = mlt_properties_get( context->producer_map, "root" );
+ if ( root != NULL && strcmp( root, "" ) )
+ {
+ char *full_resource = malloc( strlen( root ) + strlen( resource ) + 2 );
+ if ( resource[ 0 ] != '/' && strchr( resource, ':' ) == NULL )
+ {
+ strcpy( full_resource, root );
+ strcat( full_resource, "/" );
+ strcat( full_resource, resource );
+ }
+ else
+ {
+ strcpy( full_resource, resource );
+ }
+ mlt_properties_set( properties, name, full_resource );
+ free( full_resource );
+ }
+ }
+}
+
+
+/** This function adds a producer to a playlist or multitrack when
+ there is no entry or track element.
+*/
+
+static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
+{
+ // Return value (0 = service remains top of stack, 1 means it can be removed)
+ int result = 0;
+
+ // Get the parent producer
+ enum service_type type = mlt_invalid_type;
+ mlt_service container = context_pop_service( context, &type );
+ int contained = 0;
+
+ if ( service != NULL && container != NULL )
+ {
+ char *container_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( container ), "_westley_branch" );
+ char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_westley_branch" );
+ contained = !strncmp( container_branch, service_branch, strlen( container_branch ) );
+ }
+
+ if ( contained )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ char *hide_s = mlt_properties_get( properties, "hide" );
+
+ // Indicate that this service is no longer top of stack
+ result = 1;
+
+ switch( type )
+ {
+ case mlt_tractor_type:
+ {
+ mlt_multitrack multitrack = mlt_tractor_multitrack( MLT_TRACTOR( container ) );
+ mlt_multitrack_connect( multitrack, MLT_PRODUCER( service ), mlt_multitrack_count( multitrack ) );
+ }
+ break;
+ case mlt_multitrack_type:
+ {
+ mlt_multitrack_connect( MLT_MULTITRACK( container ),
+ MLT_PRODUCER( service ),
+ mlt_multitrack_count( MLT_MULTITRACK( container ) ) );
+ }
+ break;
+ case mlt_playlist_type:
+ {
+ mlt_playlist_append_io( MLT_PLAYLIST( container ), MLT_PRODUCER( service ), in, out );
+ }
+ break;
+ default:
+ result = 0;
+ fprintf( stderr, "Producer defined inside something that isn't a container\n" );
+ break;
+ };
+
+ // Set the hide state of the track producer
+ if ( hide_s != NULL )
+ {
+ if ( strcmp( hide_s, "video" ) == 0 )
+ mlt_properties_set_int( properties, "hide", 1 );
+ else if ( strcmp( hide_s, "audio" ) == 0 )
+ mlt_properties_set_int( properties, "hide", 2 );
+ else if ( strcmp( hide_s, "both" ) == 0 )
+ mlt_properties_set_int( properties, "hide", 3 );
+ }
+ }
+
+ // Put the parent producer back
+ if ( container != NULL )
+ context_push_service( context, container, type );
+
+ return result;
+}
+
+/** Attach filters defined on that to this.
+*/
+
+static void attach_filters( mlt_service this, mlt_service that )
+{
+ if ( that != NULL )
+ {
+ int i = 0;
+ mlt_filter filter = NULL;
+ for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
+ {
+ mlt_service_attach( this, filter );
+ attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
+ }
+ }
+}
+
+static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ mlt_tractor tractor = mlt_tractor_new( );
+ mlt_service service = MLT_TRACTOR_SERVICE( tractor );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
+
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+ mlt_properties_set_int( MLT_TRACTOR_PROPERTIES( tractor ), "global_feed", 1 );
+
+ if ( mlt_properties_get( properties, "id" ) != NULL )
+ mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
+
+ context_push_service( context, service, mlt_tractor_type );
+}
+
+static void on_end_tractor( deserialise_context context, const xmlChar *name )
+{
+ // Get the tractor
+ enum service_type type;
+ mlt_service tractor = context_pop_service( context, &type );
+
+ if ( tractor != NULL && type == mlt_tractor_type )
+ {
+ // See if the tractor should be added to a playlist or multitrack
+ if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
+ context_push_service( context, tractor, type );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid state for tractor\n" );
+ }
+}
+
+static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ enum service_type type;
+ mlt_service parent = context_pop_service( context, &type );
+
+ // If we don't have a parent, then create one now, providing we're in a state where we can
+ if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
+ {
+ mlt_tractor tractor = NULL;
+ // Push the parent back
+ if ( parent != NULL )
+ context_push_service( context, parent, type );
+
+ // Create a tractor to contain the multitrack
+ tractor = mlt_tractor_new( );
+ parent = MLT_TRACTOR_SERVICE( tractor );
+ track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
+ type = mlt_tractor_type;
+
+ // Flag it as a synthesised tractor for clean up later
+ mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "fezzik_synth", 1 );
+ }
+
+ if ( type == mlt_tractor_type )
+ {
+ mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+ if ( mlt_properties_get( properties, "id" ) != NULL )
+ mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
+
+ context_push_service( context, parent, type );
+ context_push_service( context, service, mlt_multitrack_type );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid multitrack position\n" );
+ }
+}
+
+static void on_end_multitrack( deserialise_context context, const xmlChar *name )
+{
+ // Get the multitrack from the stack
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+
+ if ( service == NULL || type != mlt_multitrack_type )
+ fprintf( stderr, "End multitrack in the wrong state...\n" );
+}
+
+static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ mlt_playlist playlist = mlt_playlist_init( );
+ mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
+
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+ // Out will be overwritten later as we append, so we need to save it
+ if ( xmlStrcmp( atts[ 0 ], _x("out") ) == 0 )
+ mlt_properties_set( properties, "_westley.out", ( const char* )atts[ 1 ] );
+ }
+
+ if ( mlt_properties_get( properties, "id" ) != NULL )
+ mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
+
+ context_push_service( context, service, mlt_playlist_type );
+}
+
+static void on_end_playlist( deserialise_context context, const xmlChar *name )
+{
+ // Get the playlist from the stack
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+
+ if ( service != NULL && type == mlt_playlist_type )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ mlt_position in = mlt_properties_get_position( properties, "in" );
+ mlt_position out = mlt_properties_get_position( properties, "out" );
+
+ // See if the playlist should be added to a playlist or multitrack
+ if ( add_producer( context, service, in, out ) == 0 )
+ context_push_service( context, service, type );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid state of playlist end\n" );
+ }
+}
+
+static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // use a dummy service to hold properties to allow arbitrary nesting
+ mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+ mlt_service_init( service, NULL );
+
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ context_push_service( context, service, mlt_dummy_producer_type );
+
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+}
+
+// Parse a SMIL clock value (as produced by Kino 0.9.1) and return position in frames
+static mlt_position parse_clock_value( char *value, double fps )
+{
+ // This implementation expects a fully specified clock value - no optional
+ // parts (e.g. 1:05)
+ char *pos, *copy = strdup( value );
+ int hh, mm, ss, ms;
+ mlt_position result = -1;
+
+ value = copy;
+ pos = strchr( value, ':' );
+ if ( !pos )
+ return result;
+ *pos = '\0';
+ hh = atoi( value );
+ value = pos + 1;
+
+ pos = strchr( value, ':' );
+ if ( !pos )
+ return result;
+ *pos = '\0';
+ mm = atoi( value );
+ value = pos + 1;
+
+ pos = strchr( value, '.' );
+ if ( !pos )
+ return result;
+ *pos = '\0';
+ ss = atoi( value );
+ value = pos + 1;
+
+ ms = atoi( value );
+ free( copy );
+ result = ( fps * ( ( (hh * 3600) + (mm * 60) + ss ) * 1000 + ms ) / 1000 + 0.5 );
+
+ return result;
+}
+
+static void on_end_producer( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( service != NULL && type == mlt_dummy_producer_type )
+ {
+ mlt_service producer = NULL;
+
+ qualify_property( context, properties, "resource" );
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ // Let Kino-SMIL src be a synonym for resource
+ if ( resource == NULL )
+ {
+ qualify_property( context, properties, "src" );
+ resource = mlt_properties_get( properties, "src" );
+ }
+
+ // Instantiate the producer
+ if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
+ {
+ char temp[ 1024 ];
+ strncpy( temp, mlt_properties_get( properties, "mlt_service" ), 1024 );
+ if ( resource != NULL )
+ {
+ strcat( temp, ":" );
+ strncat( temp, resource, 1023 - strlen( temp ) );
+ }
+ producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", temp ) );
+ }
+
+ // Just in case the plugin requested doesn't exist...
+ if ( producer == NULL && resource != NULL )
+ producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", resource ) );
+
+ if ( producer == NULL )
+ producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", "+INVALID.txt" ) );
+
+ if ( producer == NULL )
+ producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", "colour:red" ) );
+
+ // Track this producer
+ track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
+
+ // Propogate the properties
+ qualify_property( context, properties, "resource" );
+ qualify_property( context, properties, "luma" );
+ qualify_property( context, properties, "luma.resource" );
+ qualify_property( context, properties, "composite.luma" );
+ qualify_property( context, properties, "producer.resource" );
+
+ // Handle in/out properties separately
+ mlt_position in = -1;
+ mlt_position out = -1;
+
+ // Get in
+ if ( mlt_properties_get( properties, "in" ) != NULL )
+ in = mlt_properties_get_position( properties, "in" );
+ // Let Kino-SMIL clipBegin be a synonym for in
+ if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
+ {
+ if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) )
+ // Parse clock value
+ in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ),
+ mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
+ else
+ // Parse frames value
+ in = mlt_properties_get_position( properties, "clipBegin" );
+ }
+ // Get out
+ if ( mlt_properties_get( properties, "out" ) != NULL )
+ out = mlt_properties_get_position( properties, "out" );
+ // Let Kino-SMIL clipEnd be a synonym for out
+ if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
+ {
+ if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) )
+ // Parse clock value
+ out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ),
+ mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
+ else
+ // Parse frames value
+ out = mlt_properties_get_position( properties, "clipEnd" );
+ }
+ // Remove in and out
+ mlt_properties_set( properties, "in", NULL );
+ mlt_properties_set( properties, "out", NULL );
+
+ // Inherit the properties
+ mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
+
+ // Attach all filters from service onto producer
+ attach_filters( producer, service );
+
+ // Add the producer to the producer map
+ if ( mlt_properties_get( properties, "id" ) != NULL )
+ mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
+
+ // See if the producer should be added to a playlist or multitrack
+ if ( add_producer( context, producer, in, out ) == 0 )
+ {
+ // Otherwise, set in and out on...
+ if ( in != -1 || out != -1 )
+ {
+ // Get the parent service
+ enum service_type type;
+ mlt_service parent = context_pop_service( context, &type );
+ if ( parent != NULL )
+ {
+ // Get the parent properties
+ properties = MLT_SERVICE_PROPERTIES( parent );
+
+ char *resource = mlt_properties_get( properties, "resource" );
+
+ // Put the parent producer back
+ context_push_service( context, parent, type );
+
+ // If the parent is a track or entry
+ if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
+ {
+ mlt_properties_set_position( properties, "in", in );
+ mlt_properties_set_position( properties, "out", out );
+ }
+ else
+ {
+ // Otherwise, set in and out on producer directly
+ mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
+ }
+ }
+ else
+ {
+ // Otherwise, set in and out on producer directly
+ mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
+ }
+ }
+
+ // Push the producer onto the stack
+ context_push_service( context, producer, mlt_producer_type );
+ }
+
+ mlt_service_close( service );
+ }
+}
+
+static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // Get the playlist from the stack
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_position length = 0;
+
+ if ( type == mlt_playlist_type && service != NULL )
+ {
+ // Look for the length attribute
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
+ {
+ length = atoll( _s(atts[1]) );
+ break;
+ }
+ }
+
+ // Append a blank to the playlist
+ mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
+
+ // Push the playlist back onto the stack
+ context_push_service( context, service, type );
+ }
+ else
+ {
+ fprintf( stderr, "blank without a playlist - a definite no no\n" );
+ }
+}
+
+static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ mlt_producer entry = NULL;
+ mlt_properties temp = mlt_properties_new( );
+
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ mlt_properties_set( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+ // Look for the producer attribute
+ if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
+ {
+ mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
+ if ( producer != NULL )
+ mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
+ }
+ }
+
+ // If we have a valid entry
+ if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
+ {
+ mlt_playlist_clip_info info;
+ enum service_type parent_type = invalid_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+ mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
+
+ if ( parent_type == mlt_playlist_type )
+ {
+ // Append the producer to the playlist
+ if ( mlt_properties_get( temp, "in" ) != NULL || mlt_properties_get( temp, "out" ) != NULL )
+ {
+ mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
+ mlt_properties_get_position( temp, "in" ),
+ mlt_properties_get_position( temp, "out" ) );
+ }
+ else
+ {
+ mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
+ }
+
+ // Handle the repeat property
+ if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
+ {
+ mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
+ mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
+ mlt_properties_get_int( temp, "repeat" ) );
+ }
+
+ mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
+ entry = info.cut;
+ }
+ else
+ {
+ fprintf( stderr, "Entry not part of a playlist...\n" );
+ }
+
+ context_push_service( context, parent, parent_type );
+ }
+
+ // Push the cut onto the stack
+ context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
+
+ mlt_properties_close( temp );
+}
+
+static void on_end_entry( deserialise_context context, const xmlChar *name )
+{
+ // Get the entry from the stack
+ enum service_type entry_type = invalid_type;
+ mlt_service entry = context_pop_service( context, &entry_type );
+
+ if ( entry == NULL && entry_type != mlt_entry_type )
+ {
+ fprintf( stderr, "Invalid state at end of entry\n" );
+ }
+}
+
+static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // use a dummy service to hold properties to allow arbitrary nesting
+ mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+ mlt_service_init( service, NULL );
+
+ // Push the dummy service onto the stack
+ context_push_service( context, service, mlt_entry_type );
+
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
+
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+ // Look for the producer attribute
+ if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
+ {
+ mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
+ if ( producer != NULL )
+ mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
+ }
+ }
+}
+
+static void on_end_track( deserialise_context context, const xmlChar *name )
+{
+ // Get the track from the stack
+ enum service_type track_type;
+ mlt_service track = context_pop_service( context, &track_type );
+
+ if ( track != NULL && track_type == mlt_entry_type )
+ {
+ mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
+ enum service_type parent_type = invalid_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+ mlt_multitrack multitrack = NULL;
+
+ mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ if ( parent_type == mlt_tractor_type )
+ multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
+ else if ( parent_type == mlt_multitrack_type )
+ multitrack = MLT_MULTITRACK( parent );
+ else
+ fprintf( stderr, "track contained in an invalid container\n" );
+
+ if ( multitrack != NULL )
+ {
+ // Set producer i/o if specified
+ if ( mlt_properties_get( track_props, "in" ) != NULL ||
+ mlt_properties_get( track_props, "out" ) != NULL )
+ {
+ mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ),
+ mlt_properties_get_position( track_props, "in" ),
+ mlt_properties_get_position( track_props, "out" ) );
+ mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
+ mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
+ track_props = MLT_PRODUCER_PROPERTIES( cut );
+ mlt_producer_close( cut );
+ }
+ else
+ {
+ mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
+ }
+
+ // Set the hide state of the track producer
+ char *hide_s = mlt_properties_get( track_props, "hide" );
+ if ( hide_s != NULL )
+ {
+ if ( strcmp( hide_s, "video" ) == 0 )
+ mlt_properties_set_int( producer_props, "hide", 1 );
+ else if ( strcmp( hide_s, "audio" ) == 0 )
+ mlt_properties_set_int( producer_props, "hide", 2 );
+ else if ( strcmp( hide_s, "both" ) == 0 )
+ mlt_properties_set_int( producer_props, "hide", 3 );
+ }
+ }
+
+ if ( parent != NULL )
+ context_push_service( context, parent, parent_type );
+
+ mlt_service_close( track );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid state at end of track\n" );
+ }
+}
+
+static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // use a dummy service to hold properties to allow arbitrary nesting
+ mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+ mlt_service_init( service, NULL );
+
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ context_push_service( context, service, mlt_dummy_filter_type );
+
+ // Set the properties
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
+}
+
+static void on_end_filter( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ enum service_type parent_type = invalid_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+
+ if ( service != NULL && type == mlt_dummy_filter_type )
+ {
+ mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) );
+ mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
+
+ track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
+
+ // Propogate the properties
+ qualify_property( context, properties, "resource" );
+ qualify_property( context, properties, "luma" );
+ qualify_property( context, properties, "luma.resource" );
+ qualify_property( context, properties, "composite.luma" );
+ qualify_property( context, properties, "producer.resource" );
+ mlt_properties_inherit( filter_props, properties );
+
+ // Attach all filters from service onto filter
+ attach_filters( filter, service );
+
+ // Associate the filter with the parent
+ if ( parent != NULL )
+ {
+ if ( parent_type == mlt_tractor_type )
+ {
+ mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
+ mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
+ mlt_filter_set_in_and_out( MLT_FILTER( filter ),
+ mlt_properties_get_int( properties, "in" ),
+ mlt_properties_get_int( properties, "out" ) );
+ }
+ else
+ {
+ mlt_service_attach( parent, MLT_FILTER( filter ) );
+ }
+
+ // Put the parent back on the stack
+ context_push_service( context, parent, parent_type );
+ }
+ else
+ {
+ fprintf( stderr, "filter closed with invalid parent...\n" );
+ }
+
+ // Close the dummy filter service
+ mlt_service_close( service );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid top of stack on filter close\n" );
+ }
+}
+
+static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // use a dummy service to hold properties to allow arbitrary nesting
+ mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+ mlt_service_init( service, NULL );
+
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ context_push_service( context, service, mlt_dummy_transition_type );
+
+ // Set the properties
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
+}
+
+static void on_end_transition( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ enum service_type parent_type = invalid_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+
+ if ( service != NULL && type == mlt_dummy_transition_type )
+ {
+ char *id = mlt_properties_get( properties, "mlt_service" );
+ mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
+ mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
+
+ track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
+
+ // Propogate the properties
+ qualify_property( context, properties, "resource" );
+ qualify_property( context, properties, "luma" );
+ qualify_property( context, properties, "luma.resource" );
+ qualify_property( context, properties, "composite.luma" );
+ qualify_property( context, properties, "producer.resource" );
+ mlt_properties_inherit( effect_props, properties );
+
+ // Attach all filters from service onto effect
+ attach_filters( effect, service );
+
+ // Associate the filter with the parent
+ if ( parent != NULL )
+ {
+ if ( parent_type == mlt_tractor_type )
+ {
+ mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
+ if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
+ mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
+ mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
+ mlt_properties_get_int( properties, "a_track" ),
+ mlt_properties_get_int( properties, "b_track" ) );
+ mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
+ mlt_properties_get_int( properties, "in" ),
+ mlt_properties_get_int( properties, "out" ) );
+ }
+ else
+ {
+ fprintf( stderr, "Misplaced transition - ignoring\n" );
+ }
+
+ // Put the parent back on the stack
+ context_push_service( context, parent, parent_type );
+ }
+ else
+ {
+ fprintf( stderr, "transition closed with invalid parent...\n" );
+ }
+
+ // Close the dummy filter service
+ mlt_service_close( service );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid top of stack on transition close\n" );
+ }
+}
+
+static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ const char *value = NULL;
+
+ if ( service != NULL )
+ {
+ // Set the properties
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
+ context->property = strdup( _s(atts[ 1 ]) );
+ else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
+ value = _s(atts[ 1 ]);
+ }
+
+ if ( context->property != NULL )
+ mlt_properties_set( properties, context->property, value == NULL ? "" : value );
+
+ // Tell parser to collect any further nodes for serialisation
+ context->is_value = 1;
+
+ context_push_service( context, service, type );
+ }
+ else
+ {
+ fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name );
+ }
+}
+
+static void on_end_property( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( service != NULL )
+ {
+ // Tell parser to stop building a tree
+ context->is_value = 0;
+
+ // See if there is a xml tree for the value
+ if ( context->property != NULL && context->value_doc != NULL )
+ {
+ xmlChar *value;
+ int size;
+
+ // Serialise the tree to get value
+ xmlDocDumpMemory( context->value_doc, &value, &size );
+ mlt_properties_set( properties, context->property, _s(value) );
+ xmlFree( value );
+ xmlFreeDoc( context->value_doc );
+ context->value_doc = NULL;
+ }
+
+ // Close this property handling
+ free( context->property );
+ context->property = NULL;
+
+ context_push_service( context, service, type );
+ }
+ else
+ {
+ fprintf( stderr, "Property without a service '%s'??\n", (const char *)name );
+ }
+}
+
+static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+//printf("on_start_element: %s\n", name );
+ context->branch[ context->depth ] ++;
+ context->depth ++;
+
+ // Build a tree from nodes within a property value
+ if ( context->is_value == 1 )
+ {
+ xmlNodePtr node = xmlNewNode( NULL, name );
+
+ if ( context->value_doc == NULL )
+ {
+ // Start a new tree
+ context->value_doc = xmlNewDoc( _x("1.0") );
+ xmlDocSetRootElement( context->value_doc, node );
+ }
+ else
+ {
+ // Append child to tree
+ xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
+ }
+ context_push_node( context, node );
+
+ // Set the attributes
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
+ }
+ else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
+ on_start_tractor( context, name, atts );
+ else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
+ on_start_multitrack( context, name, atts );
+ else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
+ on_start_playlist( context, name, atts );
+ else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
+ on_start_producer( context, name, atts );
+ else if ( xmlStrcmp( name, _x("blank") ) == 0 )
+ on_start_blank( context, name, atts );
+ else if ( xmlStrcmp( name, _x("entry") ) == 0 )
+ on_start_entry( context, name, atts );
+ else if ( xmlStrcmp( name, _x("track") ) == 0 )
+ on_start_track( context, name, atts );
+ else if ( xmlStrcmp( name, _x("filter") ) == 0 )
+ on_start_filter( context, name, atts );
+ else if ( xmlStrcmp( name, _x("transition") ) == 0 )
+ on_start_transition( context, name, atts );
+ else if ( xmlStrcmp( name, _x("property") ) == 0 )
+ on_start_property( context, name, atts );
+ else if ( xmlStrcmp( name, _x("westley") ) == 0 )
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( context->producer_map, ( const char * )atts[ 0 ], ( const char * )atts[ 1 ] );
+}
+
+static void on_end_element( void *ctx, const xmlChar *name )
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+//printf("on_end_element: %s\n", name );
+ if ( context->is_value == 1 && xmlStrcmp( name, _x("property") ) != 0 )
+ context_pop_node( context );
+ else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
+ on_end_multitrack( context, name );
+ else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
+ on_end_playlist( context, name );
+ else if ( xmlStrcmp( name, _x("track") ) == 0 )
+ on_end_track( context, name );
+ else if ( xmlStrcmp( name, _x("entry") ) == 0 )
+ on_end_entry( context, name );
+ else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
+ on_end_tractor( context, name );
+ else if ( xmlStrcmp( name, _x("property") ) == 0 )
+ on_end_property( context, name );
+ else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
+ on_end_producer( context, name );
+ else if ( xmlStrcmp( name, _x("filter") ) == 0 )
+ on_end_filter( context, name );
+ else if ( xmlStrcmp( name, _x("transition") ) == 0 )
+ on_end_transition( context, name );
+
+ context->branch[ context->depth ] = 0;
+ context->depth --;
+}
+
+static void on_characters( void *ctx, const xmlChar *ch, int len )
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+ char *value = calloc( len + 1, 1 );
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( service != NULL )
+ context_push_service( context, service, type );
+
+ value[ len ] = 0;
+ strncpy( value, (const char*) ch, len );
+
+ if ( context->stack_node_size > 0 )
+ xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
+
+ // libxml2 generates an on_characters immediately after a get_entity within
+ // an element value, and we ignore it because it is called again during
+ // actual substitution.
+ else if ( context->property != NULL && context->entity_is_replace == 0 )
+ {
+ char *s = mlt_properties_get( properties, context->property );
+ if ( s != NULL )
+ {
+ // Append new text to existing content
+ char *new = calloc( strlen( s ) + len + 1, 1 );
+ strcat( new, s );
+ strcat( new, value );
+ mlt_properties_set( properties, context->property, new );
+ free( new );
+ }
+ else
+ mlt_properties_set( properties, context->property, value );
+ }
+ context->entity_is_replace = 0;
+
+ free( value);
+}
+
+/** Convert parameters parsed from resource into entity declarations.
+*/
+static void params_to_entities( deserialise_context context )
+{
+ if ( context->params != NULL )
+ {
+ int i;
+
+ // Add our params as entitiy declarations
+ for ( i = 0; i < mlt_properties_count( context->params ); i++ )
+ {
+ xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
+ xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
+ context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
+ }
+
+ // Flag completion
+ mlt_properties_close( context->params );
+ context->params = NULL;
+ }
+}
+
+// The following 3 facilitate entity substitution in the SAX parser
+static void on_internal_subset( void *ctx, const xmlChar* name,
+ const xmlChar* publicId, const xmlChar* systemId )
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+ context->publicId = publicId;
+ context->systemId = systemId;
+ xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
+
+ // Override default entities with our parameters
+ params_to_entities( context );
+}
+
+// TODO: Check this with Dan... I think this is for westley parameterisation
+// but it's breaking standard escaped entities (like < etc).
+static void on_entity_declaration( void *ctx, const xmlChar* name, int type,
+ const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+ xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
+}
+
+// TODO: Check this functionality (see on_entity_declaration)
+static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+ xmlEntityPtr e = NULL;
+
+ // Setup for entity declarations if not ready
+ if ( xmlGetIntSubset( context->entity_doc ) == NULL )
+ {
+ xmlCreateIntSubset( context->entity_doc, _x("westley"), _x(""), _x("") );
+ context->publicId = _x("");
+ context->systemId = _x("");
+ }
+
+ // Add our parameters if not already
+ params_to_entities( context );
+
+ e = xmlGetPredefinedEntity( name );
+
+ // Send signal to on_characters that an entity substitutin is pending
+ if ( e == NULL )
+ {
+ e = xmlGetDocEntity( context->entity_doc, name );
+ if ( e != NULL )
+ context->entity_is_replace = 1;
+ }
+
+ return e;
+}
+
+/** Convert a hexadecimal character to its value.
+*/
+static int tohex( char p )
+{
+ return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
+}
+
+/** Decode a url-encoded string containing hexadecimal character sequences.
+*/
+static char *url_decode( char *dest, char *src )
+{
+ char *p = dest;
+
+ while ( *src )
+ {
+ if ( *src == '%' )
+ {
+ *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
+ src += 3;
+ }
+ else
+ {
+ *p ++ = *src ++;
+ }
+ }
+
+ *p = *src;
+ return dest;
+}
+
+/** Extract the filename from a URL attaching parameters to a properties list.
+*/
+static void parse_url( mlt_properties properties, char *url )
+{
+ int i;
+ int n = strlen( url );
+ char *name = NULL;
+ char *value = NULL;
+
+ for ( i = 0; i < n; i++ )
+ {
+ switch ( url[ i ] )
+ {
+ case '?':
+ url[ i++ ] = '\0';
+ name = &url[ i ];
+ break;
+
+ case ':':
+ case '=':
+ url[ i++ ] = '\0';
+ value = &url[ i ];
+ break;
+
+ case '&':
+ url[ i++ ] = '\0';
+ if ( name != NULL && value != NULL )
+ mlt_properties_set( properties, name, value );
+ name = &url[ i ];
+ value = NULL;
+ break;
+ }
+ }
+ if ( name != NULL && value != NULL )
+ mlt_properties_set( properties, name, value );
+}
+
+// Quick workaround to avoid unecessary libxml2 warnings
+static int file_exists( char *file )
+{
+ char *name = strdup( file );
+ int exists = 0;
+ if ( name != NULL && strchr( name, '?' ) )
+ *( strchr( name, '?' ) ) = '\0';
+ if ( name != NULL )
+ {
+ FILE *f = fopen( name, "r" );
+ exists = f != NULL;
+ if ( exists ) fclose( f );
+ }
+ free( name );
+ return exists;
+}
+
+mlt_producer producer_westley_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
+{
+ xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
+ struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
+ mlt_properties properties = NULL;
+ int i = 0;
+ struct _xmlParserCtxt *xmlcontext;
+ int well_formed = 0;
+ char *filename = NULL;
+ int info = strcmp( id, "westley-xml" ) ? 0 : 1;
+
+ if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
+ return NULL;
+
+ context = calloc( 1, sizeof( struct deserialise_context_s ) );
+ if ( context == NULL )
+ return NULL;
+
+ context->producer_map = mlt_properties_new();
+ context->destructors = mlt_properties_new();
+ context->params = mlt_properties_new();
+ context->profile = profile;
+
+ // Decode URL and parse parameters
+ mlt_properties_set( context->producer_map, "root", "" );
+ if ( info == 0 )
+ {
+ filename = strdup( data );
+ parse_url( context->params, url_decode( filename, data ) );
+
+ // We need the directory prefix which was used for the westley
+ if ( strchr( filename, '/' ) )
+ {
+ char *root = NULL;
+ mlt_properties_set( context->producer_map, "root", filename );
+ root = mlt_properties_get( context->producer_map, "root" );
+ *( strrchr( root, '/' ) ) = '\0';
+
+ // If we don't have an absolute path here, we're heading for disaster...
+ if ( root[ 0 ] != '/' )
+ {
+ char *cwd = getcwd( NULL, 0 );
+ char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
+ sprintf( real, "%s/%s", cwd, root );
+ mlt_properties_set( context->producer_map, "root", real );
+ free( real );
+ free( cwd );
+ }
+ }
+ }
+
+ // We need to track the number of registered filters
+ mlt_properties_set_int( context->destructors, "registered", 0 );
+
+ // Setup SAX callbacks
+ sax->startElement = on_start_element;
+ sax->endElement = on_end_element;
+ sax->characters = on_characters;
+ sax->cdataBlock = on_characters;
+ sax->internalSubset = on_internal_subset;
+ sax->entityDecl = on_entity_declaration;
+ sax->getEntity = on_get_entity;
+
+ // Setup libxml2 SAX parsing
+ xmlInitParser();
+ xmlSubstituteEntitiesDefault( 1 );
+ // This is used to facilitate entity substitution in the SAX parser
+ context->entity_doc = xmlNewDoc( _x("1.0") );
+ if ( info == 0 )
+ xmlcontext = xmlCreateFileParserCtxt( filename );
+ else
+ xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
+
+ // Invalid context - clean up and return NULL
+ if ( xmlcontext == NULL )
+ {
+ mlt_properties_close( context->producer_map );
+ mlt_properties_close( context->destructors );
+ mlt_properties_close( context->params );
+ free( context );
+ free( sax );
+ free( filename );
+ return NULL;
+ }
+
+ xmlcontext->sax = sax;
+ xmlcontext->_private = ( void* )context;
+
+ // Parse
+ xmlParseDocument( xmlcontext );
+ well_formed = xmlcontext->wellFormed;
+
+ // Cleanup after parsing
+ xmlFreeDoc( context->entity_doc );
+ free( sax );
+ xmlcontext->sax = NULL;
+ xmlcontext->_private = NULL;
+ xmlFreeParserCtxt( xmlcontext );
+ xmlMemoryDump( ); // for debugging
+
+ // Get the last producer on the stack
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ if ( well_formed && service != NULL )
+ {
+ // Verify it is a producer service (mlt_type="mlt_producer")
+ // (producer, playlist, multitrack)
+ char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
+ if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
+ service = NULL;
+ }
+
+#ifdef DEBUG
+ xmlDocPtr doc = westley_make_doc( service );
+ xmlDocFormatDump( stdout, doc, 1 );
+ xmlFreeDoc( doc );
+ service = NULL;
+#endif
+
+ if ( well_formed && service != NULL )
+ {
+ char *title = mlt_properties_get( context->producer_map, "title" );
+
+ // Need the complete producer list for various reasons
+ properties = context->destructors;
+
+ // Now make sure we don't have a reference to the service in the properties
+ for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
+ {
+ char *name = mlt_properties_get_name( properties, i );
+ if ( mlt_properties_get_data( properties, name, NULL ) == service )
+ {
+ mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
+ break;
+ }
+ }
+
+ // We are done referencing destructor property list
+ // Set this var to service properties for convenience
+ properties = MLT_SERVICE_PROPERTIES( service );
+
+ // Assign the title
+ mlt_properties_set( properties, "title", title );
+
+ // Optimise for overlapping producers
+ mlt_producer_optimise( MLT_PRODUCER( service ) );
+
+ // Handle deep copies
+ if ( getenv( "MLT_WESTLEY_DEEP" ) == NULL )
+ {
+ // Now assign additional properties
+ if ( info == 0 )
+ mlt_properties_set( properties, "resource", data );
+
+ // This tells consumer_westley not to deep copy
+ mlt_properties_set( properties, "westley", "was here" );
+ }
+ else
+ {
+ // Allow the project to be edited
+ mlt_properties_set( properties, "_westley", "was here" );
+ mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
+ }
+ }
+ else
+ {
+ // Return null if not well formed
+ service = NULL;
+ }
+
+ // Clean up
+ mlt_properties_close( context->producer_map );
+ if ( context->params != NULL )
+ mlt_properties_close( context->params );
+ mlt_properties_close( context->destructors );
+ free( context );
+ free( filename );
+
+ return MLT_PRODUCER( service );
+}
--- /dev/null
+<?xml version='1.0' encoding='utf-8'?>
+
+<!-- MLT westley DTD v0.1.0 -->
+
+<!ELEMENT westley (producer | playlist | tractor | multitrack)+ >
+<!ELEMENT property ANY >
+<!ATTLIST property
+ name CDATA #REQUIRED
+ value CDATA #IMPLIED
+>
+<!ELEMENT producer (property)* >
+<!ATTLIST producer
+ id ID #IMPLIED
+ in CDATA #IMPLIED
+ out CDATA #IMPLIED
+ mlt_service CDATA #IMPLIED
+>
+<!ELEMENT filter (property)* >
+<!ATTLIST filter
+ id ID #IMPLIED
+ in CDATA #IMPLIED
+ out CDATA #IMPLIED
+ mlt_service CDATA #IMPLIED
+ track CDATA #IMPLIED
+>
+<!ELEMENT transition (property)* >
+<!ATTLIST transition
+ id ID #IMPLIED
+ in CDATA #IMPLIED
+ out CDATA #IMPLIED
+ mlt_service CDATA #IMPLIED
+ a_track CDATA #IMPLIED
+ b_track CDATA #IMPLIED
+>
+<!ELEMENT playlist (entry | blank | producer | playlist | tractor | multitrack)+ >
+<!ATTLIST playlist
+ id ID #IMPLIED
+ in CDATA #IMPLIED
+ out CDATA #IMPLIED
+>
+<!ELEMENT entry (producer | playlist | tractor | multitrack | filter | transition)* >
+<!ATTLIST entry
+ producer IDREF #IMPLIED
+ in CDATA #IMPLIED
+ out CDATA #IMPLIED
+>
+<!ELEMENT blank EMPTY >
+<!ATTLIST blank
+ length CDATA #REQUIRED
+>
+<!ELEMENT tractor (multitrack, (filter | transition)*) >
+<!ATTLIST tractor
+ id ID #IMPLIED
+ in CDATA #IMPLIED
+ out CDATA #IMPLIED
+>
+<!ELEMENT multitrack (track | producer | playlist | tractor | multitrack)+ >
+<!ATTLIST multitrack
+ id ID #IMPLIED
+>
+<!ELEMENT track (producer | playlist | tractor | multitrack | filter | transition)* >
+<!ATTLIST track
+ producer IDREF #IMPLIED
+>
--- /dev/null
+include ../../../config.mak
+
+TARGET = ../libmltxine$(LIBSUF)
+
+OBJS = factory.o \
+ deinterlace.o \
+ filter_deinterlace.o
+
+ifdef MMX_FLAGS
+OBJS += cpu_accel.o
+endif
+
+CFLAGS += -I../../ -DARCH_X86
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET)
+
+install: all
+ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * attributes.h
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ *
+ * mpeg2dec 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpeg2dec is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* use gcc attribs to align critical data structures */
+
+#ifndef ATTRIBUTE_H_
+#define ATTRIBUTE_H_
+
+#ifdef ATTRIBUTE_ALIGNED_MAX
+#define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align)))
+#else
+#define ATTR_ALIGN(align)
+#endif
+
+/* disable GNU __attribute__ extension, when not compiling with GNU C */
+#if defined(__GNUC__) || defined (__ICC)
+#ifndef ATTRIBUTE_PACKED
+#define ATTRIBUTE_PACKED 1
+#endif
+#else
+#undef ATTRIBUTE_PACKED
+#ifndef __attribute__
+#define __attribute__(x) /**/
+#endif /* __attribute __*/
+#endif
+
+#endif /* ATTRIBUTE_H_ */
+
--- /dev/null
+/*
+ * cpu_accel.c
+ * Copyright (C) 1999-2001 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ *
+ * mpeg2dec 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpeg2dec is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <dlfcn.h>
+
+#define LOG_MODULE "cpu_accel"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "xineutils.h"
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+#if defined __x86_64__
+static uint32_t arch_accel (void)
+{
+ uint32_t caps;
+ /* No need to test for this on AMD64, we know what the
+ platform has. */
+ caps = MM_ACCEL_X86_MMX | MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT | MM_ACCEL_X86_SSE2;
+
+ return caps;
+}
+#else
+static uint32_t arch_accel (void)
+{
+#ifndef _MSC_VER
+
+ uint32_t eax, ebx, ecx, edx;
+ int AMD;
+ uint32_t caps;
+
+#ifndef PIC
+#define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("cpuid" \
+ : "=a" (eax), \
+ "=b" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "a" (op) \
+ : "cc")
+#else /* PIC version : save ebx */
+#define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("pushl %%ebx\n\t" \
+ "cpuid\n\t" \
+ "movl %%ebx,%1\n\t" \
+ "popl %%ebx" \
+ : "=a" (eax), \
+ "=r" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "a" (op) \
+ : "cc")
+#endif
+
+ __asm__ ("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl $0x200000,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl"
+ : "=r" (eax),
+ "=r" (ebx)
+ :
+ : "cc");
+
+ if (eax == ebx) /* no cpuid */
+ return 0;
+
+ cpuid (0x00000000, eax, ebx, ecx, edx);
+ if (!eax) /* vendor string only */
+ return 0;
+
+ AMD = (ebx == 0x68747541) && (ecx == 0x444d4163) && (edx == 0x69746e65);
+
+ cpuid (0x00000001, eax, ebx, ecx, edx);
+ if (! (edx & 0x00800000)) /* no MMX */
+ return 0;
+
+ caps = MM_ACCEL_X86_MMX;
+ if (edx & 0x02000000) /* SSE - identical to AMD MMX extensions */
+ caps |= MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT;
+
+ if (edx & 0x04000000) /* SSE2 */
+ caps |= MM_ACCEL_X86_SSE2;
+
+ cpuid (0x80000000, eax, ebx, ecx, edx);
+ if (eax < 0x80000001) /* no extended capabilities */
+ return caps;
+
+ cpuid (0x80000001, eax, ebx, ecx, edx);
+
+ if (edx & 0x80000000)
+ caps |= MM_ACCEL_X86_3DNOW;
+
+ if (AMD && (edx & 0x00400000)) /* AMD MMX extensions */
+ caps |= MM_ACCEL_X86_MMXEXT;
+
+ return caps;
+#else /* _MSC_VER */
+ return 0;
+#endif
+}
+#endif /* x86_64 */
+
+static jmp_buf sigill_return;
+
+static void sigill_handler (int n) {
+ longjmp(sigill_return, 1);
+}
+#endif /* ARCH_X86 */
+
+#if defined (ARCH_PPC) && defined (ENABLE_ALTIVEC)
+static sigjmp_buf jmpbuf;
+static volatile sig_atomic_t canjump = 0;
+
+static void sigill_handler (int sig)
+{
+ if (!canjump) {
+ signal (sig, SIG_DFL);
+ raise (sig);
+ }
+
+ canjump = 0;
+ siglongjmp (jmpbuf, 1);
+}
+
+static uint32_t arch_accel (void)
+{
+ signal (SIGILL, sigill_handler);
+ if (sigsetjmp (jmpbuf, 1)) {
+ signal (SIGILL, SIG_DFL);
+ return 0;
+ }
+
+ canjump = 1;
+
+ __asm__ volatile ("mtspr 256, %0\n\t"
+ "vand %%v0, %%v0, %%v0"
+ :
+ : "r" (-1));
+
+ signal (SIGILL, SIG_DFL);
+ return MM_ACCEL_PPC_ALTIVEC;
+}
+#endif /* ARCH_PPC */
+
+uint32_t xine_mm_accel (void)
+{
+ static int initialized = 0;
+ static uint32_t accel;
+
+ if (!initialized) {
+#if defined (ARCH_X86) || (defined (ARCH_PPC) && defined (ENABLE_ALTIVEC))
+ accel = arch_accel ();
+#elif defined (HAVE_MLIB)
+#ifdef MLIB_LAZYLOAD
+ void *hndl;
+
+ if ((hndl = dlopen("libmlib.so.2", RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE)) == NULL) {
+ accel = 0;
+ }
+ else {
+ dlclose(hndl);
+ accel = MM_ACCEL_MLIB;
+ }
+#else
+ accel = MM_ACCEL_MLIB;
+#endif
+#else
+ accel = 0;
+#endif
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+#ifndef _MSC_VER
+ /* test OS support for SSE */
+ if( accel & MM_ACCEL_X86_SSE ) {
+ void (*old_sigill_handler)(int);
+
+ old_sigill_handler = signal (SIGILL, sigill_handler);
+
+ if (setjmp(sigill_return)) {
+ lprintf ("OS doesn't support SSE instructions.\n");
+ accel &= ~(MM_ACCEL_X86_SSE|MM_ACCEL_X86_SSE2);
+ } else {
+ __asm__ volatile ("xorps %xmm0, %xmm0");
+ }
+
+ signal (SIGILL, old_sigill_handler);
+ }
+#endif /* _MSC_VER */
+#endif /* ARCH_X86 || ARCH_X86_64 */
+
+ if(getenv("XINE_NO_ACCEL")) {
+ accel = 0;
+ }
+
+ initialized = 1;
+ }
+
+ return accel;
+}
--- /dev/null
+ /*
+ * Copyright (C) 2001 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Deinterlace routines by Miguel Freitas
+ * based of DScaler project sources (deinterlace.sourceforge.net)
+ *
+ * Currently only available for Xv driver and MMX extensions
+ *
+ * small todo list:
+ * - implement non-MMX versions for all methods
+ * - support MMX2 instructions
+ * - move some generic code from xv driver to this file
+ * - make it also work for yuy2 frames
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "deinterlace.h"
+#include "xineutils.h"
+
+#define xine_fast_memcpy memcpy
+#define xine_fast_memmove memmove
+
+/*
+ DeinterlaceFieldBob algorithm
+ Based on Virtual Dub plugin by Gunnar Thalin
+ MMX asm version from dscaler project (deinterlace.sourceforge.net)
+ Linux version for Xine player by Miguel Freitas
+*/
+static void deinterlace_bob_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height )
+{
+#ifdef USE_MMX
+ int Line;
+ uint64_t *YVal1;
+ uint64_t *YVal2;
+ uint64_t *YVal3;
+ uint64_t *Dest;
+ uint8_t* pEvenLines = psrc[0];
+ uint8_t* pOddLines = psrc[0]+width;
+ int LineLength = width;
+ int SourcePitch = width * 2;
+ int IsOdd = 1;
+ long EdgeDetect = 625;
+ long JaggieThreshold = 73;
+
+ int n;
+
+ uint64_t qwEdgeDetect;
+ uint64_t qwThreshold;
+
+ static mmx_t YMask = {ub:{0xff,0,0xff,0,0xff,0,0xff,0}};
+ static mmx_t Mask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+ qwEdgeDetect = EdgeDetect;
+ qwEdgeDetect += (qwEdgeDetect << 48) + (qwEdgeDetect << 32) + (qwEdgeDetect << 16);
+ qwThreshold = JaggieThreshold;
+ qwThreshold += (qwThreshold << 48) + (qwThreshold << 32) + (qwThreshold << 16);
+
+
+ // copy first even line no matter what, and the first odd line if we're
+ // processing an odd field.
+ xine_fast_memcpy(pdst, pEvenLines, LineLength);
+ if (IsOdd)
+ xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength);
+
+ height = height / 2;
+ for (Line = 0; Line < height - 1; ++Line)
+ {
+ if (IsOdd)
+ {
+ YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ YVal2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+ Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+ }
+ else
+ {
+ YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+ YVal2 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+ }
+
+ // For ease of reading, the comments below assume that we're operating on an odd
+ // field (i.e., that bIsOdd is true). The exact same processing is done when we
+ // operate on an even field, but the roles of the odd and even fields are reversed.
+ // It's just too cumbersome to explain the algorithm in terms of "the next odd
+ // line if we're doing an odd field, or the next even line if we're doing an
+ // even field" etc. So wherever you see "odd" or "even" below, keep in mind that
+ // half the time this function is called, those words' meanings will invert.
+
+ // Copy the odd line to the overlay verbatim.
+ xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength);
+
+ n = LineLength >> 3;
+ while( n-- )
+ {
+ movq_m2r (*YVal1++, mm0);
+ movq_m2r (*YVal2++, mm1);
+ movq_m2r (*YVal3++, mm2);
+
+ // get intensities in mm3 - 4
+ movq_r2r ( mm0, mm3 );
+ pand_m2r ( YMask, mm3 );
+ movq_r2r ( mm1, mm4 );
+ pand_m2r ( YMask, mm4 );
+ movq_r2r ( mm2, mm5 );
+ pand_m2r ( YMask, mm5 );
+
+ // get average in mm0
+ pand_m2r ( Mask, mm0 );
+ pand_m2r ( Mask, mm2 );
+ psrlw_i2r ( 01, mm0 );
+ psrlw_i2r ( 01, mm2 );
+ paddw_r2r ( mm2, mm0 );
+
+ // work out (O1 - E) * (O2 - E) / 2 - EdgeDetect * (O1 - O2) ^ 2 >> 12
+ // result will be in mm6
+
+ psrlw_i2r ( 01, mm3 );
+ psrlw_i2r ( 01, mm4 );
+ psrlw_i2r ( 01, mm5 );
+
+ movq_r2r ( mm3, mm6 );
+ psubw_r2r ( mm4, mm6 ); //mm6 = O1 - E
+
+ movq_r2r ( mm5, mm7 );
+ psubw_r2r ( mm4, mm7 ); //mm7 = O2 - E
+
+ pmullw_r2r ( mm7, mm6 ); // mm6 = (O1 - E) * (O2 - E)
+
+ movq_r2r ( mm3, mm7 );
+ psubw_r2r ( mm5, mm7 ); // mm7 = (O1 - O2)
+ pmullw_r2r ( mm7, mm7 ); // mm7 = (O1 - O2) ^ 2
+ psrlw_i2r ( 12, mm7 ); // mm7 = (O1 - O2) ^ 2 >> 12
+ pmullw_m2r ( *&qwEdgeDetect, mm7 );// mm7 = EdgeDetect * (O1 - O2) ^ 2 >> 12
+
+ psubw_r2r ( mm7, mm6 ); // mm6 is what we want
+
+ pcmpgtw_m2r ( *&qwThreshold, mm6 );
+
+ movq_r2r ( mm6, mm7 );
+
+ pand_r2r ( mm6, mm0 );
+
+ pandn_r2r ( mm1, mm7 );
+
+ por_r2r ( mm0, mm7 );
+
+ movq_r2m ( mm7, *Dest++ );
+ }
+ }
+
+ // Copy last odd line if we're processing an even field.
+ if (! IsOdd)
+ {
+ xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+ pOddLines + (height - 1) * SourcePitch,
+ LineLength);
+ }
+
+ // clear out the MMX registers ready for doing floating point
+ // again
+ emms();
+#endif
+}
+
+/* Deinterlace the latest field, with a tendency to weave rather than bob.
+ Good for high detail on low-movement scenes.
+ Seems to produce bad output in general case, need to check if this
+ is normal or if the code is broken.
+*/
+static int deinterlace_weave_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height )
+{
+#ifdef USE_MMX
+
+ int Line;
+ uint64_t *YVal1;
+ uint64_t *YVal2;
+ uint64_t *YVal3;
+ uint64_t *YVal4;
+ uint64_t *Dest;
+ uint8_t* pEvenLines = psrc[0];
+ uint8_t* pOddLines = psrc[0]+width;
+ uint8_t* pPrevLines;
+
+ int LineLength = width;
+ int SourcePitch = width * 2;
+ int IsOdd = 1;
+
+ long TemporalTolerance = 300;
+ long SpatialTolerance = 600;
+ long SimilarityThreshold = 25;
+
+ int n;
+
+ uint64_t qwSpatialTolerance;
+ uint64_t qwTemporalTolerance;
+ uint64_t qwThreshold;
+
+ static mmx_t YMask = {ub:{0xff,0,0xff,0,0xff,0,0xff,0}};
+ static mmx_t Mask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+
+ // Make sure we have all the data we need.
+ if ( psrc[0] == NULL || psrc[1] == NULL )
+ return 0;
+
+ if (IsOdd)
+ pPrevLines = psrc[1] + width;
+ else
+ pPrevLines = psrc[1];
+
+ // Since the code uses MMX to process 4 pixels at a time, we need our constants
+ // to be represented 4 times per quadword.
+ qwSpatialTolerance = SpatialTolerance;
+ qwSpatialTolerance += (qwSpatialTolerance << 48) + (qwSpatialTolerance << 32) + (qwSpatialTolerance << 16);
+ qwTemporalTolerance = TemporalTolerance;
+ qwTemporalTolerance += (qwTemporalTolerance << 48) + (qwTemporalTolerance << 32) + (qwTemporalTolerance << 16);
+ qwThreshold = SimilarityThreshold;
+ qwThreshold += (qwThreshold << 48) + (qwThreshold << 32) + (qwThreshold << 16);
+
+ // copy first even line no matter what, and the first odd line if we're
+ // processing an even field.
+ xine_fast_memcpy(pdst, pEvenLines, LineLength);
+ if (!IsOdd)
+ xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength);
+
+ height = height / 2;
+ for (Line = 0; Line < height - 1; ++Line)
+ {
+ if (IsOdd)
+ {
+ YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+ YVal2 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ YVal4 = (uint64_t *)(pPrevLines + Line * SourcePitch);
+ Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+ }
+ else
+ {
+ YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ YVal2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+ YVal4 = (uint64_t *)(pPrevLines + (Line + 1) * SourcePitch);
+ Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+ }
+
+ // For ease of reading, the comments below assume that we're operating on an odd
+ // field (i.e., that bIsOdd is true). The exact same processing is done when we
+ // operate on an even field, but the roles of the odd and even fields are reversed.
+ // It's just too cumbersome to explain the algorithm in terms of "the next odd
+ // line if we're doing an odd field, or the next even line if we're doing an
+ // even field" etc. So wherever you see "odd" or "even" below, keep in mind that
+ // half the time this function is called, those words' meanings will invert.
+
+ // Copy the even scanline below this one to the overlay buffer, since we'll be
+ // adapting the current scanline to the even lines surrounding it. The scanline
+ // above has already been copied by the previous pass through the loop.
+ xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength);
+
+ n = LineLength >> 3;
+ while( n-- )
+ {
+ movq_m2r ( *YVal1++, mm0 ); // mm0 = E1
+ movq_m2r ( *YVal2++, mm1 ); // mm1 = O
+ movq_m2r ( *YVal3++, mm2 ); // mm2 = E2
+
+ movq_r2r ( mm0, mm3 ); // mm3 = intensity(E1)
+ movq_r2r ( mm1, mm4 ); // mm4 = intensity(O)
+ movq_r2r ( mm2, mm6 ); // mm6 = intensity(E2)
+
+ pand_m2r ( YMask, mm3 );
+ pand_m2r ( YMask, mm4 );
+ pand_m2r ( YMask, mm6 );
+
+ // Average E1 and E2 for interpolated bobbing.
+ // leave result in mm0
+ pand_m2r ( Mask, mm0 ); // mm0 = E1 with lower chroma bit stripped off
+ pand_m2r ( Mask, mm2 ); // mm2 = E2 with lower chroma bit stripped off
+ psrlw_i2r ( 01, mm0 ); // mm0 = E1 / 2
+ psrlw_i2r ( 01, mm2 ); // mm2 = E2 / 2
+ paddb_r2r ( mm2, mm0 );
+
+ // The meat of the work is done here. We want to see whether this pixel is
+ // close in luminosity to ANY of: its top neighbor, its bottom neighbor,
+ // or its predecessor. To do this without branching, we use MMX's
+ // saturation feature, which gives us Z(x) = x if x>=0, or 0 if x<0.
+ //
+ // The formula we're computing here is
+ // Z(ST - (E1 - O) ^ 2) + Z(ST - (E2 - O) ^ 2) + Z(TT - (Oold - O) ^ 2)
+ // where ST is spatial tolerance and TT is temporal tolerance. The idea
+ // is that if a pixel is similar to none of its neighbors, the resulting
+ // value will be pretty low, probably zero. A high value therefore indicates
+ // that the pixel had a similar neighbor. The pixel in the same position
+ // in the field before last (Oold) is considered a neighbor since we want
+ // to be able to display 1-pixel-high horizontal lines.
+
+ movq_m2r ( *&qwSpatialTolerance, mm7 );
+ movq_r2r ( mm3, mm5 ); // mm5 = E1
+ psubsw_r2r ( mm4, mm5 ); // mm5 = E1 - O
+ psraw_i2r ( 1, mm5 );
+ pmullw_r2r ( mm5, mm5 ); // mm5 = (E1 - O) ^ 2
+ psubusw_r2r ( mm5, mm7 ); // mm7 = ST - (E1 - O) ^ 2, or 0 if that's negative
+
+ movq_m2r ( *&qwSpatialTolerance, mm3 );
+ movq_r2r ( mm6, mm5 ); // mm5 = E2
+ psubsw_r2r ( mm4, mm5 ); // mm5 = E2 - O
+ psraw_i2r ( 1, mm5 );
+ pmullw_r2r ( mm5, mm5 ); // mm5 = (E2 - O) ^ 2
+ psubusw_r2r ( mm5, mm3 ); // mm0 = ST - (E2 - O) ^ 2, or 0 if that's negative
+ paddusw_r2r ( mm3, mm7 ); // mm7 = (ST - (E1 - O) ^ 2) + (ST - (E2 - O) ^ 2)
+
+ movq_m2r ( *&qwTemporalTolerance, mm3 );
+ movq_m2r ( *YVal4++, mm5 ); // mm5 = Oold
+ pand_m2r ( YMask, mm5 );
+ psubsw_r2r ( mm4, mm5 ); // mm5 = Oold - O
+ psraw_i2r ( 1, mm5 ); // XXX
+ pmullw_r2r ( mm5, mm5 ); // mm5 = (Oold - O) ^ 2
+ psubusw_r2r ( mm5, mm3 ); /* mm0 = TT - (Oold - O) ^ 2, or 0 if that's negative */
+ paddusw_r2r ( mm3, mm7 ); // mm7 = our magic number
+
+ /*
+ * Now compare the similarity totals against our threshold. The pcmpgtw
+ * instruction will populate the target register with a bunch of mask bits,
+ * filling words where the comparison is true with 1s and ones where it's
+ * false with 0s. A few ANDs and NOTs and an OR later, we have bobbed
+ * values for pixels under the similarity threshold and weaved ones for
+ * pixels over the threshold.
+ */
+
+ pcmpgtw_m2r( *&qwThreshold, mm7 ); // mm7 = 0xffff where we're greater than the threshold, 0 elsewhere
+ movq_r2r ( mm7, mm6 ); // mm6 = 0xffff where we're greater than the threshold, 0 elsewhere
+ pand_r2r ( mm1, mm7 ); // mm7 = weaved data where we're greater than the threshold, 0 elsewhere
+ pandn_r2r ( mm0, mm6 ); // mm6 = bobbed data where we're not greater than the threshold, 0 elsewhere
+ por_r2r ( mm6, mm7 ); // mm7 = bobbed and weaved data
+
+ movq_r2m ( mm7, *Dest++ );
+ }
+ }
+
+ // Copy last odd line if we're processing an odd field.
+ if (IsOdd)
+ {
+ xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+ pOddLines + (height - 1) * SourcePitch,
+ LineLength);
+ }
+
+ // clear out the MMX registers ready for doing floating point
+ // again
+ emms();
+
+#endif
+
+ return 1;
+}
+
+
+// This is a simple lightweight DeInterlace method that uses little CPU time
+// but gives very good results for low or intermedite motion. (MORE CPU THAN BOB)
+// It defers frames by one field, but that does not seem to produce noticeable
+// lip sync problems.
+//
+// The method used is to take either the older or newer weave pixel depending
+// upon which give the smaller comb factor, and then clip to avoid large damage
+// when wrong.
+//
+// I'd intended this to be part of a larger more elaborate method added to
+// Blended Clip but this give too good results for the CPU to ignore here.
+static int deinterlace_greedy_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height )
+{
+#ifdef USE_MMX
+ int Line;
+ int LoopCtr;
+ uint64_t *L1; // ptr to Line1, of 3
+ uint64_t *L2; // ptr to Line2, the weave line
+ uint64_t *L3; // ptr to Line3
+ uint64_t *LP2; // ptr to prev Line2
+ uint64_t *Dest;
+ uint8_t* pEvenLines = psrc[0];
+ uint8_t* pOddLines = psrc[0]+width;
+ uint8_t* pPrevLines;
+
+ static mmx_t ShiftMask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+ int LineLength = width;
+ int SourcePitch = width * 2;
+ int IsOdd = 1;
+ long GreedyMaxComb = 15;
+ static mmx_t MaxComb;
+ int i;
+
+ if ( psrc[0] == NULL || psrc[1] == NULL )
+ return 0;
+
+ if (IsOdd)
+ pPrevLines = psrc[1] + width;
+ else
+ pPrevLines = psrc[1];
+
+
+ for( i = 0; i < 8; i++ )
+ MaxComb.ub[i] = GreedyMaxComb; // How badly do we let it weave? 0-255
+
+
+ // copy first even line no matter what, and the first odd line if we're
+ // processing an EVEN field. (note diff from other deint rtns.)
+ xine_fast_memcpy(pdst, pEvenLines, LineLength); //DL0
+ if (!IsOdd)
+ xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength); //DL1
+
+ height = height / 2;
+ for (Line = 0; Line < height - 1; ++Line)
+ {
+ LoopCtr = LineLength / 8; // there are LineLength / 8 qwords per line
+
+ if (IsOdd)
+ {
+ L1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+ L2 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ L3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ LP2 = (uint64_t *)(pPrevLines + Line * SourcePitch); // prev Odd lines
+ Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+ }
+ else
+ {
+ L1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ L2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ L3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+ LP2 = (uint64_t *)(pPrevLines + (Line + 1) * SourcePitch); //prev even lines
+ Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+ }
+
+ xine_fast_memcpy((char *)Dest + LineLength, L3, LineLength);
+
+// For ease of reading, the comments below assume that we're operating on an odd
+// field (i.e., that info->IsOdd is true). Assume the obvious for even lines..
+
+ while( LoopCtr-- )
+ {
+ movq_m2r ( *L1++, mm1 );
+ movq_m2r ( *L2++, mm2 );
+ movq_m2r ( *L3++, mm3 );
+ movq_m2r ( *LP2++, mm0 );
+
+ // average L1 and L3 leave result in mm4
+ movq_r2r ( mm1, mm4 ); // L1
+
+ pand_m2r ( ShiftMask, mm4 );
+ psrlw_i2r ( 01, mm4 );
+ movq_r2r ( mm3, mm5 ); // L3
+ pand_m2r ( ShiftMask, mm5 );
+ psrlw_i2r ( 01, mm5 );
+ paddb_r2r ( mm5, mm4 ); // the average, for computing comb
+
+ // get abs value of possible L2 comb
+ movq_r2r ( mm2, mm7 ); // L2
+ psubusb_r2r ( mm4, mm7 ); // L2 - avg
+ movq_r2r ( mm4, mm5 ); // avg
+ psubusb_r2r ( mm2, mm5 ); // avg - L2
+ por_r2r ( mm7, mm5 ); // abs(avg-L2)
+ movq_r2r ( mm4, mm6 ); // copy of avg for later
+
+ // get abs value of possible LP2 comb
+ movq_r2r ( mm0, mm7 ); // LP2
+ psubusb_r2r ( mm4, mm7 ); // LP2 - avg
+ psubusb_r2r ( mm0, mm4 ); // avg - LP2
+ por_r2r ( mm7, mm4 ); // abs(avg-LP2)
+
+ // use L2 or LP2 depending upon which makes smaller comb
+ psubusb_r2r ( mm5, mm4 ); // see if it goes to zero
+ psubusb_r2r ( mm5, mm5 ); // 0
+ pcmpeqb_r2r ( mm5, mm4 ); // if (mm4=0) then FF else 0
+ pcmpeqb_r2r ( mm4, mm5 ); // opposite of mm4
+
+ // if Comb(LP2) <= Comb(L2) then mm4=ff, mm5=0 else mm4=0, mm5 = 55
+ pand_r2r ( mm2, mm5 ); // use L2 if mm5 == ff, else 0
+ pand_r2r ( mm0, mm4 ); // use LP2 if mm4 = ff, else 0
+ por_r2r ( mm5, mm4 ); // may the best win
+
+ // Now lets clip our chosen value to be not outside of the range
+ // of the high/low range L1-L3 by more than abs(L1-L3)
+ // This allows some comb but limits the damages and also allows more
+ // detail than a boring oversmoothed clip.
+
+ movq_r2r ( mm1, mm2 ); // copy L1
+ psubusb_r2r ( mm3, mm2 ); // - L3, with saturation
+ paddusb_r2r ( mm3, mm2 ); // now = Max(L1,L3)
+
+ pcmpeqb_r2r ( mm7, mm7 ); // all ffffffff
+ psubusb_r2r ( mm1, mm7 ); // - L1
+ paddusb_r2r ( mm7, mm3 ); // add, may sat at fff..
+ psubusb_r2r ( mm7, mm3 ); // now = Min(L1,L3)
+
+ // allow the value to be above the high or below the low by amt of MaxComb
+ paddusb_m2r ( MaxComb, mm2 ); // increase max by diff
+ psubusb_m2r ( MaxComb, mm3 ); // lower min by diff
+
+ psubusb_r2r ( mm3, mm4 ); // best - Min
+ paddusb_r2r ( mm3, mm4 ); // now = Max(best,Min(L1,L3)
+
+ pcmpeqb_r2r ( mm7, mm7 ); // all ffffffff
+ psubusb_r2r ( mm4, mm7 ); // - Max(best,Min(best,L3)
+ paddusb_r2r ( mm7, mm2 ); // add may sat at FFF..
+ psubusb_r2r ( mm7, mm2 ); // now = Min( Max(best, Min(L1,L3), L2 )=L2 clipped
+
+ movq_r2m ( mm2, *Dest++ ); // move in our clipped best
+
+ }
+ }
+
+ /* Copy last odd line if we're processing an Odd field. */
+ if (IsOdd)
+ {
+ xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+ pOddLines + (height - 1) * SourcePitch,
+ LineLength);
+ }
+
+ /* clear out the MMX registers ready for doing floating point again */
+ emms();
+
+#endif
+
+ return 1;
+}
+
+/* Use one field to interpolate the other (low cpu utilization)
+ Will lose resolution but does not produce weaving effect
+ (good for fast moving scenes) also know as "linear interpolation"
+*/
+static void deinterlace_onefield_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height )
+{
+#ifdef USE_MMX
+ int Line;
+ uint64_t *YVal1;
+ uint64_t *YVal3;
+ uint64_t *Dest;
+ uint8_t* pEvenLines = psrc[0];
+ uint8_t* pOddLines = psrc[0]+width;
+ int LineLength = width;
+ int SourcePitch = width * 2;
+ int IsOdd = 1;
+
+ int n;
+
+ static mmx_t Mask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+ /*
+ * copy first even line no matter what, and the first odd line if we're
+ * processing an odd field.
+ */
+
+ xine_fast_memcpy(pdst, pEvenLines, LineLength);
+ if (IsOdd)
+ xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength);
+
+ height = height / 2;
+ for (Line = 0; Line < height - 1; ++Line)
+ {
+ if (IsOdd)
+ {
+ YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+ YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+ Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+ }
+ else
+ {
+ YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+ YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+ Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+ }
+
+ // Copy the odd line to the overlay verbatim.
+ xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength);
+
+ n = LineLength >> 3;
+ while( n-- )
+ {
+ movq_m2r (*YVal1++, mm0);
+ movq_m2r (*YVal3++, mm2);
+
+ // get average in mm0
+ pand_m2r ( Mask, mm0 );
+ pand_m2r ( Mask, mm2 );
+ psrlw_i2r ( 01, mm0 );
+ psrlw_i2r ( 01, mm2 );
+ paddw_r2r ( mm2, mm0 );
+
+ movq_r2m ( mm0, *Dest++ );
+ }
+ }
+
+ /* Copy last odd line if we're processing an even field. */
+ if (! IsOdd)
+ {
+ xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+ pOddLines + (height - 1) * SourcePitch,
+ LineLength);
+ }
+
+ /* clear out the MMX registers ready for doing floating point
+ * again
+ */
+ emms();
+#endif
+}
+
+/* Linear Blend filter - does a kind of vertical blurring on the image.
+ (idea borrowed from mplayer's sources)
+*/
+static void deinterlace_linearblend_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height )
+{
+#ifdef USE_MMX
+ int Line;
+ uint64_t *YVal1;
+ uint64_t *YVal2;
+ uint64_t *YVal3;
+ uint64_t *Dest;
+ int LineLength = width;
+
+ int n;
+
+ /* Copy first line */
+ xine_fast_memmove(pdst, psrc[0], LineLength);
+
+ for (Line = 1; Line < height - 1; ++Line)
+ {
+ YVal1 = (uint64_t *)(psrc[0] + (Line - 1) * LineLength);
+ YVal2 = (uint64_t *)(psrc[0] + (Line) * LineLength);
+ YVal3 = (uint64_t *)(psrc[0] + (Line + 1) * LineLength);
+ Dest = (uint64_t *)(pdst + Line * LineLength);
+
+ n = LineLength >> 3;
+ while( n-- )
+ {
+ /* load data from 3 lines */
+ movq_m2r (*YVal1++, mm0);
+ movq_m2r (*YVal2++, mm1);
+ movq_m2r (*YVal3++, mm2);
+
+ /* expand bytes to words */
+ punpckhbw_r2r (mm0, mm3);
+ punpckhbw_r2r (mm1, mm4);
+ punpckhbw_r2r (mm2, mm5);
+ punpcklbw_r2r (mm0, mm0);
+ punpcklbw_r2r (mm1, mm1);
+ punpcklbw_r2r (mm2, mm2);
+
+ /*
+ * deinterlacing:
+ * deint_line = (line0 + 2*line1 + line2) / 4
+ */
+ psrlw_i2r (07, mm0);
+ psrlw_i2r (06, mm1);
+ psrlw_i2r (07, mm2);
+ psrlw_i2r (07, mm3);
+ psrlw_i2r (06, mm4);
+ psrlw_i2r (07, mm5);
+ paddw_r2r (mm1, mm0);
+ paddw_r2r (mm2, mm0);
+ paddw_r2r (mm4, mm3);
+ paddw_r2r (mm5, mm3);
+ psrlw_i2r (03, mm0);
+ psrlw_i2r (03, mm3);
+
+ /* pack 8 words to 8 bytes in mm0 */
+ packuswb_r2r (mm3, mm0);
+
+ movq_r2m ( mm0, *Dest++ );
+ }
+ }
+
+ /* Copy last line */
+ xine_fast_memmove(pdst + Line * LineLength,
+ psrc[0] + Line * LineLength, LineLength);
+
+ /* clear out the MMX registers ready for doing floating point
+ * again
+ */
+ emms();
+#endif
+}
+
+/* Linear Blend filter - C version contributed by Rogerio Brito.
+ This algorithm has the same interface as the other functions.
+
+ The destination "screen" (pdst) is constructed from the source
+ screen (psrc[0]) line by line.
+
+ The i-th line of the destination screen is the average of 3 lines
+ from the source screen: the (i-1)-th, i-th and (i+1)-th lines, with
+ the i-th line having weight 2 in the computation.
+
+ Remarks:
+ * each line on pdst doesn't depend on previous lines;
+ * due to the way the algorithm is defined, the first & last lines of the
+ screen aren't deinterlaced.
+
+*/
+static void deinterlace_linearblend_yuv( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height )
+{
+ register int x, y;
+ register uint8_t *l0, *l1, *l2, *l3;
+
+ l0 = pdst; /* target line */
+ l1 = psrc[0]; /* 1st source line */
+ l2 = l1 + width; /* 2nd source line = line that follows l1 */
+ l3 = l2 + width; /* 3rd source line = line that follows l2 */
+
+ /* Copy the first line */
+ xine_fast_memcpy(l0, l1, width);
+ l0 += width;
+
+ for (y = 1; y < height-1; ++y) {
+ /* computes avg of: l1 + 2*l2 + l3 */
+
+ for (x = 0; x < width; ++x) {
+ l0[x] = (l1[x] + (l2[x]<<1) + l3[x]) >> 2;
+ }
+
+ /* updates the line pointers */
+ l1 = l2; l2 = l3; l3 += width;
+ l0 += width;
+ }
+
+ /* Copy the last line */
+ xine_fast_memcpy(l0, l1, width);
+}
+
+static int check_for_mmx(void)
+{
+#ifdef USE_MMX
+static int config_flags = -1;
+
+ if ( config_flags == -1 )
+ config_flags = xine_mm_accel();
+ if (config_flags & MM_ACCEL_X86_MMX)
+ return 1;
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+/* generic YUV deinterlacer
+ pdst -> pointer to destination bitmap
+ psrc -> array of pointers to source bitmaps ([0] = most recent)
+ width,height -> dimension for bitmaps
+ method -> DEINTERLACE_xxx
+*/
+
+void deinterlace_yuv( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height, int method )
+{
+ switch( method ) {
+ case DEINTERLACE_NONE:
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ break;
+ case DEINTERLACE_BOB:
+ if( check_for_mmx() )
+ deinterlace_bob_yuv_mmx(pdst,psrc,width,height);
+ else /* FIXME: provide an alternative? */
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ break;
+ case DEINTERLACE_WEAVE:
+ if( check_for_mmx() )
+ {
+ if( !deinterlace_weave_yuv_mmx(pdst,psrc,width,height) )
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ }
+ else /* FIXME: provide an alternative? */
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ break;
+ case DEINTERLACE_GREEDY:
+ if( check_for_mmx() )
+ {
+ if( !deinterlace_greedy_yuv_mmx(pdst,psrc,width,height) )
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ }
+ else /* FIXME: provide an alternative? */
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ break;
+ case DEINTERLACE_ONEFIELD:
+ if( check_for_mmx() )
+ deinterlace_onefield_yuv_mmx(pdst,psrc,width,height);
+ else /* FIXME: provide an alternative? */
+ xine_fast_memcpy(pdst,psrc[0],width*height);
+ break;
+ case DEINTERLACE_ONEFIELDXV:
+ lprintf("ONEFIELDXV must be handled by the video driver.\n");
+ break;
+ case DEINTERLACE_LINEARBLEND:
+ if( check_for_mmx() )
+ deinterlace_linearblend_yuv_mmx(pdst,psrc,width,height);
+ else
+ deinterlace_linearblend_yuv(pdst,psrc,width,height);
+ break;
+ default:
+ lprintf("unknown method %d.\n",method);
+ break;
+ }
+}
+
+int deinterlace_yuv_supported ( int method )
+{
+ switch( method ) {
+ case DEINTERLACE_NONE:
+ return 1;
+ case DEINTERLACE_BOB:
+ case DEINTERLACE_WEAVE:
+ case DEINTERLACE_GREEDY:
+ case DEINTERLACE_ONEFIELD:
+ return check_for_mmx();
+ case DEINTERLACE_ONEFIELDXV:
+ lprintf ("ONEFIELDXV must be handled by the video driver.\n");
+ return 0;
+ case DEINTERLACE_LINEARBLEND:
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *deinterlace_methods[] = {
+ "none",
+ "bob",
+ "weave",
+ "greedy",
+ "onefield",
+ "onefield_xv",
+ "linearblend",
+ NULL
+};
+
+
--- /dev/null
+ /*
+ * Copyright (C) 2001 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Deinterlace routines by Miguel Freitas
+ * based of DScaler project sources (deinterlace.sourceforge.net)
+ *
+ * Currently only available for Xv driver and MMX extensions
+ *
+ */
+
+#ifndef __DEINTERLACE_H__
+#define __DEINTERLACE_H__
+
+//#include "video_out.h"
+#include <stdint.h>
+
+int deinterlace_yuv_supported ( int method );
+void deinterlace_yuv( uint8_t *pdst, uint8_t *psrc[],
+ int width, int height, int method );
+
+#define DEINTERLACE_NONE 0
+#define DEINTERLACE_BOB 1
+#define DEINTERLACE_WEAVE 2
+#define DEINTERLACE_GREEDY 3
+#define DEINTERLACE_ONEFIELD 4
+#define DEINTERLACE_ONEFIELDXV 5
+#define DEINTERLACE_LINEARBLEND 6
+
+extern const char *deinterlace_methods[];
+
+#endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_deinterlace_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+ MLT_REGISTER( filter_type, "deinterlace", filter_deinterlace_init );
+}
--- /dev/null
+/*
+ * filter_deinterlace.c -- deinterlace filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#include <framework/mlt_filter.h>
+#include "deinterlace.h"
+
+#include <framework/mlt_frame.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ int error = 0;
+ int deinterlace = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "consumer_deinterlace" );
+
+ // Pop the service off the stack
+ mlt_filter filter = mlt_frame_pop_service( this );
+
+ // Determine if we need a writable version or not
+ if ( deinterlace && !writable )
+ writable = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" );
+
+ // Get the input image
+ error = mlt_frame_get_image( this, image, format, width, height, writable );
+
+ // Check that we want progressive and we aren't already progressive
+ if ( deinterlace && *format == mlt_image_yuv422 && *image != NULL && !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" ) )
+ {
+ // Determine deinterlace method
+ char *method_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "method" );
+ int method = DEINTERLACE_LINEARBLEND;
+ char *frame_method_str = mlt_properties_get( MLT_FRAME_PROPERTIES( this ), "deinterlace_method" );
+
+ if ( frame_method_str != NULL )
+ method_str = frame_method_str;
+
+ if ( method_str == NULL )
+ method = DEINTERLACE_LINEARBLEND;
+ else if ( strcmp( method_str, "bob" ) == 0 )
+ method = DEINTERLACE_BOB;
+ else if ( strcmp( method_str, "weave" ) == 0 )
+ method = DEINTERLACE_BOB;
+ else if ( strcmp( method_str, "greedy" ) == 0 )
+ method = DEINTERLACE_GREEDY;
+ else if ( strcmp( method_str, "onefield" ) == 0 )
+ method = DEINTERLACE_ONEFIELD;
+
+ // Deinterlace the image
+ deinterlace_yuv( *image, image, *width * 2, *height, method );
+
+ // Make sure that others know the frame is deinterlaced
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "progressive", 1 );
+ }
+
+ return error;
+}
+
+/** Deinterlace filter processing - this should be lazy evaluation here...
+*/
+
+static mlt_frame deinterlace_process( mlt_filter this, mlt_frame frame )
+{
+ // Push this on to the service stack
+ mlt_frame_push_service( frame, this );
+
+ // Push the get_image method on to the stack
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_deinterlace_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter this = mlt_filter_new( );
+ if ( this != NULL )
+ {
+ this->process = deinterlace_process;
+ mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "method", arg );
+ }
+ return this;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2000-2004 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id$
+ *
+ */
+#ifndef XINEUTILS_H
+#define XINEUTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <pthread.h>
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+//#ifdef XINE_COMPILE
+# include "attributes.h"
+//# include "compat.h"
+//# include "xmlparser.h"
+//# include "xine_buffer.h"
+//# include "configfile.h"
+//#else
+//# include <xine/attributes.h>
+//# include <xine/compat.h>
+//# include <xine/xmlparser.h>
+//# include <xine/xine_buffer.h>
+//# include <xine/configfile.h>
+//#endif
+
+//#ifdef HAVE_CONFIG_H
+//#include "config.h"
+//#endif
+
+#include <stdio.h>
+#include <string.h>
+
+ /*
+ * debugable mutexes
+ */
+
+ typedef struct {
+ pthread_mutex_t mutex;
+ char id[80];
+ char *locked_by;
+ } xine_mutex_t;
+
+ int xine_mutex_init (xine_mutex_t *mutex, const pthread_mutexattr_t *mutexattr,
+ char *id);
+
+ int xine_mutex_lock (xine_mutex_t *mutex, char *who);
+ int xine_mutex_unlock (xine_mutex_t *mutex, char *who);
+ int xine_mutex_destroy (xine_mutex_t *mutex);
+
+
+
+ /* CPU Acceleration */
+
+/*
+ * The type of an value that fits in an MMX register (note that long
+ * long constant values MUST be suffixed by LL and unsigned long long
+ * values by ULL, lest they be truncated by the compiler)
+ */
+
+/* generic accelerations */
+#define MM_ACCEL_MLIB 0x00000001
+
+/* x86 accelerations */
+#define MM_ACCEL_X86_MMX 0x80000000
+#define MM_ACCEL_X86_3DNOW 0x40000000
+#define MM_ACCEL_X86_MMXEXT 0x20000000
+#define MM_ACCEL_X86_SSE 0x10000000
+#define MM_ACCEL_X86_SSE2 0x08000000
+/* powerpc accelerations */
+#define MM_ACCEL_PPC_ALTIVEC 0x04000000
+/* x86 compat defines */
+#define MM_MMX MM_ACCEL_X86_MMX
+#define MM_3DNOW MM_ACCEL_X86_3DNOW
+#define MM_MMXEXT MM_ACCEL_X86_MMXEXT
+#define MM_SSE MM_ACCEL_X86_SSE
+#define MM_SSE2 MM_ACCEL_X86_SSE2
+
+uint32_t xine_mm_accel (void);
+
+#ifdef USE_MMX
+
+typedef union {
+ int64_t q; /* Quadword (64-bit) value */
+ uint64_t uq; /* Unsigned Quadword */
+ int d[2]; /* 2 Doubleword (32-bit) values */
+ unsigned int ud[2]; /* 2 Unsigned Doubleword */
+ short w[4]; /* 4 Word (16-bit) values */
+ unsigned short uw[4]; /* 4 Unsigned Word */
+ char b[8]; /* 8 Byte (8-bit) values */
+ unsigned char ub[8]; /* 8 Unsigned Byte */
+ float s[2]; /* Single-precision (32-bit) value */
+} ATTR_ALIGN(8) mmx_t; /* On an 8-byte (64-bit) boundary */
+
+
+
+#define mmx_i2r(op,imm,reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "i" (imm) )
+
+#define mmx_m2r(op,mem,reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "m" (mem))
+
+#define mmx_r2m(op,reg,mem) \
+ __asm__ __volatile__ (#op " %%" #reg ", %0" \
+ : "=m" (mem) \
+ : /* nothing */ )
+
+#define mmx_r2r(op,regs,regd) \
+ __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+
+#define emms() __asm__ __volatile__ ("emms")
+
+#define movd_m2r(var,reg) mmx_m2r (movd, var, reg)
+#define movd_r2m(reg,var) mmx_r2m (movd, reg, var)
+#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd)
+
+#define movq_m2r(var,reg) mmx_m2r (movq, var, reg)
+#define movq_r2m(reg,var) mmx_r2m (movq, reg, var)
+#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd)
+
+#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg)
+#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd)
+#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg)
+#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd)
+
+#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg)
+#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd)
+
+#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg)
+#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd)
+#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg)
+#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd)
+#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg)
+#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd)
+
+#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg)
+#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd)
+#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg)
+#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd)
+
+#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg)
+#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd)
+#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg)
+#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd)
+
+#define pand_m2r(var,reg) mmx_m2r (pand, var, reg)
+#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd)
+
+#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg)
+#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd)
+
+#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg)
+#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd)
+#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg)
+#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd)
+#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg)
+#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd)
+
+#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg)
+#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd)
+#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg)
+#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd)
+#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg)
+#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd)
+
+#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg)
+#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd)
+
+#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg)
+#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd)
+
+#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg)
+#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd)
+
+#define por_m2r(var,reg) mmx_m2r (por, var, reg)
+#define por_r2r(regs,regd) mmx_r2r (por, regs, regd)
+
+#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg)
+#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg)
+#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd)
+#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg)
+#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg)
+#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd)
+#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg)
+#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg)
+#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd)
+
+#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg)
+#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg)
+#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd)
+#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg)
+#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg)
+#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd)
+
+#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg)
+#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg)
+#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd)
+#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg)
+#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg)
+#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd)
+#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg)
+#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg)
+#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd)
+
+#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg)
+#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd)
+#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg)
+#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd)
+#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg)
+#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd)
+
+#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg)
+#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd)
+#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg)
+#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd)
+
+#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg)
+#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd)
+#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg)
+#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd)
+
+#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg)
+#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd)
+#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg)
+#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd)
+#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg)
+#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd)
+
+#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg)
+#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd)
+#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg)
+#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd)
+#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg)
+#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd)
+
+#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg)
+#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd)
+
+
+/* 3DNOW extensions */
+
+#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg)
+#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd)
+
+
+/* AMD MMX extensions - also available in intel SSE */
+
+
+#define mmx_m2ri(op,mem,reg,imm) \
+ __asm__ __volatile__ (#op " %1, %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (mem), "X" (imm))
+#define mmx_r2ri(op,regs,regd,imm) \
+ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+ : /* nothing */ \
+ : "X" (imm) )
+
+#define mmx_fetch(mem,hint) \
+ __asm__ __volatile__ ("prefetch" #hint " %0" \
+ : /* nothing */ \
+ : "X" (mem))
+
+
+#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg)
+
+#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var)
+
+#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg)
+#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd)
+#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg)
+#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd)
+
+#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm)
+
+#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm)
+
+#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg)
+#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd)
+
+#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg)
+#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd)
+
+#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg)
+#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd)
+
+#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg)
+#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd)
+
+#define pmovmskb(mmreg,reg) \
+ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
+
+#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg)
+#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd)
+
+#define prefetcht0(mem) mmx_fetch (mem, t0)
+#define prefetcht1(mem) mmx_fetch (mem, t1)
+#define prefetcht2(mem) mmx_fetch (mem, t2)
+#define prefetchnta(mem) mmx_fetch (mem, nta)
+
+#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg)
+#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd)
+
+#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm)
+#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm)
+
+#define sfence() __asm__ __volatile__ ("sfence\n\t")
+
+typedef union {
+ float sf[4]; /* Single-precision (32-bit) value */
+} ATTR_ALIGN(16) sse_t; /* On a 16 byte (128-bit) boundary */
+
+
+#define sse_i2r(op, imm, reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (imm) )
+
+#define sse_m2r(op, mem, reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (mem))
+
+#define sse_r2m(op, reg, mem) \
+ __asm__ __volatile__ (#op " %%" #reg ", %0" \
+ : "=X" (mem) \
+ : /* nothing */ )
+
+#define sse_r2r(op, regs, regd) \
+ __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+#define sse_r2ri(op, regs, regd, imm) \
+ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+ : /* nothing */ \
+ : "X" (imm) )
+
+#define sse_m2ri(op, mem, reg, subop) \
+ __asm__ __volatile__ (#op " %0, %%" #reg ", " #subop \
+ : /* nothing */ \
+ : "X" (mem))
+
+
+#define movaps_m2r(var, reg) sse_m2r(movaps, var, reg)
+#define movaps_r2m(reg, var) sse_r2m(movaps, reg, var)
+#define movaps_r2r(regs, regd) sse_r2r(movaps, regs, regd)
+
+#define movntps_r2m(xmmreg, var) sse_r2m(movntps, xmmreg, var)
+
+#define movups_m2r(var, reg) sse_m2r(movups, var, reg)
+#define movups_r2m(reg, var) sse_r2m(movups, reg, var)
+#define movups_r2r(regs, regd) sse_r2r(movups, regs, regd)
+
+#define movhlps_r2r(regs, regd) sse_r2r(movhlps, regs, regd)
+
+#define movlhps_r2r(regs, regd) sse_r2r(movlhps, regs, regd)
+
+#define movhps_m2r(var, reg) sse_m2r(movhps, var, reg)
+#define movhps_r2m(reg, var) sse_r2m(movhps, reg, var)
+
+#define movlps_m2r(var, reg) sse_m2r(movlps, var, reg)
+#define movlps_r2m(reg, var) sse_r2m(movlps, reg, var)
+
+#define movss_m2r(var, reg) sse_m2r(movss, var, reg)
+#define movss_r2m(reg, var) sse_r2m(movss, reg, var)
+#define movss_r2r(regs, regd) sse_r2r(movss, regs, regd)
+
+#define shufps_m2r(var, reg, index) sse_m2ri(shufps, var, reg, index)
+#define shufps_r2r(regs, regd, index) sse_r2ri(shufps, regs, regd, index)
+
+#define cvtpi2ps_m2r(var, xmmreg) sse_m2r(cvtpi2ps, var, xmmreg)
+#define cvtpi2ps_r2r(mmreg, xmmreg) sse_r2r(cvtpi2ps, mmreg, xmmreg)
+
+#define cvtps2pi_m2r(var, mmreg) sse_m2r(cvtps2pi, var, mmreg)
+#define cvtps2pi_r2r(xmmreg, mmreg) sse_r2r(cvtps2pi, mmreg, xmmreg)
+
+#define cvttps2pi_m2r(var, mmreg) sse_m2r(cvttps2pi, var, mmreg)
+#define cvttps2pi_r2r(xmmreg, mmreg) sse_r2r(cvttps2pi, mmreg, xmmreg)
+
+#define cvtsi2ss_m2r(var, xmmreg) sse_m2r(cvtsi2ss, var, xmmreg)
+#define cvtsi2ss_r2r(reg, xmmreg) sse_r2r(cvtsi2ss, reg, xmmreg)
+
+#define cvtss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg)
+#define cvtss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg)
+
+#define cvttss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg)
+#define cvttss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg)
+
+#define movmskps(xmmreg, reg) \
+ __asm__ __volatile__ ("movmskps %" #xmmreg ", %" #reg)
+
+#define addps_m2r(var, reg) sse_m2r(addps, var, reg)
+#define addps_r2r(regs, regd) sse_r2r(addps, regs, regd)
+
+#define addss_m2r(var, reg) sse_m2r(addss, var, reg)
+#define addss_r2r(regs, regd) sse_r2r(addss, regs, regd)
+
+#define subps_m2r(var, reg) sse_m2r(subps, var, reg)
+#define subps_r2r(regs, regd) sse_r2r(subps, regs, regd)
+
+#define subss_m2r(var, reg) sse_m2r(subss, var, reg)
+#define subss_r2r(regs, regd) sse_r2r(subss, regs, regd)
+
+#define mulps_m2r(var, reg) sse_m2r(mulps, var, reg)
+#define mulps_r2r(regs, regd) sse_r2r(mulps, regs, regd)
+
+#define mulss_m2r(var, reg) sse_m2r(mulss, var, reg)
+#define mulss_r2r(regs, regd) sse_r2r(mulss, regs, regd)
+
+#define divps_m2r(var, reg) sse_m2r(divps, var, reg)
+#define divps_r2r(regs, regd) sse_r2r(divps, regs, regd)
+
+#define divss_m2r(var, reg) sse_m2r(divss, var, reg)
+#define divss_r2r(regs, regd) sse_r2r(divss, regs, regd)
+
+#define rcpps_m2r(var, reg) sse_m2r(rcpps, var, reg)
+#define rcpps_r2r(regs, regd) sse_r2r(rcpps, regs, regd)
+
+#define rcpss_m2r(var, reg) sse_m2r(rcpss, var, reg)
+#define rcpss_r2r(regs, regd) sse_r2r(rcpss, regs, regd)
+
+#define rsqrtps_m2r(var, reg) sse_m2r(rsqrtps, var, reg)
+#define rsqrtps_r2r(regs, regd) sse_r2r(rsqrtps, regs, regd)
+
+#define rsqrtss_m2r(var, reg) sse_m2r(rsqrtss, var, reg)
+#define rsqrtss_r2r(regs, regd) sse_r2r(rsqrtss, regs, regd)
+
+#define sqrtps_m2r(var, reg) sse_m2r(sqrtps, var, reg)
+#define sqrtps_r2r(regs, regd) sse_r2r(sqrtps, regs, regd)
+
+#define sqrtss_m2r(var, reg) sse_m2r(sqrtss, var, reg)
+#define sqrtss_r2r(regs, regd) sse_r2r(sqrtss, regs, regd)
+
+#define andps_m2r(var, reg) sse_m2r(andps, var, reg)
+#define andps_r2r(regs, regd) sse_r2r(andps, regs, regd)
+
+#define andnps_m2r(var, reg) sse_m2r(andnps, var, reg)
+#define andnps_r2r(regs, regd) sse_r2r(andnps, regs, regd)
+
+#define orps_m2r(var, reg) sse_m2r(orps, var, reg)
+#define orps_r2r(regs, regd) sse_r2r(orps, regs, regd)
+
+#define xorps_m2r(var, reg) sse_m2r(xorps, var, reg)
+#define xorps_r2r(regs, regd) sse_r2r(xorps, regs, regd)
+
+#define maxps_m2r(var, reg) sse_m2r(maxps, var, reg)
+#define maxps_r2r(regs, regd) sse_r2r(maxps, regs, regd)
+
+#define maxss_m2r(var, reg) sse_m2r(maxss, var, reg)
+#define maxss_r2r(regs, regd) sse_r2r(maxss, regs, regd)
+
+#define minps_m2r(var, reg) sse_m2r(minps, var, reg)
+#define minps_r2r(regs, regd) sse_r2r(minps, regs, regd)
+
+#define minss_m2r(var, reg) sse_m2r(minss, var, reg)
+#define minss_r2r(regs, regd) sse_r2r(minss, regs, regd)
+
+#define cmpps_m2r(var, reg, op) sse_m2ri(cmpps, var, reg, op)
+#define cmpps_r2r(regs, regd, op) sse_r2ri(cmpps, regs, regd, op)
+
+#define cmpeqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 0)
+#define cmpeqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 0)
+
+#define cmpltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 1)
+#define cmpltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 1)
+
+#define cmpleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 2)
+#define cmpleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 2)
+
+#define cmpunordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 3)
+#define cmpunordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 3)
+
+#define cmpneqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 4)
+#define cmpneqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 4)
+
+#define cmpnltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 5)
+#define cmpnltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 5)
+
+#define cmpnleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 6)
+#define cmpnleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 6)
+
+#define cmpordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 7)
+#define cmpordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 7)
+
+#define cmpss_m2r(var, reg, op) sse_m2ri(cmpss, var, reg, op)
+#define cmpss_r2r(regs, regd, op) sse_r2ri(cmpss, regs, regd, op)
+
+#define cmpeqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 0)
+#define cmpeqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 0)
+
+#define cmpltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 1)
+#define cmpltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 1)
+
+#define cmpless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 2)
+#define cmpless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 2)
+
+#define cmpunordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 3)
+#define cmpunordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 3)
+
+#define cmpneqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 4)
+#define cmpneqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 4)
+
+#define cmpnltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 5)
+#define cmpnltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 5)
+
+#define cmpnless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 6)
+#define cmpnless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 6)
+
+#define cmpordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 7)
+#define cmpordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 7)
+
+#define comiss_m2r(var, reg) sse_m2r(comiss, var, reg)
+#define comiss_r2r(regs, regd) sse_r2r(comiss, regs, regd)
+
+#define ucomiss_m2r(var, reg) sse_m2r(ucomiss, var, reg)
+#define ucomiss_r2r(regs, regd) sse_r2r(ucomiss, regs, regd)
+
+#define unpcklps_m2r(var, reg) sse_m2r(unpcklps, var, reg)
+#define unpcklps_r2r(regs, regd) sse_r2r(unpcklps, regs, regd)
+
+#define unpckhps_m2r(var, reg) sse_m2r(unpckhps, var, reg)
+#define unpckhps_r2r(regs, regd) sse_r2r(unpckhps, regs, regd)
+
+#define fxrstor(mem) \
+ __asm__ __volatile__ ("fxrstor %0" \
+ : /* nothing */ \
+ : "X" (mem))
+
+#define fxsave(mem) \
+ __asm__ __volatile__ ("fxsave %0" \
+ : /* nothing */ \
+ : "X" (mem))
+
+#define stmxcsr(mem) \
+ __asm__ __volatile__ ("stmxcsr %0" \
+ : /* nothing */ \
+ : "X" (mem))
+
+#define ldmxcsr(mem) \
+ __asm__ __volatile__ ("ldmxcsr %0" \
+ : /* nothing */ \
+ : "X" (mem))
+#endif /* USE_MMX */
+
+
+
+ /* Optimized/fast memcpy */
+
+/*
+ TODO : fix dll linkage problem for xine_fast_memcpy on win32
+
+ xine_fast_memcpy dll linkage is screwy here.
+ declaring as dllimport seems to fix the problem
+ but causes compiler warning with libxineutils
+*/
+#ifdef _MSC_VER
+__declspec( dllimport ) extern void *(* xine_fast_memcpy)(void *to, const void *from, size_t len);
+#else
+extern void *(* xine_fast_memcpy)(void *to, const void *from, size_t len);
+#endif
+
+#ifdef HAVE_XINE_INTERNAL_H
+/* Benchmark available memcpy methods */
+void xine_probe_fast_memcpy(xine_t *xine);
+#endif
+
+
+/*
+ * Debug stuff
+ */
+/*
+ * profiling (unworkable in non DEBUG isn't defined)
+ */
+void xine_profiler_init (void);
+int xine_profiler_allocate_slot (char *label);
+void xine_profiler_start_count (int id);
+void xine_profiler_stop_count (int id);
+void xine_profiler_print_results (void);
+
+/*
+ * Allocate and clean memory size_t 'size', then return the pointer
+ * to the allocated memory.
+ */
+#if !defined(__GNUC__) || __GNUC__ < 3
+void *xine_xmalloc(size_t size);
+#else
+void *xine_xmalloc(size_t size) __attribute__ ((__malloc__));
+#endif
+
+/*
+ * Same as above, but memory is aligned to 'alignement'.
+ * **base is used to return pointer to un-aligned memory, use
+ * this to free the mem chunk
+ */
+void *xine_xmalloc_aligned(size_t alignment, size_t size, void **base);
+
+/*
+ * Get user home directory.
+ */
+const char *xine_get_homedir(void);
+
+/*
+ * Clean a string (remove spaces and '=' at the begin,
+ * and '\n', '\r' and spaces at the end.
+ */
+char *xine_chomp (char *str);
+
+/*
+ * A thread-safe usecond sleep
+ */
+void xine_usec_sleep(unsigned usec);
+
+
+ /*
+ * Some string functions
+ */
+
+
+void xine_strdupa(char *dest, char *src);
+#define xine_strdupa(d, s) do { \
+ (d) = NULL; \
+ if((s) != NULL) { \
+ (d) = (char *) alloca(strlen((s)) + 1); \
+ strcpy((d), (s)); \
+ } \
+ } while(0)
+
+/* Shamefully copied from glibc 2.2.3 */
+#ifdef HAVE_STRPBRK
+#define xine_strpbrk strpbrk
+#else
+static inline const char *_private_strpbrk(const char *s, const char *accept) {
+
+ while(*s != '\0') {
+ const char *a = accept;
+ while(*a != '\0')
+ if(*a++ == *s)
+ return s;
+ ++s;
+ }
+
+ return NULL;
+}
+#define xine_strpbrk _private_strpbrk
+#endif
+
+#if defined HAVE_STRSEP && !defined(_MSC_VER)
+#define xine_strsep strsep
+#else
+static inline char *_private_strsep(char **stringp, const char *delim) {
+ char *begin, *end;
+
+ begin = *stringp;
+ if(begin == NULL)
+ return NULL;
+
+ if(delim[0] == '\0' || delim[1] == '\0') {
+ char ch = delim[0];
+
+ if(ch == '\0')
+ end = NULL;
+ else {
+ if(*begin == ch)
+ end = begin;
+ else if(*begin == '\0')
+ end = NULL;
+ else
+ end = strchr(begin + 1, ch);
+ }
+ }
+ else
+ end = xine_strpbrk(begin, delim);
+
+ if(end) {
+ *end++ = '\0';
+ *stringp = end;
+ }
+ else
+ *stringp = NULL;
+
+ return begin;
+}
+#define xine_strsep _private_strsep
+#endif
+
+
+#ifdef HAVE_SETENV
+#define xine_setenv setenv
+#else
+static inline void _private_setenv(const char *name, const char *val, int _xx) {
+ int len = strlen(name) + strlen(val) + 2;
+ char env[len];
+
+ sprintf(env, "%s%c%s", name, '=', val);
+ putenv(env);
+}
+#define xine_setenv _private_setenv
+#endif
+
+/*
+ * Color Conversion Utility Functions
+ * The following data structures and functions facilitate the conversion
+ * of RGB images to packed YUV (YUY2) images. There are also functions to
+ * convert from YUV9 -> YV12. All of the meaty details are written in
+ * color.c.
+ */
+
+typedef struct yuv_planes_s {
+
+ unsigned char *y;
+ unsigned char *u;
+ unsigned char *v;
+ unsigned int row_width; /* frame width */
+ unsigned int row_count; /* frame height */
+
+} yuv_planes_t;
+
+void init_yuv_conversion(void);
+void init_yuv_planes(yuv_planes_t *yuv_planes, int width, int height);
+void free_yuv_planes(yuv_planes_t *yuv_planes);
+
+extern void (*yuv444_to_yuy2)
+ (yuv_planes_t *yuv_planes, unsigned char *yuy2_map, int pitch);
+extern void (*yuv9_to_yv12)
+ (unsigned char *y_src, int y_src_pitch, unsigned char *y_dest, int y_dest_pitch,
+ unsigned char *u_src, int u_src_pitch, unsigned char *u_dest, int u_dest_pitch,
+ unsigned char *v_src, int v_src_pitch, unsigned char *v_dest, int v_dest_pitch,
+ int width, int height);
+extern void (*yuv411_to_yv12)
+ (unsigned char *y_src, int y_src_pitch, unsigned char *y_dest, int y_dest_pitch,
+ unsigned char *u_src, int u_src_pitch, unsigned char *u_dest, int u_dest_pitch,
+ unsigned char *v_src, int v_src_pitch, unsigned char *v_dest, int v_dest_pitch,
+ int width, int height);
+extern void (*yv12_to_yuy2)
+ (unsigned char *y_src, int y_src_pitch,
+ unsigned char *u_src, int u_src_pitch,
+ unsigned char *v_src, int v_src_pitch,
+ unsigned char *yuy2_map, int yuy2_pitch,
+ int width, int height, int progressive);
+extern void (*yuy2_to_yv12)
+ (unsigned char *yuy2_map, int yuy2_pitch,
+ unsigned char *y_dst, int y_dst_pitch,
+ unsigned char *u_dst, int u_dst_pitch,
+ unsigned char *v_dst, int v_dst_pitch,
+ int width, int height);
+
+#define SCALEFACTOR 65536
+#define CENTERSAMPLE 128
+
+#define COMPUTE_Y(r, g, b) \
+ (unsigned char) \
+ ((y_r_table[r] + y_g_table[g] + y_b_table[b]) / SCALEFACTOR)
+#define COMPUTE_U(r, g, b) \
+ (unsigned char) \
+ ((u_r_table[r] + u_g_table[g] + u_b_table[b]) / SCALEFACTOR + CENTERSAMPLE)
+#define COMPUTE_V(r, g, b) \
+ (unsigned char) \
+ ((v_r_table[r] + v_g_table[g] + v_b_table[b]) / SCALEFACTOR + CENTERSAMPLE)
+
+#define UNPACK_BGR15(packed_pixel, r, g, b) \
+ b = (packed_pixel & 0x7C00) >> 7; \
+ g = (packed_pixel & 0x03E0) >> 2; \
+ r = (packed_pixel & 0x001F) << 3;
+
+#define UNPACK_BGR16(packed_pixel, r, g, b) \
+ b = (packed_pixel & 0xF800) >> 8; \
+ g = (packed_pixel & 0x07E0) >> 3; \
+ r = (packed_pixel & 0x001F) << 3;
+
+#define UNPACK_RGB15(packed_pixel, r, g, b) \
+ r = (packed_pixel & 0x7C00) >> 7; \
+ g = (packed_pixel & 0x03E0) >> 2; \
+ b = (packed_pixel & 0x001F) << 3;
+
+#define UNPACK_RGB16(packed_pixel, r, g, b) \
+ r = (packed_pixel & 0xF800) >> 8; \
+ g = (packed_pixel & 0x07E0) >> 3; \
+ b = (packed_pixel & 0x001F) << 3;
+
+extern int y_r_table[256];
+extern int y_g_table[256];
+extern int y_b_table[256];
+
+extern int u_r_table[256];
+extern int u_g_table[256];
+extern int u_b_table[256];
+
+extern int v_r_table[256];
+extern int v_g_table[256];
+extern int v_b_table[256];
+
+/* frame copying functions */
+extern void yv12_to_yv12
+ (unsigned char *y_src, int y_src_pitch, unsigned char *y_dst, int y_dst_pitch,
+ unsigned char *u_src, int u_src_pitch, unsigned char *u_dst, int u_dst_pitch,
+ unsigned char *v_src, int v_src_pitch, unsigned char *v_dst, int v_dst_pitch,
+ int width, int height);
+extern void yuy2_to_yuy2
+ (unsigned char *src, int src_pitch,
+ unsigned char *dst, int dst_pitch,
+ int width, int height);
+
+/* print a hexdump of the given data */
+void xine_hexdump (const char *buf, int length);
+
+/*
+ * Optimization macros for conditions
+ * Taken from the FIASCO L4 microkernel sources
+ */
+#if !defined(__GNUC__) || __GNUC__ < 3
+# define EXPECT_TRUE(x) (x)
+# define EXPECT_FALSE(x) (x)
+#else
+# define EXPECT_TRUE(x) __builtin_expect((x),1)
+# define EXPECT_FALSE(x) __builtin_expect((x),0)
+#endif
+
+#ifdef NDEBUG
+#define _x_assert(exp) \
+ do { \
+ if (!(exp)) \
+ fprintf(stderr, "assert: %s:%d: %s: Assertion `%s' failed.\n", \
+ __FILE__, __LINE__, __XINE_FUNCTION__, #exp); \
+ } while(0)
+#else
+#define _x_assert(exp) \
+ do { \
+ if (!(exp)) { \
+ fprintf(stderr, "assert: %s:%d: %s: Assertion `%s' failed.\n", \
+ __FILE__, __LINE__, __XINE_FUNCTION__, #exp); \
+ abort(); \
+ } \
+ } while(0)
+#endif
+
+#define _x_abort() \
+ do { \
+ fprintf(stderr, "abort: %s:%d: %s: Aborting.\n", \
+ __FILE__, __LINE__, __XINE_FUNCTION__); \
+ abort(); \
+ } while(0)
+
+
+/****** logging with xine **********************************/
+
+#ifndef LOG_MODULE
+ #define LOG_MODULE __FILE__
+#endif /* LOG_MODULE */
+
+#define LOG_MODULE_STRING printf("%s: ", LOG_MODULE );
+
+#ifdef LOG_VERBOSE
+ #define LONG_LOG_MODULE_STRING \
+ printf("%s: (%s:%d) ", LOG_MODULE, __XINE_FUNCTION__, __LINE__ );
+#else
+ #define LONG_LOG_MODULE_STRING LOG_MODULE_STRING
+#endif /* LOG_VERBOSE */
+
+#ifdef LOG
+ #ifdef __GNUC__
+ #define lprintf(fmt, args...) \
+ do { \
+ LONG_LOG_MODULE_STRING \
+ printf(fmt, ##args); \
+ } while(0)
+ #else /* __GNUC__ */
+ #ifdef _MSC_VER
+ #define lprintf(fmtargs) \
+ do { \
+ LONG_LOG_MODULE_STRING \
+ printf("%s", fmtargs); \
+ } while(0)
+ #else /* _MSC_VER */
+ #define lprintf(fmt, ...) \
+ do { \
+ LONG_LOG_MODULE_STRING \
+ printf(__VA_ARGS__); \
+ } while(0)
+ #endif /* _MSC_VER */
+ #endif /* __GNUC__ */
+#else /* LOG */
+ #ifdef __GNUC__
+ #define lprintf(fmt, args...) do {} while(0)
+ #else
+ #ifdef _MSC_VER
+ #define lprintf
+ #else
+ #define lprintf(...) do {} while(0)
+ #endif /* _MSC_VER */
+ #endif /* __GNUC__ */
+#endif /* LOG */
+
+#ifdef __GNUC__
+ #define llprintf(cat, fmt, args...) \
+ do{ \
+ if(cat){ \
+ LONG_LOG_MODULE_STRING \
+ printf( fmt, ##args ); \
+ } \
+ }while(0)
+#else
+#ifdef _MSC_VER
+ #define llprintf(cat, fmtargs) \
+ do{ \
+ if(cat){ \
+ LONG_LOG_MODULE_STRING \
+ printf( "%s", fmtargs ); \
+ } \
+ }while(0)
+#else
+ #define llprintf(cat, ...) \
+ do{ \
+ if(cat){ \
+ LONG_LOG_MODULE_STRING \
+ printf( __VA_ARGS__ ); \
+ } \
+ }while(0)
+#endif /* _MSC_VER */
+#endif /* __GNUC__ */
+
+#ifdef __GNUC__
+ #define xprintf(xine, verbose, fmt, args...) \
+ do { \
+ if((xine) && (xine)->verbosity >= verbose){ \
+ xine_log(xine, XINE_LOG_TRACE, fmt, ##args); \
+ } \
+ } while(0)
+#else
+#ifdef _MSC_VER
+ #define xprintf(xine, verbose, fmtargs) \
+ do { \
+ if((xine) && (xine)->verbosity >= verbose){ \
+ xine_log(xine, XINE_LOG_TRACE, fmtargs); \
+ } \
+ } while(0)
+#else
+ #define xprintf(xine, verbose, ...) \
+ do { \
+ if((xine) && (xine)->verbosity >= verbose){ \
+ xine_log(xine, XINE_LOG_TRACE, __VA_ARGS__); \
+ } \
+ } while(0)
+#endif /* _MSC_VER */
+#endif /* __GNUC__ */
+
+/* time measuring macros for profiling tasks */
+
+#ifdef DEBUG
+# define XINE_PROFILE(function) \
+ do { \
+ struct timeval current_time; \
+ double dtime; \
+ gettimeofday(¤t_time, NULL); \
+ dtime = -(current_time.tv_sec + (current_time.tv_usec / 1000000.0)); \
+ function; \
+ gettimeofday(¤t_time, NULL); \
+ dtime += current_time.tv_sec + (current_time.tv_usec / 1000000.0); \
+ printf("%s: (%s:%d) took %lf seconds\n", \
+ LOG_MODULE, __XINE_FUNCTION__, __LINE__, dtime); \
+ } while(0)
+# define XINE_PROFILE_ACCUMULATE(function) \
+ do { \
+ struct timeval current_time; \
+ static double dtime = 0; \
+ gettimeofday(¤t_time, NULL); \
+ dtime -= current_time.tv_sec + (current_time.tv_usec / 1000000.0); \
+ function; \
+ gettimeofday(¤t_time, NULL); \
+ dtime += current_time.tv_sec + (current_time.tv_usec / 1000000.0); \
+ printf("%s: (%s:%d) took %lf seconds\n", \
+ LOG_MODULE, __XINE_FUNCTION__, __LINE__, dtime); \
+ } while(0)
+#else
+# define XINE_PROFILE(function) function
+# define XINE_PROFILE_ACCUMULATE(function) function
+#endif /* LOG */
+
+
+/******** double chained lists with builtin iterator *******/
+
+typedef struct xine_node_s {
+
+ struct xine_node_s *next, *prev;
+
+ void *content;
+
+ int priority;
+
+} xine_node_t;
+
+
+typedef struct {
+
+ xine_node_t *first, *last, *cur;
+
+} xine_list_t;
+
+
+
+xine_list_t *xine_list_new (void);
+
+
+/**
+ * dispose the whole list.
+ * note: disposes _only_ the list structure, content must be free()d elsewhere
+ */
+void xine_list_free(xine_list_t *l);
+
+
+/**
+ * returns: Boolean
+ */
+int xine_list_is_empty (xine_list_t *l);
+
+/**
+ * return content of first entry in list.
+ */
+void *xine_list_first_content (xine_list_t *l);
+
+/**
+ * return next content in list.
+ */
+void *xine_list_next_content (xine_list_t *l);
+
+/**
+ * Return last content of list.
+ */
+void *xine_list_last_content (xine_list_t *l);
+
+/**
+ * Return previous content of list.
+ */
+void *xine_list_prev_content (xine_list_t *l);
+
+/**
+ * Append content to list, sorted by decreasing priority.
+ */
+void xine_list_append_priority_content (xine_list_t *l, void *content, int priority);
+
+/**
+ * Append content to list.
+ */
+void xine_list_append_content (xine_list_t *l, void *content);
+
+/**
+ * Insert content in list.
+ */
+void xine_list_insert_content (xine_list_t *l, void *content);
+
+/**
+ * Remove current content in list.
+ * note: removes only the list entry; content must be free()d elsewhere.
+ */
+void xine_list_delete_current (xine_list_t *l);
+
+#ifndef HAVE_BASENAME
+/*
+ * get base name
+ */
+char *basename (char const *name);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+include ../../config.mak
+
+TARGET = dan charlie pango pixbuf dissolve luma
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../modules
+LDFLAGS += -L../framework -lmlt
+
+all: $(TARGET)
+
+hello: hello.o
+ $(CC) hello.o -o $@ -L../framework -L../modules -lmlt
+
+pango: pango.o
+ $(CC) pango.o -o $@ $(LDFLAGS)
+
+pixbuf: pixbuf.o
+ $(CC) pixbuf.o -o $@ $(LDFLAGS)
+
+dissolve: dissolve.o
+ $(CC) dissolve.o -o $@ $(LDFLAGS)
+
+luma: luma.o
+ $(CC) luma.o -o $@ $(LDFLAGS)
+
+dan: dan.o
+ $(CC) dan.o -o $@ $(LDFLAGS)
+
+charlie: charlie.o io.o
+ $(CC) charlie.o io.o -o $@ $(LDFLAGS)
+
+clean:
+ rm -f dan.o io.o charlie.o dan charlie
+
+depend: dan.c charlie.c io.c
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <framework/mlt.h>
+
+#include "io.h"
+
+mlt_producer create_producer( char *file )
+{
+ mlt_producer result = NULL;
+
+ // 1st Line preferences
+ if ( strstr( file, ".inigo" ) )
+ {
+ char *args[ 2 ] = { file, NULL };
+ result = mlt_factory_producer( "inigo", args );
+ }
+ else if ( strstr( file, ".mpg" ) )
+ result = mlt_factory_producer( "mcmpeg", file );
+ else if ( strstr( file, ".mpeg" ) )
+ result = mlt_factory_producer( "mcmpeg", file );
+ else if ( strstr( file, ".dat" ) )
+ result = mlt_factory_producer( "mcmpeg", file );
+ else if ( strstr( file, ".dv" ) )
+ result = mlt_factory_producer( "mcdv", file );
+ else if ( strstr( file, ".dif" ) )
+ result = mlt_factory_producer( "mcdv", file );
+ else if ( strstr( file, ".jpg" ) )
+ result = mlt_factory_producer( "pixbuf", file );
+ else if ( strstr( file, ".JPG" ) )
+ result = mlt_factory_producer( "pixbuf", file );
+ else if ( strstr( file, ".jpeg" ) )
+ result = mlt_factory_producer( "pixbuf", file );
+ else if ( strstr( file, ".png" ) )
+ result = mlt_factory_producer( "pixbuf", file );
+
+ // 2nd Line fallbacks
+ if ( result == NULL && strstr( file, ".dv" ) )
+ result = mlt_factory_producer( "libdv", file );
+ else if ( result == NULL && strstr( file, ".dif" ) )
+ result = mlt_factory_producer( "libdv", file );
+
+ return result;
+}
+
+void transport_action( mlt_producer producer, char *value )
+{
+ mlt_properties properties = mlt_producer_properties( producer );
+
+ switch( value[ 0 ] )
+ {
+ case 'q':
+ mlt_properties_set_int( properties, "done", 1 );
+ break;
+ case '0':
+ mlt_producer_set_speed( producer, 1 );
+ mlt_producer_seek( producer, 0 );
+ break;
+ case '1':
+ mlt_producer_set_speed( producer, -5 );
+ break;
+ case '2':
+ mlt_producer_set_speed( producer, -2.5 );
+ break;
+ case '3':
+ mlt_producer_set_speed( producer, -1 );
+ break;
+ case '4':
+ mlt_producer_set_speed( producer, -0.5 );
+ break;
+ case '5':
+ mlt_producer_set_speed( producer, 0 );
+ break;
+ case '6':
+ mlt_producer_set_speed( producer, 0.5 );
+ break;
+ case '7':
+ mlt_producer_set_speed( producer, 1 );
+ break;
+ case '8':
+ mlt_producer_set_speed( producer, 2.5 );
+ break;
+ case '9':
+ mlt_producer_set_speed( producer, 5 );
+ break;
+ }
+}
+
+mlt_consumer create_consumer( char *id, mlt_producer producer )
+{
+ char *arg = strchr( id, ':' );
+ if ( arg != NULL )
+ *arg ++ = '\0';
+ mlt_consumer consumer = mlt_factory_consumer( id, arg );
+ if ( consumer != NULL )
+ {
+ mlt_properties properties = mlt_consumer_properties( consumer );
+ mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
+ mlt_properties_set_data( properties, "transport_producer", producer, 0, NULL, NULL );
+ }
+ return consumer;
+}
+
+void track_service( mlt_field field, void *service, mlt_destructor destructor )
+{
+ mlt_properties properties = mlt_field_properties( field );
+ int registered = mlt_properties_get_int( properties, "registered" );
+ char *key = mlt_properties_get( properties, "registered" );
+ mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
+ mlt_properties_set_int( properties, "registered", ++ registered );
+}
+
+void set_properties( mlt_service service, char *namevalue )
+{
+ mlt_properties properties = mlt_service_properties( service );
+ mlt_properties_parse( properties, namevalue );
+}
+
+void transport( mlt_producer producer )
+{
+ mlt_properties properties = mlt_producer_properties( producer );
+
+ term_init( );
+ fprintf( stderr, "Press 'q' to continue\n" );
+ while( mlt_properties_get_int( properties, "done" ) == 0 )
+ {
+ int value = term_read( );
+ if ( value != -1 )
+ transport_action( producer, ( char * )&value );
+ }
+}
+
+int main( int argc, char **argv )
+{
+ int i;
+ mlt_service service = NULL;
+ mlt_consumer consumer = NULL;
+ mlt_producer producer = NULL;
+ mlt_playlist playlist = NULL;
+
+ // Construct the factory
+ mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
+
+ // Set up containers
+ playlist = mlt_playlist_init( );
+
+ // Parse the arguments
+ for ( i = 1; i < argc; i ++ )
+ {
+ if ( !strcmp( argv[ i ], "-consumer" ) )
+ {
+ consumer = create_consumer( argv[ ++ i ], mlt_playlist_producer( playlist ) );
+ if ( consumer != NULL )
+ service = mlt_consumer_service( consumer );
+ }
+ else if ( !strstr( argv[ i ], "=" ) )
+ {
+ if ( producer != NULL )
+ mlt_playlist_append( playlist, producer );
+ producer = create_producer( argv[ i ] );
+ if ( producer != NULL )
+ service = mlt_producer_service( producer );
+ }
+ else
+ {
+ set_properties( service, argv[ i ] );
+ }
+ }
+
+ // If we have no consumer, default to sdl
+ if ( consumer == NULL )
+ consumer = create_consumer( "sdl", mlt_playlist_producer( playlist ) );
+
+ // Connect producer to playlist
+ if ( producer != NULL )
+ mlt_playlist_append( playlist, producer );
+
+ // Connect consumer to playlist
+ mlt_consumer_connect( consumer, mlt_playlist_service( playlist ) );
+
+ // Transport functionality
+ transport( mlt_playlist_producer( playlist ) );
+
+ // Close the services
+ mlt_consumer_close( consumer );
+ mlt_playlist_close( playlist );
+
+ // Close the factory
+ mlt_factory_close( );
+
+ return 0;
+}
--- /dev/null
+
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+
+int main( int argc, char **argv )
+{
+ mlt_properties p = mlt_properties_parse_yaml( argv[1] );
+ mlt_properties q = mlt_properties_new();
+ mlt_properties_set_data( q, "metadata", p, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml );
+ printf( "%s", mlt_properties_get( q, "metadata" ) );
+ mlt_properties_close( q );
+
+ mlt_repository repo = mlt_factory_init( NULL );
+ mlt_properties metadata = mlt_repository_metadata( repo, producer_type, "avformat" );
+ if ( metadata )
+ {
+ char *s = mlt_properties_serialise_yaml( metadata );
+ printf( "%s", s );
+ free( s );
+ }
+ mlt_factory_close();
+ return 0;
+}
--- /dev/null
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+ char temp[ 132 ];
+ char *file1 = NULL;
+ char *file2 = NULL;
+
+ mlt_factory_init( "../modules" );
+
+ if ( argc < 3 )
+ {
+ fprintf( stderr, "usage: dissolve file1.mpeg file2.mpeg\n" );
+ return 1;
+ }
+ else
+ {
+ file1 = argv[ 1 ];
+ file2 = argv[ 2 ];
+ }
+
+ // Start the consumer...
+ mlt_consumer consumer = mlt_factory_consumer( "sdl", "PAL" );
+
+ // Create the producer(s)
+ mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+ mlt_producer dv2 = mlt_factory_producer( "mcmpeg", file2 );
+
+ mlt_playlist playlist1 = mlt_playlist_init();
+ mlt_playlist_append_io( playlist1, dv1, 0.0, 5.0 );
+
+ mlt_playlist playlist2 = mlt_playlist_init();
+ mlt_playlist_blank( playlist2, 2.9 );
+ mlt_playlist_append( playlist2, dv2 );
+
+ // Register producers(s) with a multitrack object
+ mlt_multitrack multitrack = mlt_multitrack_init( );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist1 ), 0 );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist2 ), 1 );
+
+ // Define a transition
+ mlt_transition transition = mlt_factory_transition( "luma", NULL );
+ mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+ mlt_transition_set_in_and_out( transition, 3.0, 5.0 );
+
+ // Buy a tractor and connect it to the filter
+ mlt_tractor tractor = mlt_tractor_init( );
+ mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+ // Connect the tractor to the consumer
+ mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+ // Do stuff until we're told otherwise...
+ fprintf( stderr, "Press return to continue\n" );
+ fgets( temp, 132, stdin );
+
+ // Close everything...
+ mlt_consumer_close( consumer );
+ mlt_tractor_close( tractor );
+ mlt_transition_close( transition );
+ mlt_multitrack_close( multitrack );
+ mlt_playlist_close( playlist1 );
+ mlt_playlist_close( playlist2 );
+ mlt_producer_close( dv1 );
+ mlt_producer_close( dv2 );
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <framework/mlt.h>
+
+mlt_producer create_playlist( int argc, char **argv )
+{
+ // We're creating a playlist here
+ mlt_playlist playlist = mlt_playlist_init( );
+
+ // We need the playlist properties to ensure clean up
+ mlt_properties properties = mlt_playlist_properties( playlist );
+
+ // Loop through each of the arguments
+ int i = 0;
+ for ( i = 1; i < argc; i ++ )
+ {
+ // Definie the unique key
+ char key[ 256 ];
+
+ // Create the producer
+ mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );
+
+ // Add it to the playlist
+ mlt_playlist_append( playlist, producer );
+
+ // Create a unique key for this producer
+ sprintf( key, "producer%d", i );
+
+ // Now we need to ensure the producers are destroyed
+ mlt_properties_set_data( properties, key, producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ }
+
+ // Return the playlist as a producer
+ return mlt_playlist_producer( playlist );
+}
+
+mlt_producer create_tracks( int argc, char **argv )
+{
+ // Create the field
+ mlt_field field = mlt_field_init( );
+
+ // Obtain the multitrack
+ mlt_multitrack multitrack = mlt_field_multitrack( field );
+
+ // Obtain the tractor
+ mlt_tractor tractor = mlt_field_tractor( field );
+
+ // Obtain a composite transition
+ mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" );
+
+ // Create track 0
+ mlt_producer track0 = create_playlist( argc, argv );
+
+ // Get the length of track0
+ mlt_position length = mlt_producer_get_playtime( track0 );
+
+ // Create the watermark track
+ mlt_producer track1 = mlt_factory_producer( "fezzik", "pango:" );
+
+ // Get the properties of track1
+ mlt_properties properties = mlt_producer_properties( track1 );
+
+ // Set the properties
+ mlt_properties_set( properties, "text", "Hello\nWorld" );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", length - 1 );
+ mlt_properties_set_position( properties, "length", length );
+
+ // Now set the properties on the transition
+ properties = mlt_transition_properties( transition );
+ mlt_properties_set_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", length - 1 );
+
+ // Add our tracks to the multitrack
+ mlt_multitrack_connect( multitrack, track0, 0 );
+ mlt_multitrack_connect( multitrack, track1, 1 );
+
+ // Now plant the transition
+ mlt_field_plant_transition( field, transition, 0, 1 );
+
+ // Now set the properties on the transition
+ properties = mlt_tractor_properties( tractor );
+
+ // Ensure clean up and set properties correctly
+ mlt_properties_set_data( properties, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
+ mlt_properties_set_data( properties, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );
+ mlt_properties_set_data( properties, "track0", track0, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ mlt_properties_set_data( properties, "track1", track1, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ mlt_properties_set_data( properties, "transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
+ mlt_properties_set_position( properties, "length", length );
+ mlt_properties_set_position( properties, "out", length - 1 );
+
+ // Return the tractor
+ return mlt_tractor_producer( tractor );
+}
+
+int main( int argc, char **argv )
+{
+ // Initialise the factory
+ if ( mlt_factory_init( NULL ) == 0 )
+ {
+ // Create the default consumer
+ mlt_consumer hello = mlt_factory_consumer( NULL, NULL );
+
+ // Create a producer using the default normalising selecter
+ mlt_producer world = create_tracks( argc, argv );
+
+ // Connect the producer to the consumer
+ mlt_consumer_connect( hello, mlt_producer_service( world ) );
+
+ // Start the consumer
+ mlt_consumer_start( hello );
+
+ // Wait for the consumer to terminate
+ while( !mlt_consumer_is_stopped( hello ) )
+ sleep( 1 );
+
+ // Close the consumer
+ mlt_consumer_close( hello );
+
+ // Close the producer
+ mlt_producer_close( world );
+
+ // Close the factory
+ mlt_factory_close( );
+ }
+ else
+ {
+ // Report an error during initialisation
+ fprintf( stderr, "Unable to locate factory modules\n" );
+ }
+
+ // End of program
+ return 0;
+}
+
--- /dev/null
+/*
+ * io.c -- dv1394d client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+
+/* Application header files */
+#include "io.h"
+
+char *chomp( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ if ( length && input[ length - 1 ] == '\n' )
+ input[ length - 1 ] = '\0';
+ if ( length > 1 && input[ length - 2 ] == '\r' )
+ input[ length - 2 ] = '\0';
+ }
+ return input;
+}
+
+char *trim( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ int first = 0;
+ while( first < length && isspace( input[ first ] ) )
+ first ++;
+ memmove( input, input + first, length - first + 1 );
+ length = length - first;
+ while ( length > 0 && isspace( input[ length - 1 ] ) )
+ input[ -- length ] = '\0';
+ }
+ return input;
+}
+
+char *strip_quotes( char *input )
+{
+ if ( input != NULL )
+ {
+ char *ptr = strrchr( input, '\"' );
+ if ( ptr != NULL )
+ *ptr = '\0';
+ if ( input[ 0 ] == '\"' )
+ strcpy( input, input + 1 );
+ }
+ return input;
+}
+
+char *get_string( char *output, int maxlength, char *use )
+{
+ char *value = NULL;
+ strcpy( output, use );
+ if ( trim( chomp( fgets( output, maxlength, stdin ) ) ) != NULL )
+ {
+ if ( !strcmp( output, "" ) )
+ strcpy( output, use );
+ value = output;
+ }
+ return value;
+}
+
+int *get_int( int *output, int use )
+{
+ int *value = NULL;
+ char temp[ 132 ];
+ *output = use;
+ if ( trim( chomp( fgets( temp, 132, stdin ) ) ) != NULL )
+ {
+ if ( strcmp( temp, "" ) )
+ *output = atoi( temp );
+ value = output;
+ }
+ return value;
+}
+
+/** This stores the previous settings
+*/
+
+static struct termios oldtty;
+static int mode = 0;
+
+/** This is called automatically on application exit to restore the
+ previous tty settings.
+*/
+
+void term_exit(void)
+{
+ if ( mode == 1 )
+ {
+ tcsetattr( 0, TCSANOW, &oldtty );
+ mode = 0;
+ }
+}
+
+/** Init terminal so that we can grab keys without blocking.
+*/
+
+void term_init( )
+{
+ struct termios tty;
+
+ tcgetattr( 0, &tty );
+ oldtty = tty;
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[ VMIN ] = 1;
+ tty.c_cc[ VTIME ] = 0;
+
+ tcsetattr( 0, TCSANOW, &tty );
+
+ mode = 1;
+
+ atexit( term_exit );
+}
+
+/** Check for a keypress without blocking infinitely.
+ Returns: ASCII value of keypress or -1 if no keypress detected.
+*/
+
+int term_read( )
+{
+ int n = 1;
+ unsigned char ch;
+ struct timeval tv;
+ fd_set rfds;
+
+ FD_ZERO( &rfds );
+ FD_SET( 0, &rfds );
+ tv.tv_sec = 0;
+ tv.tv_usec = 250;
+ n = select( 1, &rfds, NULL, NULL, &tv );
+ if (n > 0)
+ {
+ n = read( 0, &ch, 1 );
+ tcflush( 0, TCIFLUSH );
+ if (n == 1)
+ return ch;
+ return n;
+ }
+ return -1;
+}
+
+char get_keypress( )
+{
+ char value = '\0';
+ int pressed = 0;
+
+ fflush( stdout );
+
+ term_init( );
+ while ( ( pressed = term_read( ) ) == -1 ) ;
+ term_exit( );
+
+ value = (char)pressed;
+
+ return value;
+}
+
+void wait_for_any_key( char *message )
+{
+ if ( message == NULL )
+ printf( "Press any key to continue: " );
+ else
+ printf( "%s", message );
+
+ get_keypress( );
+
+ printf( "\n\n" );
+}
+
+void beep( )
+{
+ printf( "%c", 7 );
+ fflush( stdout );
+}
--- /dev/null
+/*
+ * io.h -- dv1394d client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_IO_H_
+#define _DEMO_IO_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *chomp( char * );
+extern char *trim( char * );
+extern char *strip_quotes( char * );
+extern char *get_string( char *, int, char * );
+extern int *get_int( int *, int );
+extern void term_init( );
+extern int term_read( );
+extern void term_exit( );
+extern char get_keypress( );
+extern void wait_for_any_key( char * );
+extern void beep( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+ char temp[ 132 ];
+ char *file1 = NULL;
+ char *file2 = NULL;
+ char *wipe = NULL;
+
+ mlt_factory_init( "../modules" );
+
+ if ( argc < 4 )
+ {
+ fprintf( stderr, "usage: luma file1.mpeg file2.mpeg wipe.pgm\n" );
+ return 1;
+ }
+ else
+ {
+ file1 = argv[ 1 ];
+ file2 = argv[ 2 ];
+ wipe = argv[ 3 ];
+ }
+
+ // Start the consumer...
+ mlt_consumer consumer = mlt_factory_consumer( "bluefish", "NTSC" );
+
+ // Create the producer(s)
+ mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+ mlt_producer dv2 = mlt_factory_producer( "mcmpeg", file2 );
+
+ mlt_playlist playlist1 = mlt_playlist_init();
+ mlt_playlist_append_io( playlist1, dv1, 0.0, 5.0 );
+
+ mlt_playlist playlist2 = mlt_playlist_init();
+ mlt_playlist_blank( playlist2, 2.9 );
+ mlt_playlist_append( playlist2, dv2 );
+
+ // Register producers(s) with a multitrack object
+ mlt_multitrack multitrack = mlt_multitrack_init( );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist1 ), 0 );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist2 ), 1 );
+
+ // Define a transition
+ mlt_transition transition = mlt_factory_transition( "luma", wipe );
+ mlt_properties_set( mlt_transition_properties( transition ), "filename", wipe );
+ mlt_properties_set_double( mlt_transition_properties( transition ), "softness", 0.1 );
+ mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+ mlt_transition_set_in_and_out( transition, 3.0, 5.0 );
+
+ // Buy a tractor and connect it to the filter
+ mlt_tractor tractor = mlt_tractor_init( );
+ mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+ // Connect the tractor to the consumer
+ mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+ // Do stuff until we're told otherwise...
+ fprintf( stderr, "Press return to continue\n" );
+ fgets( temp, 132, stdin );
+
+ // Close everything...
+ mlt_consumer_close( consumer );
+ mlt_tractor_close( tractor );
+ mlt_transition_close( transition );
+ mlt_multitrack_close( multitrack );
+ mlt_playlist_close( playlist1 );
+ mlt_playlist_close( playlist2 );
+ mlt_producer_close( dv1 );
+ mlt_producer_close( dv2 );
+
+ return 0;
+}
--- /dev/null
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+ char temp[ 132 ];
+ char *file1 = NULL;
+ char *text = NULL;
+
+ mlt_factory_init( "../modules" );
+
+ if ( argc < 3 )
+ {
+ fprintf( stderr, "usage: pango file.mpeg text_to_display\n" );
+ return 1;
+ }
+ else
+ {
+ file1 = argv[ 1 ];
+ text = argv[ 2 ];
+ }
+
+ // Start the consumer...
+ mlt_consumer consumer = mlt_factory_consumer( "bluefish", "NTSC" );
+
+ // Create the producer(s)
+ mlt_playlist pl1 = mlt_playlist_init();
+ mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+ mlt_playlist_append( pl1, dv1 );
+
+ mlt_playlist pl2 = mlt_playlist_init();
+ mlt_producer title = mlt_factory_producer( "pango", NULL ); //"<span font_desc=\"Sans Bold 36\">Mutton <span font_desc=\"Luxi Serif Bold Oblique 36\">Lettuce</span> Tomato</span>" );
+ mlt_playlist_append( pl2, title );
+ mlt_properties_set( mlt_producer_properties( title ), "font", "Sans Bold 36" );
+ mlt_properties_set( mlt_producer_properties( title ), "text", text );
+ mlt_properties_set_int( mlt_producer_properties( title ), "bgcolor", 0x0000007f );
+ mlt_properties_set_int( mlt_producer_properties( title ), "pad", 8 );
+ mlt_properties_set_int( mlt_producer_properties( title ), "align", 1 );
+ mlt_properties_set_int( mlt_producer_properties( title ), "x", 200 );
+ mlt_properties_set_int( mlt_producer_properties( title ), "y", 40 );
+ mlt_properties_set_double( mlt_producer_properties( title ), "mix", 0.8 );
+
+ // Register producers(s) with a multitrack object
+ mlt_multitrack multitrack = mlt_multitrack_init( );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl1 ), 0 );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl2 ), 1 );
+
+ // Define a transition
+ mlt_transition transition = mlt_factory_transition( "composite", NULL );
+ mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+ mlt_transition_set_in_and_out( transition, 0.0, 9999.0 );
+
+ // Buy a tractor and connect it to the filter
+ mlt_tractor tractor = mlt_tractor_init( );
+ mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+ // Connect the tractor to the consumer
+ mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+ // Do stuff until we're told otherwise...
+ fprintf( stderr, "Press return to continue\n" );
+ fgets( temp, 132, stdin );
+
+ // Close everything...
+ mlt_consumer_close( consumer );
+ mlt_tractor_close( tractor );
+ mlt_transition_close( transition );
+ mlt_multitrack_close( multitrack );
+ mlt_playlist_close( pl1 );
+ mlt_playlist_close( pl2 );
+ mlt_producer_close( dv1 );
+ mlt_producer_close( title );
+
+ return 0;
+}
--- /dev/null
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+ char temp[ 132 ];
+ char *file1 = NULL;
+ char *file2 = NULL;
+
+ mlt_factory_init( "../modules" );
+
+ if ( argc < 3 )
+ {
+ fprintf( stderr, "usage: pixbuf file.mpeg file.{png,jpg,etc}\n" );
+ return 1;
+ }
+ else
+ {
+ file1 = argv[ 1 ];
+ file2 = argv[ 2 ];
+ }
+
+ // Start the consumer...
+ mlt_consumer consumer = mlt_factory_consumer( "sdl", "PAL" );
+
+ // Create the producer(s)
+ mlt_playlist pl1 = mlt_playlist_init();
+ mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+ mlt_playlist_append( pl1, dv1 );
+
+ mlt_playlist pl2 = mlt_playlist_init();
+ mlt_producer overlay = mlt_factory_producer( "pixbuf", file2 );
+ mlt_playlist_append( pl2, overlay );
+ mlt_properties_set_int( mlt_producer_properties( overlay ), "x", 600 );
+ mlt_properties_set_int( mlt_producer_properties( overlay ), "y", 460 );
+ mlt_properties_set_double( mlt_producer_properties( overlay ), "mix", 0.8 );
+
+ // Register producers(s) with a multitrack object
+ mlt_multitrack multitrack = mlt_multitrack_init( );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl1 ), 0 );
+ mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl2 ), 1 );
+
+ // Define a transition
+ mlt_transition transition = mlt_factory_transition( "composite", NULL );
+ mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+ mlt_transition_set_in_and_out( transition, 0.0, 9999.0 );
+
+ // Buy a tractor and connect it to the filter
+ mlt_tractor tractor = mlt_tractor_init( );
+ mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+ // Connect the tractor to the consumer
+ mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+ // Do stuff until we're told otherwise...
+ fprintf( stderr, "Press return to continue\n" );
+ fgets( temp, 132, stdin );
+
+ // Close everything...
+ mlt_consumer_close( consumer );
+ mlt_tractor_close( tractor );
+ mlt_transition_close( transition );
+ mlt_multitrack_close( multitrack );
+ mlt_playlist_close( pl1 );
+ mlt_playlist_close( pl2 );
+ mlt_producer_close( dv1 );
+ mlt_producer_close( overlay );
+
+ return 0;
+}
--- /dev/null
+export MLT_REPOSITORY=`pwd`/../modules
+
+export LD_LIBRARY_PATH=`pwd`/../framework:\
+`pwd`/../modules/bluefish:\
+`pwd`/../../../bluefish/lib:\
+`pwd`/../../../mpeg_sdk_demo/bin:\
+`pwd`/../../../dv_sdk
--- /dev/null
+include ../../config.mak
+
+ifneq ($(targetos), Darwin)
+NAME = libvalerie$(LIBSUF)
+TARGET = $(NAME).$(version)
+SONAME = $(NAME).$(soversion)
+SHFLAGS += -Wl,-soname,$(SONAME)
+else
+NAME = libvalerie$(LIBSUF)
+TARGET = libvalerie.$(version)$(LIBSUF)
+SONAME = libvalerie.$(soversion)$(LIBSUF)
+SHFLAGS += -install_name $(libdir)/$(SONAME) -current_version $(version) -compatibility_version $(soversion)
+endif
+
+OBJS = valerie.o \
+ valerie_notifier.o \
+ valerie_parser.o \
+ valerie_response.o \
+ valerie_status.o \
+ valerie_tokeniser.o \
+ valerie_util.o \
+ valerie_remote.o \
+ valerie_socket.o
+
+INCS = valerie.h \
+ valerie_notifier.h \
+ valerie_parser.h \
+ valerie_remote.h \
+ valerie_response.h \
+ valerie_socket.h \
+ valerie_status.h \
+ valerie_tokeniser.h \
+ valerie_util.h
+
+SRCS := $(OBJS:.o=.c)
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../framework -lmlt -lpthread
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+ ln -sf $(TARGET) $(NAME)
+ ln -sf $(TARGET) $(SONAME)
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) $(TARGET) $(NAME)
+
+install: all
+ install -m 755 $(TARGET) $(DESTDIR)$(libdir)
+ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(SONAME)
+ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(NAME)
+ mkdir -p "$(DESTDIR)$(prefix)/include/mlt/valerie"
+ install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/valerie"
+
+uninstall:
+ rm -f "$(DESTDIR)$(libdir)/$(TARGET)"
+ rm -f "$(DESTDIR)$(libdir)/$(SONAME)"
+ rm -f "$(DESTDIR)$(libdir)/$(NAME)"
+ rm -rf "$(DESTDIR)$(prefix)/include/mlt/valerie"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+#!/bin/sh
+echo "valerie -I$prefix/include/mlt -D_REENTRANT -L$libdir -lvalerie" >> ../../packages.dat
--- /dev/null
+/*
+ * valerie.c -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Application header files */
+#include "valerie.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Initialise the valerie structure.
+*/
+
+valerie valerie_init( valerie_parser parser )
+{
+ valerie this = malloc( sizeof( valerie_t ) );
+ if ( this != NULL )
+ {
+ memset( this, 0, sizeof( valerie_t ) );
+ this->parser = parser;
+ }
+ return this;
+}
+
+/** Set the response structure associated to the last command.
+*/
+
+static void valerie_set_last_response( valerie this, valerie_response response )
+{
+ if ( this != NULL )
+ {
+ if ( this->last_response != NULL )
+ valerie_response_close( this->last_response );
+ this->last_response = response;
+ }
+}
+
+/** Connect to the parser.
+*/
+
+valerie_error_code valerie_connect( valerie this )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ valerie_response response = valerie_parser_connect( this->parser );
+ if ( response != NULL )
+ {
+ valerie_set_last_response( this, response );
+ if ( valerie_response_get_error_code( response ) == 100 )
+ error = valerie_ok;
+ }
+ return error;
+}
+
+/** Interpret a non-context sensitive error code.
+*/
+
+static valerie_error_code valerie_get_error_code( valerie this, valerie_response response )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ switch( valerie_response_get_error_code( response ) )
+ {
+ case -1:
+ error = valerie_server_unavailable;
+ break;
+ case -2:
+ error = valerie_no_response;
+ break;
+ case 200:
+ case 201:
+ case 202:
+ error = valerie_ok;
+ break;
+ case 400:
+ error = valerie_invalid_command;
+ break;
+ case 401:
+ error = valerie_server_timeout;
+ break;
+ case 402:
+ error = valerie_missing_argument;
+ break;
+ case 403:
+ error = valerie_unit_unavailable;
+ break;
+ case 404:
+ error = valerie_invalid_file;
+ break;
+ default:
+ case 500:
+ error = valerie_unknown_error;
+ break;
+ }
+ return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_execute( valerie this, size_t size, const char *format, ... )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ char *command = malloc( size );
+ if ( this != NULL && command != NULL )
+ {
+ va_list list;
+ va_start( list, format );
+ if ( vsnprintf( command, size, format, list ) != 0 )
+ {
+ valerie_response response = valerie_parser_execute( this->parser, command );
+ valerie_set_last_response( this, response );
+ error = valerie_get_error_code( this, response );
+ }
+ else
+ {
+ error = valerie_invalid_command;
+ }
+ va_end( list );
+ }
+ else
+ {
+ error = valerie_malloc_failed;
+ }
+ free( command );
+ return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_receive( valerie this, char *doc, size_t size, const char *format, ... )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ char *command = malloc( size );
+ if ( this != NULL && command != NULL )
+ {
+ va_list list;
+ va_start( list, format );
+ if ( vsnprintf( command, size, format, list ) != 0 )
+ {
+ valerie_response response = valerie_parser_received( this->parser, command, doc );
+ valerie_set_last_response( this, response );
+ error = valerie_get_error_code( this, response );
+ }
+ else
+ {
+ error = valerie_invalid_command;
+ }
+ va_end( list );
+ }
+ else
+ {
+ error = valerie_malloc_failed;
+ }
+ free( command );
+ return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_push( valerie this, mlt_service service, size_t size, const char *format, ... )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ char *command = malloc( size );
+ if ( this != NULL && command != NULL )
+ {
+ va_list list;
+ va_start( list, format );
+ if ( vsnprintf( command, size, format, list ) != 0 )
+ {
+ valerie_response response = valerie_parser_push( this->parser, command, service );
+ valerie_set_last_response( this, response );
+ error = valerie_get_error_code( this, response );
+ }
+ else
+ {
+ error = valerie_invalid_command;
+ }
+ va_end( list );
+ }
+ else
+ {
+ error = valerie_malloc_failed;
+ }
+ free( command );
+ return error;
+}
+
+/** Set a global property.
+*/
+
+valerie_error_code valerie_set( valerie this, char *property, char *value )
+{
+ return valerie_execute( this, 1024, "SET %s=%s", property, value );
+}
+
+/** Get a global property.
+*/
+
+valerie_error_code valerie_get( valerie this, char *property, char *value, int length )
+{
+ valerie_error_code error = valerie_execute( this, 1024, "GET %s", property );
+ if ( error == valerie_ok )
+ {
+ valerie_response response = valerie_get_last_response( this );
+ strncpy( value, valerie_response_get_line( response, 1 ), length );
+ }
+ return error;
+}
+
+/** Run a script.
+*/
+
+valerie_error_code valerie_run( valerie this, char *file )
+{
+ return valerie_execute( this, 10240, "RUN \"%s\"", file );
+}
+
+/** Add a unit.
+*/
+
+valerie_error_code valerie_unit_add( valerie this, char *guid, int *unit )
+{
+ valerie_error_code error = valerie_execute( this, 1024, "UADD %s", guid );
+ if ( error == valerie_ok )
+ {
+ int length = valerie_response_count( this->last_response );
+ char *line = valerie_response_get_line( this->last_response, length - 1 );
+ if ( line == NULL || sscanf( line, "U%d", unit ) != 1 )
+ error = valerie_unit_creation_failed;
+ }
+ else
+ {
+ if ( error == valerie_unknown_error )
+ error = valerie_unit_creation_failed;
+ }
+ return error;
+}
+
+/** Load a file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_load( valerie this, int unit, char *file )
+{
+ return valerie_execute( this, 10240, "LOAD U%d \"%s\"", unit, file );
+}
+
+static void valerie_interpret_clip_offset( char *output, valerie_clip_offset offset, int clip )
+{
+ switch( offset )
+ {
+ case valerie_absolute:
+ sprintf( output, "%d", clip );
+ break;
+ case valerie_relative:
+ if ( clip < 0 )
+ sprintf( output, "%d", clip );
+ else
+ sprintf( output, "+%d", clip );
+ break;
+ }
+}
+
+/** Load a file on the specified unit with the specified in/out points.
+*/
+
+valerie_error_code valerie_unit_load_clipped( valerie this, int unit, char *file, int32_t in, int32_t out )
+{
+ return valerie_execute( this, 10240, "LOAD U%d \"%s\" %d %d", unit, file, in, out );
+}
+
+/** Load a file on the specified unit at the end of the current pump.
+*/
+
+valerie_error_code valerie_unit_load_back( valerie this, int unit, char *file )
+{
+ return valerie_execute( this, 10240, "LOAD U%d \"!%s\"", unit, file );
+}
+
+/** Load a file on the specified unit at the end of the pump with the specified in/out points.
+*/
+
+valerie_error_code valerie_unit_load_back_clipped( valerie this, int unit, char *file, int32_t in, int32_t out )
+{
+ return valerie_execute( this, 10240, "LOAD U%d \"!%s\" %d %d", unit, file, in, out );
+}
+
+/** Append a file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_append( valerie this, int unit, char *file, int32_t in, int32_t out )
+{
+ return valerie_execute( this, 10240, "APND U%d \"%s\" %d %d", unit, file, in, out );
+}
+
+/** Push a service on to a unit.
+*/
+
+valerie_error_code valerie_unit_receive( valerie this, int unit, char *command, char *doc )
+{
+ return valerie_receive( this, doc, 10240, "PUSH U%d %s", unit, command );
+}
+
+/** Push a service on to a unit.
+*/
+
+valerie_error_code valerie_unit_push( valerie this, int unit, char *command, mlt_service service )
+{
+ return valerie_push( this, service, 10240, "PUSH U%d %s", unit, command );
+}
+
+/** Clean the unit - this function removes all but the currently playing clip.
+*/
+
+valerie_error_code valerie_unit_clean( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "CLEAN U%d", unit );
+}
+
+/** Clear the unit - this function removes all clips.
+*/
+
+valerie_error_code valerie_unit_clear( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "CLEAR U%d", unit );
+}
+
+/** Wipe the unit - this function removes all clips before the current one.
+*/
+
+valerie_error_code valerie_unit_wipe( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "WIPE U%d", unit );
+}
+
+/** Move clips on the units playlist.
+*/
+
+valerie_error_code valerie_unit_clip_move( valerie this, int unit, valerie_clip_offset src_offset, int src, valerie_clip_offset dest_offset, int dest )
+{
+ char temp1[ 100 ];
+ char temp2[ 100 ];
+ valerie_interpret_clip_offset( temp1, src_offset, src );
+ valerie_interpret_clip_offset( temp2, dest_offset, dest );
+ return valerie_execute( this, 1024, "MOVE U%d %s %s", unit, temp1, temp2 );
+}
+
+/** Remove clip at the specified position.
+*/
+
+valerie_error_code valerie_unit_clip_remove( valerie this, int unit, valerie_clip_offset offset, int clip )
+{
+ char temp[ 100 ];
+ valerie_interpret_clip_offset( temp, offset, clip );
+ return valerie_execute( this, 1024, "REMOVE U%d %s", unit, temp );
+}
+
+/** Remove the currently playing clip.
+*/
+
+valerie_error_code valerie_unit_remove_current_clip( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "REMOVE U%d", unit );
+}
+
+/** Insert clip at the specified position.
+*/
+
+valerie_error_code valerie_unit_clip_insert( valerie this, int unit, valerie_clip_offset offset, int clip, char *file, int32_t in, int32_t out )
+{
+ char temp[ 100 ];
+ valerie_interpret_clip_offset( temp, offset, clip );
+ return valerie_execute( this, 1024, "INSERT U%d \"%s\" %s %d %d", unit, file, temp, in, out );
+}
+
+/** Play the unit at normal speed.
+*/
+
+valerie_error_code valerie_unit_play( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "PLAY U%d 1000", unit );
+}
+
+/** Play the unit at specified speed.
+*/
+
+valerie_error_code valerie_unit_play_at_speed( valerie this, int unit, int speed )
+{
+ return valerie_execute( this, 10240, "PLAY U%d %d", unit, speed );
+}
+
+/** Stop playback on the specified unit.
+*/
+
+valerie_error_code valerie_unit_stop( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "STOP U%d", unit );
+}
+
+/** Pause playback on the specified unit.
+*/
+
+valerie_error_code valerie_unit_pause( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "PAUSE U%d", unit );
+}
+
+/** Rewind the specified unit.
+*/
+
+valerie_error_code valerie_unit_rewind( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "REW U%d", unit );
+}
+
+/** Fast forward the specified unit.
+*/
+
+valerie_error_code valerie_unit_fast_forward( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "FF U%d", unit );
+}
+
+/** Step by the number of frames on the specified unit.
+*/
+
+valerie_error_code valerie_unit_step( valerie this, int unit, int32_t step )
+{
+ return valerie_execute( this, 1024, "STEP U%d %d", unit, step );
+}
+
+/** Goto the specified frame on the specified unit.
+*/
+
+valerie_error_code valerie_unit_goto( valerie this, int unit, int32_t position )
+{
+ return valerie_execute( this, 1024, "GOTO U%d %d", unit, position );
+}
+
+/** Goto the specified frame in the clip on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clip_goto( valerie this, int unit, valerie_clip_offset offset, int clip, int32_t position )
+{
+ char temp[ 100 ];
+ valerie_interpret_clip_offset( temp, offset, clip );
+ return valerie_execute( this, 1024, "GOTO U%d %d %s", unit, position, temp );
+}
+
+/** Set the in point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_set_in( valerie this, int unit, int32_t in )
+{
+ return valerie_execute( this, 1024, "SIN U%d %d", unit, in );
+}
+
+/** Set the in point of the clip on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clip_set_in( valerie this, int unit, valerie_clip_offset offset, int clip, int32_t in )
+{
+ char temp[ 100 ];
+ valerie_interpret_clip_offset( temp, offset, clip );
+ return valerie_execute( this, 1024, "SIN U%d %d %s", unit, in, temp );
+}
+
+/** Set the out point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_set_out( valerie this, int unit, int32_t out )
+{
+ return valerie_execute( this, 1024, "SOUT U%d %d", unit, out );
+}
+
+/** Set the out point of the clip on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clip_set_out( valerie this, int unit, valerie_clip_offset offset, int clip, int32_t in )
+{
+ char temp[ 100 ];
+ valerie_interpret_clip_offset( temp, offset, clip );
+ return valerie_execute( this, 1024, "SOUT U%d %d %s", unit, in, temp );
+}
+
+/** Clear the in point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clear_in( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "SIN U%d -1", unit );
+}
+
+/** Clear the out point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clear_out( valerie this, int unit )
+{
+ return valerie_execute( this, 1024, "SOUT U%d -1", unit );
+}
+
+/** Clear the in and out points on the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clear_in_out( valerie this, int unit )
+{
+ valerie_error_code error = valerie_unit_clear_out( this, unit );
+ if ( error == valerie_ok )
+ error = valerie_unit_clear_in( this, unit );
+ return error;
+}
+
+/** Set a unit configuration property.
+*/
+
+valerie_error_code valerie_unit_set( valerie this, int unit, const char *name, const char *value )
+{
+ return valerie_execute( this, 1024, "USET U%d %s=%s", unit, name, value );
+}
+
+/** Get a unit configuration property.
+*/
+
+valerie_error_code valerie_unit_get( valerie this, int unit, char *name )
+{
+ return valerie_execute( this, 1024, "UGET U%d %s", unit, name );
+}
+
+/** Get a units status.
+*/
+
+valerie_error_code valerie_unit_status( valerie this, int unit, valerie_status status )
+{
+ valerie_error_code error = valerie_execute( this, 1024, "USTA U%d", unit );
+ int error_code = valerie_response_get_error_code( this->last_response );
+
+ memset( status, 0, sizeof( valerie_status_t ) );
+ status->unit = unit;
+ if ( error_code == 202 && valerie_response_count( this->last_response ) == 2 )
+ valerie_status_parse( status, valerie_response_get_line( this->last_response, 1 ) );
+ else if ( error_code == 403 )
+ status->status = unit_undefined;
+
+ return error;
+}
+
+/** Transfer the current settings of unit src to unit dest.
+*/
+
+valerie_error_code valerie_unit_transfer( valerie this, int src, int dest )
+{
+ return valerie_execute( this, 1024, "XFER U%d U%d", src, dest );
+}
+
+/** Obtain the parsers notifier.
+*/
+
+valerie_notifier valerie_get_notifier( valerie this )
+{
+ if ( this != NULL )
+ return valerie_parser_get_notifier( this->parser );
+ else
+ return NULL;
+}
+
+/** List the contents of the specified directory.
+*/
+
+valerie_dir valerie_dir_init( valerie this, const char *directory )
+{
+ valerie_dir dir = malloc( sizeof( valerie_dir_t ) );
+ if ( dir != NULL )
+ {
+ memset( dir, 0, sizeof( valerie_dir_t ) );
+ dir->directory = strdup( directory );
+ dir->response = valerie_parser_executef( this->parser, "CLS \"%s\"", directory );
+ }
+ return dir;
+}
+
+/** Return the error code associated to the dir.
+*/
+
+valerie_error_code valerie_dir_get_error_code( valerie_dir dir )
+{
+ if ( dir != NULL )
+ return valerie_get_error_code( NULL, dir->response );
+ else
+ return valerie_malloc_failed;
+}
+
+/** Get a particular file entry in the directory.
+*/
+
+valerie_error_code valerie_dir_get( valerie_dir dir, int index, valerie_dir_entry entry )
+{
+ valerie_error_code error = valerie_ok;
+ memset( entry, 0, sizeof( valerie_dir_entry_t ) );
+ if ( index < valerie_dir_count( dir ) )
+ {
+ char *line = valerie_response_get_line( dir->response, index + 1 );
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+ if ( valerie_tokeniser_count( tokeniser ) > 0 )
+ {
+ valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 0 ), '\"' );
+ strcpy( entry->full, dir->directory );
+ if ( entry->full[ strlen( entry->full ) - 1 ] != '/' )
+ strcat( entry->full, "/" );
+ strcpy( entry->name, valerie_tokeniser_get_string( tokeniser, 0 ) );
+ strcat( entry->full, entry->name );
+
+ switch ( valerie_tokeniser_count( tokeniser ) )
+ {
+ case 1:
+ entry->dir = 1;
+ break;
+ case 2:
+ entry->size = strtoull( valerie_tokeniser_get_string( tokeniser, 1 ), NULL, 10 );
+ break;
+ default:
+ error = valerie_invalid_file;
+ break;
+ }
+ }
+ valerie_tokeniser_close( tokeniser );
+ }
+ return error;
+}
+
+/** Get the number of entries in the directory
+*/
+
+int valerie_dir_count( valerie_dir dir )
+{
+ if ( dir != NULL && valerie_response_count( dir->response ) >= 2 )
+ return valerie_response_count( dir->response ) - 2;
+ else
+ return -1;
+}
+
+/** Close the directory structure.
+*/
+
+void valerie_dir_close( valerie_dir dir )
+{
+ if ( dir != NULL )
+ {
+ free( dir->directory );
+ valerie_response_close( dir->response );
+ free( dir );
+ }
+}
+
+/** List the playlist of the specified unit.
+*/
+
+valerie_list valerie_list_init( valerie this, int unit )
+{
+ valerie_list list = calloc( 1, sizeof( valerie_list_t ) );
+ if ( list != NULL )
+ {
+ list->response = valerie_parser_executef( this->parser, "LIST U%d", unit );
+ if ( valerie_response_count( list->response ) >= 2 )
+ list->generation = atoi( valerie_response_get_line( list->response, 1 ) );
+ }
+ return list;
+}
+
+/** Return the error code associated to the list.
+*/
+
+valerie_error_code valerie_list_get_error_code( valerie_list list )
+{
+ if ( list != NULL )
+ return valerie_get_error_code( NULL, list->response );
+ else
+ return valerie_malloc_failed;
+}
+
+/** Get a particular file entry in the list.
+*/
+
+valerie_error_code valerie_list_get( valerie_list list, int index, valerie_list_entry entry )
+{
+ valerie_error_code error = valerie_ok;
+ memset( entry, 0, sizeof( valerie_list_entry_t ) );
+ if ( index < valerie_list_count( list ) )
+ {
+ char *line = valerie_response_get_line( list->response, index + 2 );
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+ if ( valerie_tokeniser_count( tokeniser ) > 0 )
+ {
+ entry->clip = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+ valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 1 ), '\"' );
+ strcpy( entry->full, valerie_tokeniser_get_string( tokeniser, 1 ) );
+ entry->in = atol( valerie_tokeniser_get_string( tokeniser, 2 ) );
+ entry->out = atol( valerie_tokeniser_get_string( tokeniser, 3 ) );
+ entry->max = atol( valerie_tokeniser_get_string( tokeniser, 4 ) );
+ entry->size = atol( valerie_tokeniser_get_string( tokeniser, 5 ) );
+ entry->fps = atof( valerie_tokeniser_get_string( tokeniser, 6 ) );
+ }
+ valerie_tokeniser_close( tokeniser );
+ }
+ return error;
+}
+
+/** Get the number of entries in the list
+*/
+
+int valerie_list_count( valerie_list list )
+{
+ if ( list != NULL && valerie_response_count( list->response ) >= 3 )
+ return valerie_response_count( list->response ) - 3;
+ else
+ return -1;
+}
+
+/** Close the list structure.
+*/
+
+void valerie_list_close( valerie_list list )
+{
+ if ( list != NULL )
+ {
+ valerie_response_close( list->response );
+ free( list );
+ }
+}
+
+/** List the currently connected nodes.
+*/
+
+valerie_nodes valerie_nodes_init( valerie this )
+{
+ valerie_nodes nodes = malloc( sizeof( valerie_nodes_t ) );
+ if ( nodes != NULL )
+ {
+ memset( nodes, 0, sizeof( valerie_nodes_t ) );
+ nodes->response = valerie_parser_executef( this->parser, "NLS" );
+ }
+ return nodes;
+}
+
+/** Return the error code associated to the nodes list.
+*/
+
+valerie_error_code valerie_nodes_get_error_code( valerie_nodes nodes )
+{
+ if ( nodes != NULL )
+ return valerie_get_error_code( NULL, nodes->response );
+ else
+ return valerie_malloc_failed;
+}
+
+/** Get a particular node entry.
+*/
+
+valerie_error_code valerie_nodes_get( valerie_nodes nodes, int index, valerie_node_entry entry )
+{
+ valerie_error_code error = valerie_ok;
+ memset( entry, 0, sizeof( valerie_node_entry_t ) );
+ if ( index < valerie_nodes_count( nodes ) )
+ {
+ char *line = valerie_response_get_line( nodes->response, index + 1 );
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+ if ( valerie_tokeniser_count( tokeniser ) == 3 )
+ {
+ entry->node = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+ strncpy( entry->guid, valerie_tokeniser_get_string( tokeniser, 1 ), sizeof( entry->guid ) );
+ valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' );
+ strncpy( entry->name, valerie_tokeniser_get_string( tokeniser, 2 ), sizeof( entry->name ) );
+ }
+
+ valerie_tokeniser_close( tokeniser );
+ }
+ return error;
+}
+
+/** Get the number of nodes
+*/
+
+int valerie_nodes_count( valerie_nodes nodes )
+{
+ if ( nodes != NULL && valerie_response_count( nodes->response ) >= 2 )
+ return valerie_response_count( nodes->response ) - 2;
+ else
+ return -1;
+}
+
+/** Close the nodes structure.
+*/
+
+void valerie_nodes_close( valerie_nodes nodes )
+{
+ if ( nodes != NULL )
+ {
+ valerie_response_close( nodes->response );
+ free( nodes );
+ }
+}
+
+/** List the currently defined units.
+*/
+
+valerie_units valerie_units_init( valerie this )
+{
+ valerie_units units = malloc( sizeof( valerie_units_t ) );
+ if ( units != NULL )
+ {
+ memset( units, 0, sizeof( valerie_units_t ) );
+ units->response = valerie_parser_executef( this->parser, "ULS" );
+ }
+ return units;
+}
+
+/** Return the error code associated to the nodes list.
+*/
+
+valerie_error_code valerie_units_get_error_code( valerie_units units )
+{
+ if ( units != NULL )
+ return valerie_get_error_code( NULL, units->response );
+ else
+ return valerie_malloc_failed;
+}
+
+/** Get a particular unit entry.
+*/
+
+valerie_error_code valerie_units_get( valerie_units units, int index, valerie_unit_entry entry )
+{
+ valerie_error_code error = valerie_ok;
+ memset( entry, 0, sizeof( valerie_unit_entry_t ) );
+ if ( index < valerie_units_count( units ) )
+ {
+ char *line = valerie_response_get_line( units->response, index + 1 );
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+ if ( valerie_tokeniser_count( tokeniser ) == 4 )
+ {
+ entry->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) + 1 );
+ entry->node = atoi( valerie_tokeniser_get_string( tokeniser, 1 ) );
+ strncpy( entry->guid, valerie_tokeniser_get_string( tokeniser, 2 ), sizeof( entry->guid ) );
+ entry->online = atoi( valerie_tokeniser_get_string( tokeniser, 3 ) );
+ }
+
+ valerie_tokeniser_close( tokeniser );
+ }
+ return error;
+}
+
+/** Get the number of units
+*/
+
+int valerie_units_count( valerie_units units )
+{
+ if ( units != NULL && valerie_response_count( units->response ) >= 2 )
+ return valerie_response_count( units->response ) - 2;
+ else
+ return -1;
+}
+
+/** Close the units structure.
+*/
+
+void valerie_units_close( valerie_units units )
+{
+ if ( units != NULL )
+ {
+ valerie_response_close( units->response );
+ free( units );
+ }
+}
+
+/** Get the response of the last command executed.
+*/
+
+valerie_response valerie_get_last_response( valerie this )
+{
+ return this->last_response;
+}
+
+/** Obtain a printable message associated to the error code provided.
+*/
+
+const char *valerie_error_description( valerie_error_code error )
+{
+ const char *msg = "Unrecognised error";
+ switch( error )
+ {
+ case valerie_ok:
+ msg = "OK";
+ break;
+ case valerie_malloc_failed:
+ msg = "Memory allocation error";
+ break;
+ case valerie_unknown_error:
+ msg = "Unknown error";
+ break;
+ case valerie_no_response:
+ msg = "No response obtained";
+ break;
+ case valerie_invalid_command:
+ msg = "Invalid command";
+ break;
+ case valerie_server_timeout:
+ msg = "Communications with server timed out";
+ break;
+ case valerie_missing_argument:
+ msg = "Missing argument";
+ break;
+ case valerie_server_unavailable:
+ msg = "Unable to communicate with server";
+ break;
+ case valerie_unit_creation_failed:
+ msg = "Unit creation failed";
+ break;
+ case valerie_unit_unavailable:
+ msg = "Unit unavailable";
+ break;
+ case valerie_invalid_file:
+ msg = "Invalid file";
+ break;
+ case valerie_invalid_position:
+ msg = "Invalid position";
+ break;
+ }
+ return msg;
+}
+
+/** Close the valerie structure.
+*/
+
+void valerie_close( valerie this )
+{
+ if ( this != NULL )
+ {
+ valerie_set_last_response( this, NULL );
+ free( this );
+ }
+}
--- /dev/null
+/*
+ * valerie.h -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_H_
+#define _VALERIE_H_
+
+/* System header files */
+#include <limits.h>
+
+/* MLT Header files. */
+#include <framework/mlt.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_status.h"
+#include "valerie_notifier.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Client error conditions
+*/
+
+typedef enum
+{
+ valerie_ok = 0,
+ valerie_malloc_failed,
+ valerie_unknown_error,
+ valerie_no_response,
+ valerie_invalid_command,
+ valerie_server_timeout,
+ valerie_missing_argument,
+ valerie_server_unavailable,
+ valerie_unit_creation_failed,
+ valerie_unit_unavailable,
+ valerie_invalid_file,
+ valerie_invalid_position
+}
+valerie_error_code;
+
+/** Clip index specification.
+*/
+
+typedef enum
+{
+ valerie_absolute = 0,
+ valerie_relative
+}
+valerie_clip_offset;
+
+/** Client structure.
+*/
+
+typedef struct
+{
+ valerie_parser parser;
+ valerie_response last_response;
+}
+*valerie, valerie_t;
+
+/** Client API.
+*/
+
+extern valerie valerie_init( valerie_parser );
+
+/* Connect to the valerie parser instance */
+extern valerie_error_code valerie_connect( valerie );
+
+/* Global functions */
+extern valerie_error_code valerie_set( valerie, char *, char * );
+extern valerie_error_code valerie_get( valerie, char *, char *, int );
+extern valerie_error_code valerie_run( valerie, char * );
+
+/* Unit functions */
+extern valerie_error_code valerie_unit_add( valerie, char *, int * );
+extern valerie_error_code valerie_unit_load( valerie, int, char * );
+extern valerie_error_code valerie_unit_load_clipped( valerie, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_load_back( valerie, int, char * );
+extern valerie_error_code valerie_unit_load_back_clipped( valerie, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_append( valerie, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_receive( valerie, int, char *, char * );
+extern valerie_error_code valerie_unit_push( valerie, int, char *, mlt_service );
+extern valerie_error_code valerie_unit_clean( valerie, int );
+extern valerie_error_code valerie_unit_wipe( valerie, int );
+extern valerie_error_code valerie_unit_clear( valerie, int );
+extern valerie_error_code valerie_unit_clip_move( valerie, int, valerie_clip_offset, int, valerie_clip_offset, int );
+extern valerie_error_code valerie_unit_clip_remove( valerie, int, valerie_clip_offset, int );
+extern valerie_error_code valerie_unit_remove_current_clip( valerie, int );
+extern valerie_error_code valerie_unit_clip_insert( valerie, int, valerie_clip_offset, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_play( valerie, int );
+extern valerie_error_code valerie_unit_play_at_speed( valerie, int, int );
+extern valerie_error_code valerie_unit_stop( valerie, int );
+extern valerie_error_code valerie_unit_pause( valerie, int );
+extern valerie_error_code valerie_unit_rewind( valerie, int );
+extern valerie_error_code valerie_unit_fast_forward( valerie, int );
+extern valerie_error_code valerie_unit_step( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_goto( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_clip_goto( valerie, int, valerie_clip_offset, int, int32_t );
+extern valerie_error_code valerie_unit_clip_set_in( valerie, int, valerie_clip_offset, int, int32_t );
+extern valerie_error_code valerie_unit_clip_set_out( valerie, int, valerie_clip_offset, int, int32_t );
+extern valerie_error_code valerie_unit_set_in( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_set_out( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_clear_in( valerie, int );
+extern valerie_error_code valerie_unit_clear_out( valerie, int );
+extern valerie_error_code valerie_unit_clear_in_out( valerie, int );
+extern valerie_error_code valerie_unit_set( valerie, int, const char *, const char * );
+extern valerie_error_code valerie_unit_get( valerie, int, char * );
+extern valerie_error_code valerie_unit_status( valerie, int, valerie_status );
+extern valerie_error_code valerie_unit_transfer( valerie, int, int );
+
+/* Notifier functionality. */
+extern valerie_notifier valerie_get_notifier( valerie );
+
+/** Structure for the directory.
+*/
+
+typedef struct
+{
+ char *directory;
+ valerie_response response;
+}
+*valerie_dir, valerie_dir_t;
+
+/** Directory entry structure.
+*/
+
+typedef struct
+{
+ int dir;
+ char name[ NAME_MAX ];
+ char full[ PATH_MAX + NAME_MAX ];
+ unsigned long long size;
+}
+*valerie_dir_entry, valerie_dir_entry_t;
+
+/* Directory reading. */
+extern valerie_dir valerie_dir_init( valerie, const char * );
+extern valerie_error_code valerie_dir_get_error_code( valerie_dir );
+extern valerie_error_code valerie_dir_get( valerie_dir, int, valerie_dir_entry );
+extern int valerie_dir_count( valerie_dir );
+extern void valerie_dir_close( valerie_dir );
+
+/** Structure for the list.
+*/
+
+typedef struct
+{
+ int generation;
+ valerie_response response;
+}
+*valerie_list, valerie_list_t;
+
+/** List entry structure.
+*/
+
+typedef struct
+{
+ int clip;
+ char full[ PATH_MAX + NAME_MAX ];
+ int32_t in;
+ int32_t out;
+ int32_t max;
+ int32_t size;
+ int32_t fps;
+}
+*valerie_list_entry, valerie_list_entry_t;
+
+/* List reading. */
+extern valerie_list valerie_list_init( valerie, int );
+extern valerie_error_code valerie_list_get_error_code( valerie_list );
+extern valerie_error_code valerie_list_get( valerie_list, int, valerie_list_entry );
+extern int valerie_list_count( valerie_list );
+extern void valerie_list_close( valerie_list );
+
+/** Structure for nodes.
+*/
+
+typedef struct
+{
+ valerie_response response;
+}
+*valerie_nodes, valerie_nodes_t;
+
+/** Node entry structure.
+*/
+
+typedef struct
+{
+ int node;
+ char guid[ 17 ];
+ char name[ 1024 ];
+}
+*valerie_node_entry, valerie_node_entry_t;
+
+/* Node reading. */
+extern valerie_nodes valerie_nodes_init( valerie );
+extern valerie_error_code valerie_nodes_get_error_code( valerie_nodes );
+extern valerie_error_code valerie_nodes_get( valerie_nodes, int, valerie_node_entry );
+extern int valerie_nodes_count( valerie_nodes );
+extern void valerie_nodes_close( valerie_nodes );
+
+/** Structure for units.
+*/
+
+typedef struct
+{
+ valerie_response response;
+}
+*valerie_units, valerie_units_t;
+
+/** Unit entry structure.
+*/
+
+typedef struct
+{
+ int unit;
+ int node;
+ char guid[ 512 ];
+ int online;
+}
+*valerie_unit_entry, valerie_unit_entry_t;
+
+/* Unit reading. */
+extern valerie_units valerie_units_init( valerie );
+extern valerie_error_code valerie_units_get_error_code( valerie_units );
+extern valerie_error_code valerie_units_get( valerie_units, int, valerie_unit_entry );
+extern int valerie_units_count( valerie_units );
+extern void valerie_units_close( valerie_units );
+
+/* Miscellaenous functions */
+extern valerie_response valerie_get_last_response( valerie );
+extern const char *valerie_error_description( valerie_error_code );
+
+/* Courtesy functions. */
+extern valerie_error_code valerie_execute( valerie, size_t, const char *, ... );
+extern valerie_error_code valerie_push( valerie, mlt_service, size_t, const char *, ... );
+
+/* Close function. */
+extern void valerie_close( valerie );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_notifier.c -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "valerie_notifier.h"
+
+/** Notifier initialisation.
+*/
+
+valerie_notifier valerie_notifier_init( )
+{
+ valerie_notifier this = calloc( 1, sizeof( valerie_notifier_t ) );
+ if ( this != NULL )
+ {
+ int index = 0;
+ pthread_mutex_init( &this->mutex, NULL );
+ pthread_cond_init( &this->cond, NULL );
+ for ( index = 0; index < MAX_UNITS; index ++ )
+ this->store[ index ].unit = index;
+ }
+ return this;
+}
+
+/** Get a stored status for the specified unit.
+*/
+
+void valerie_notifier_get( valerie_notifier this, valerie_status status, int unit )
+{
+ pthread_mutex_lock( &this->mutex );
+ if ( unit >= 0 && unit < MAX_UNITS )
+ valerie_status_copy( status, &this->store[ unit ] );
+ else
+ memset( status, 0, sizeof( valerie_status_t ) );
+ status->unit = unit;
+ status->dummy = time( NULL );
+ pthread_mutex_unlock( &this->mutex );
+}
+
+/** Wait on a new status.
+*/
+
+int valerie_notifier_wait( valerie_notifier this, valerie_status status )
+{
+ struct timeval now;
+ struct timespec timeout;
+ int error = 0;
+
+ memset( status, 0, sizeof( valerie_status_t ) );
+ gettimeofday( &now, NULL );
+ timeout.tv_sec = now.tv_sec + 1;
+ timeout.tv_nsec = now.tv_usec * 1000;
+ pthread_mutex_lock( &this->mutex );
+ pthread_cond_timedwait( &this->cond, &this->mutex, &timeout );
+ valerie_status_copy( status, &this->last );
+ pthread_mutex_unlock( &this->mutex );
+
+ return error;
+}
+
+/** Put a new status.
+*/
+
+void valerie_notifier_put( valerie_notifier this, valerie_status status )
+{
+ pthread_mutex_lock( &this->mutex );
+ valerie_status_copy( &this->store[ status->unit ], status );
+ valerie_status_copy( &this->last, status );
+ pthread_mutex_unlock( &this->mutex );
+ pthread_cond_broadcast( &this->cond );
+}
+
+/** Communicate a disconnected status for all units to all waiting.
+*/
+
+void valerie_notifier_disconnected( valerie_notifier notifier )
+{
+ int unit = 0;
+ valerie_status_t status;
+ for ( unit = 0; unit < MAX_UNITS; unit ++ )
+ {
+ valerie_notifier_get( notifier, &status, unit );
+ status.status = unit_disconnected;
+ valerie_notifier_put( notifier, &status );
+ }
+}
+
+/** Close the notifier - note that all access must be stopped before we call this.
+*/
+
+void valerie_notifier_close( valerie_notifier this )
+{
+ if ( this != NULL )
+ {
+ pthread_mutex_destroy( &this->mutex );
+ pthread_cond_destroy( &this->cond );
+ free( this );
+ }
+}
--- /dev/null
+/*
+ * valerie_notifier.h -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_NOTIFIER_H_
+#define _VALERIE_NOTIFIER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include "valerie_status.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MAX_UNITS 16
+
+/** Status notifier definition.
+*/
+
+typedef struct
+{
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ valerie_status_t last;
+ valerie_status_t store[ MAX_UNITS ];
+}
+*valerie_notifier, valerie_notifier_t;
+
+extern valerie_notifier valerie_notifier_init( );
+extern void valerie_notifier_get( valerie_notifier, valerie_status, int );
+extern int valerie_notifier_wait( valerie_notifier, valerie_status );
+extern void valerie_notifier_put( valerie_notifier, valerie_status );
+extern void valerie_notifier_disconnected( valerie_notifier );
+extern void valerie_notifier_close( valerie_notifier );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_parser.c -- Valerie Parser for Miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_util.h"
+
+/** Connect to the parser.
+*/
+
+valerie_response valerie_parser_connect( valerie_parser parser )
+{
+ return parser->connect( parser->real );
+}
+
+/** Execute a command via the parser.
+*/
+
+valerie_response valerie_parser_execute( valerie_parser parser, char *command )
+{
+ return parser->execute( parser->real, command );
+}
+
+/** Push a service via the parser.
+*/
+
+valerie_response valerie_parser_received( valerie_parser parser, char *command, char *doc )
+{
+ return parser->received != NULL ? parser->received( parser->real, command, doc ) : NULL;
+}
+
+/** Push a service via the parser.
+*/
+
+valerie_response valerie_parser_push( valerie_parser parser, char *command, mlt_service service )
+{
+ return parser->push( parser->real, command, service );
+}
+
+/** Execute a formatted command via the parser.
+*/
+
+valerie_response valerie_parser_executef( valerie_parser parser, const char *format, ... )
+{
+ char *command = malloc( 10240 );
+ valerie_response response = NULL;
+ if ( command != NULL )
+ {
+ va_list list;
+ va_start( list, format );
+ if ( vsnprintf( command, 10240, format, list ) != 0 )
+ response = valerie_parser_execute( parser, command );
+ va_end( list );
+ free( command );
+ }
+ return response;
+}
+
+/** Execute the contents of a file. Note the special case valerie_response returned.
+*/
+
+valerie_response valerie_parser_run( valerie_parser parser, char *filename )
+{
+ valerie_response response = valerie_response_init( );
+ if ( response != NULL )
+ {
+ FILE *file = fopen( filename, "r" );
+ if ( file != NULL )
+ {
+ char command[ 1024 ];
+ valerie_response_set_error( response, 201, "OK" );
+ while ( valerie_response_get_error_code( response ) == 201 && fgets( command, 1024, file ) )
+ {
+ valerie_util_trim( valerie_util_chomp( command ) );
+ if ( strcmp( command, "" ) && command[ 0 ] != '#' )
+ {
+ valerie_response temp = NULL;
+ valerie_response_printf( response, 1024, "%s\n", command );
+ temp = valerie_parser_execute( parser, command );
+ if ( temp != NULL )
+ {
+ int index = 0;
+ for ( index = 0; index < valerie_response_count( temp ); index ++ )
+ valerie_response_printf( response, 10240, "%s\n", valerie_response_get_line( temp, index ) );
+ valerie_response_close( temp );
+ }
+ else
+ {
+ valerie_response_set_error( response, 500, "Batch execution failed" );
+ }
+ }
+ }
+ fclose( file );
+ }
+ else
+ {
+ valerie_response_set_error( response, 404, "File not found." );
+ }
+ }
+ return response;
+}
+
+/** Get the notifier associated to the parser.
+*/
+
+valerie_notifier valerie_parser_get_notifier( valerie_parser parser )
+{
+ if ( parser->notifier == NULL )
+ parser->notifier = valerie_notifier_init( );
+ return parser->notifier;
+}
+
+/** Close the parser.
+*/
+
+void valerie_parser_close( valerie_parser parser )
+{
+ if ( parser != NULL )
+ {
+ parser->close( parser->real );
+ valerie_notifier_close( parser->notifier );
+ free( parser );
+ }
+}
--- /dev/null
+/*
+ * valerie_parser.h -- Valerie Parser for Miracle Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_PARSER_H_
+#define _VALERIE_PARSER_H_
+
+/* MLT Header files */
+#include <framework/mlt.h>
+
+/* Application header files */
+#include "valerie_response.h"
+#include "valerie_notifier.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Callbacks to define the parser.
+*/
+
+typedef valerie_response (*parser_connect)( void * );
+typedef valerie_response (*parser_execute)( void *, char * );
+typedef valerie_response (*parser_received)( void *, char *, char * );
+typedef valerie_response (*parser_push)( void *, char *, mlt_service );
+typedef void (*parser_close)( void * );
+
+/** Structure for the valerie parser.
+*/
+
+typedef struct
+{
+ parser_connect connect;
+ parser_execute execute;
+ parser_push push;
+ parser_received received;
+ parser_close close;
+ void *real;
+ valerie_notifier notifier;
+}
+*valerie_parser, valerie_parser_t;
+
+/** API for the parser - note that no constructor is defined here.
+*/
+
+extern valerie_response valerie_parser_connect( valerie_parser );
+extern valerie_response valerie_parser_push( valerie_parser, char *, mlt_service );
+extern valerie_response valerie_parser_received( valerie_parser, char *, char * );
+extern valerie_response valerie_parser_execute( valerie_parser, char * );
+extern valerie_response valerie_parser_executef( valerie_parser, const char *, ... );
+extern valerie_response valerie_parser_run( valerie_parser, char * );
+extern valerie_notifier valerie_parser_get_notifier( valerie_parser );
+extern void valerie_parser_close( valerie_parser );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_remote.c -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* Application header files */
+#include <framework/mlt.h>
+#include "valerie_remote.h"
+#include "valerie_socket.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Private valerie_remote structure.
+*/
+
+typedef struct
+{
+ int terminated;
+ char *server;
+ int port;
+ valerie_socket socket;
+ valerie_socket status;
+ pthread_t thread;
+ valerie_parser parser;
+ pthread_mutex_t mutex;
+ int connected;
+}
+*valerie_remote, valerie_remote_t;
+
+/** Forward declarations.
+*/
+
+static valerie_response valerie_remote_connect( valerie_remote );
+static valerie_response valerie_remote_execute( valerie_remote, char * );
+static valerie_response valerie_remote_receive( valerie_remote, char *, char * );
+static valerie_response valerie_remote_push( valerie_remote, char *, mlt_service );
+static void valerie_remote_close( valerie_remote );
+static int valerie_remote_read_response( valerie_socket, valerie_response );
+
+/** DV Parser constructor.
+*/
+
+valerie_parser valerie_parser_init_remote( char *server, int port )
+{
+ valerie_parser parser = calloc( 1, sizeof( valerie_parser_t ) );
+ valerie_remote remote = calloc( 1, sizeof( valerie_remote_t ) );
+
+ if ( parser != NULL )
+ {
+ parser->connect = (parser_connect)valerie_remote_connect;
+ parser->execute = (parser_execute)valerie_remote_execute;
+ parser->push = (parser_push)valerie_remote_push;
+ parser->received = (parser_received)valerie_remote_receive;
+ parser->close = (parser_close)valerie_remote_close;
+ parser->real = remote;
+
+ if ( remote != NULL )
+ {
+ remote->parser = parser;
+ remote->server = strdup( server );
+ remote->port = port;
+ pthread_mutex_init( &remote->mutex, NULL );
+ }
+ }
+ return parser;
+}
+
+/** Thread for receiving and distributing the status information.
+*/
+
+static void *valerie_remote_status_thread( void *arg )
+{
+ valerie_remote remote = arg;
+ char temp[ 10240 ];
+ int length = 0;
+ int offset = 0;
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ valerie_notifier notifier = valerie_parser_get_notifier( remote->parser );
+ valerie_status_t status;
+ int index = 0;
+
+ valerie_socket_write_data( remote->status, "STATUS\r\n", 8 );
+
+ while ( !remote->terminated &&
+ ( length = valerie_socket_read_data( remote->status, temp + offset, sizeof( temp ) ) ) >= 0 )
+ {
+ if ( strchr( temp, '\n' ) == NULL )
+ {
+ offset = length;
+ continue;
+ }
+ offset = 0;
+ valerie_tokeniser_parse_new( tokeniser, temp, "\n" );
+ for ( index = 0; index < valerie_tokeniser_count( tokeniser ); index ++ )
+ {
+ char *line = valerie_tokeniser_get_string( tokeniser, index );
+ if ( line[ strlen( line ) - 1 ] == '\r' )
+ {
+ valerie_util_chomp( line );
+ valerie_status_parse( &status, line );
+ valerie_notifier_put( notifier, &status );
+ }
+ else
+ {
+ strcpy( temp, line );
+ offset = strlen( temp );
+ }
+ }
+ }
+
+ valerie_notifier_disconnected( notifier );
+ valerie_tokeniser_close( tokeniser );
+ remote->terminated = 1;
+
+ return NULL;
+}
+
+/** Forward reference.
+*/
+
+static void valerie_remote_disconnect( valerie_remote remote );
+
+/** Connect to the server.
+*/
+
+static valerie_response valerie_remote_connect( valerie_remote remote )
+{
+ valerie_response response = NULL;
+
+ valerie_remote_disconnect( remote );
+
+ if ( !remote->connected )
+ {
+ signal( SIGPIPE, SIG_IGN );
+
+ remote->socket = valerie_socket_init( remote->server, remote->port );
+ remote->status = valerie_socket_init( remote->server, remote->port );
+
+ if ( valerie_socket_connect( remote->socket ) == 0 )
+ {
+ response = valerie_response_init( );
+ valerie_remote_read_response( remote->socket, response );
+ }
+
+ if ( response != NULL && valerie_socket_connect( remote->status ) == 0 )
+ {
+ valerie_response status_response = valerie_response_init( );
+ valerie_remote_read_response( remote->status, status_response );
+ if ( valerie_response_get_error_code( status_response ) == 100 )
+ pthread_create( &remote->thread, NULL, valerie_remote_status_thread, remote );
+ valerie_response_close( status_response );
+ remote->connected = 1;
+ }
+ }
+
+ return response;
+}
+
+/** Execute the command.
+*/
+
+static valerie_response valerie_remote_execute( valerie_remote remote, char *command )
+{
+ valerie_response response = NULL;
+ pthread_mutex_lock( &remote->mutex );
+ if ( valerie_socket_write_data( remote->socket, command, strlen( command ) ) == strlen( command ) )
+ {
+ response = valerie_response_init( );
+ valerie_socket_write_data( remote->socket, "\r\n", 2 );
+ valerie_remote_read_response( remote->socket, response );
+ }
+ pthread_mutex_unlock( &remote->mutex );
+ return response;
+}
+
+/** Push a westley document to the server.
+*/
+
+static valerie_response valerie_remote_receive( valerie_remote remote, char *command, char *buffer )
+{
+ valerie_response response = NULL;
+ pthread_mutex_lock( &remote->mutex );
+ if ( valerie_socket_write_data( remote->socket, command, strlen( command ) ) == strlen( command ) )
+ {
+ char temp[ 20 ];
+ int length = strlen( buffer );
+ response = valerie_response_init( );
+ valerie_socket_write_data( remote->socket, "\r\n", 2 );
+ sprintf( temp, "%d", length );
+ valerie_socket_write_data( remote->socket, temp, strlen( temp ) );
+ valerie_socket_write_data( remote->socket, "\r\n", 2 );
+ valerie_socket_write_data( remote->socket, buffer, length );
+ valerie_socket_write_data( remote->socket, "\r\n", 2 );
+ valerie_remote_read_response( remote->socket, response );
+ }
+ pthread_mutex_unlock( &remote->mutex );
+ return response;
+}
+
+/** Push a producer to the server.
+*/
+
+static valerie_response valerie_remote_push( valerie_remote remote, char *command, mlt_service service )
+{
+ valerie_response response = NULL;
+ if ( service != NULL )
+ {
+ mlt_consumer consumer = mlt_factory_consumer( NULL, "westley", "buffer" );
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+ char *buffer = NULL;
+ // Temporary hack
+ mlt_properties_set( properties, "store", "nle_" );
+ mlt_consumer_connect( consumer, service );
+ mlt_consumer_start( consumer );
+ buffer = mlt_properties_get( properties, "buffer" );
+ response = valerie_remote_receive( remote, command, buffer );
+ mlt_consumer_close( consumer );
+ }
+ return response;
+}
+
+/** Disconnect.
+*/
+
+static void valerie_remote_disconnect( valerie_remote remote )
+{
+ if ( remote != NULL && remote->terminated )
+ {
+ if ( remote->connected )
+ pthread_join( remote->thread, NULL );
+ valerie_socket_close( remote->status );
+ valerie_socket_close( remote->socket );
+ remote->connected = 0;
+ remote->terminated = 0;
+ }
+}
+
+/** Close the parser.
+*/
+
+static void valerie_remote_close( valerie_remote remote )
+{
+ if ( remote != NULL )
+ {
+ remote->terminated = 1;
+ valerie_remote_disconnect( remote );
+ pthread_mutex_destroy( &remote->mutex );
+ free( remote->server );
+ free( remote );
+ }
+}
+
+/** Read response.
+*/
+
+static int valerie_remote_read_response( valerie_socket socket, valerie_response response )
+{
+ char temp[ 10240 ];
+ int length;
+ int terminated = 0;
+
+ while ( !terminated && ( length = valerie_socket_read_data( socket, temp, 10240 ) ) >= 0 )
+ {
+ int position = 0;
+ temp[ length ] = '\0';
+ valerie_response_write( response, temp, length );
+ position = valerie_response_count( response ) - 1;
+ if ( position < 0 || temp[ strlen( temp ) - 1 ] != '\n' )
+ continue;
+ switch( valerie_response_get_error_code( response ) )
+ {
+ case 201:
+ case 500:
+ terminated = !strcmp( valerie_response_get_line( response, position ), "" );
+ break;
+ case 202:
+ terminated = valerie_response_count( response ) >= 2;
+ break;
+ default:
+ terminated = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * valerie_remote.h -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_REMOTE_H_
+#define _VALERIE_REMOTE_H_
+
+/* Application header files */
+#include "valerie_parser.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Remote parser API.
+*/
+
+extern valerie_parser valerie_parser_init_remote( char *, int );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_response.c -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_response.h"
+
+/** Construct a new dv response.
+*/
+
+valerie_response valerie_response_init( )
+{
+ valerie_response response = malloc( sizeof( valerie_response_t ) );
+ if ( response != NULL )
+ memset( response, 0, sizeof( valerie_response_t ) );
+ return response;
+}
+
+/** Clone a dv response
+*/
+
+valerie_response valerie_response_clone( valerie_response response )
+{
+ valerie_response clone = valerie_response_init( );
+ if ( clone != NULL && response != NULL )
+ {
+ int index = 0;
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ {
+ char *line = valerie_response_get_line( response, index );
+ valerie_response_printf( clone, strlen( line ) + 2, "%s\n", line );
+ }
+ }
+ return clone;
+}
+
+/** Get the error code associated to the response.
+*/
+
+int valerie_response_get_error_code( valerie_response response )
+{
+ int error_code = -1;
+ if ( response != NULL )
+ {
+ if ( response->count > 0 )
+ {
+ if ( sscanf( response->array[ 0 ], "%d", &error_code ) != 1 )
+ error_code = 0;
+ }
+ else
+ {
+ error_code = -2;
+ }
+ }
+ return error_code;
+}
+
+/** Get the error description associated to the response.
+*/
+
+const char *valerie_response_get_error_string( valerie_response response )
+{
+ const char *error_string = "No message specified";
+ if ( response->count > 0 )
+ {
+ char *ptr = strchr( response->array[ 0 ], ' ' ) ;
+ if ( ptr != NULL )
+ error_string = ptr + 1;
+ }
+ return error_string;
+}
+
+/** Get a line of text at the given index. Note that the text itself is
+ terminated only with a NUL char and it is the responsibility of the
+ the user of the returned data to use a LF or CR/LF as appropriate.
+*/
+
+char *valerie_response_get_line( valerie_response response, int index )
+{
+ if ( index < response->count )
+ return response->array[ index ];
+ else
+ return NULL;
+}
+
+/** Return the number of lines of text in the response.
+*/
+
+int valerie_response_count( valerie_response response )
+{
+ if ( response != NULL )
+ return response->count;
+ else
+ return 0;
+}
+
+/** Set the error and description associated to the response.
+*/
+
+void valerie_response_set_error( valerie_response response, int error_code, const char *error_string )
+{
+ if ( response->count == 0 )
+ {
+ valerie_response_printf( response, 10240, "%d %s\n", error_code, error_string );
+ }
+ else
+ {
+ char temp[ 10240 ];
+ int length = sprintf( temp, "%d %s", error_code, error_string );
+ response->array[ 0 ] = realloc( response->array[ 0 ], length + 1 );
+ strcpy( response->array[ 0 ], temp );
+ }
+}
+
+/** Write formatted text to the response.
+*/
+
+int valerie_response_printf( valerie_response response, size_t size, const char *format, ... )
+{
+ int length = 0;
+ char *text = malloc( size );
+ if ( text != NULL )
+ {
+ va_list list;
+ va_start( list, format );
+ length = vsnprintf( text, size, format, list );
+ if ( length != 0 )
+ valerie_response_write( response, text, length );
+ va_end( list );
+ free( text );
+ }
+ return length;
+}
+
+/** Write text to the reponse.
+*/
+
+int valerie_response_write( valerie_response response, const char *text, int size )
+{
+ int ret = 0;
+ const char *ptr = text;
+
+ while ( size > 0 )
+ {
+ int index = response->count - 1;
+ const char *lf = strchr( ptr, '\n' );
+ int length_of_string = 0;
+
+ /* Make sure we have space in the dynamic array. */
+ if ( !response->append && response->count >= response->size - 1 )
+ {
+ response->size += 50;
+ response->array = realloc( response->array, response->size * sizeof( char * ) );
+ }
+
+ /* Make sure the array is valid, or we're really in trouble */
+ if ( response->array == NULL )
+ {
+ ret = 0;
+ break;
+ }
+
+ /* Now, if we're appending to the previous write (ie: if it wasn't
+ terminated by a LF), then use the index calculated above, otherwise
+ go to the next one and ensure it's NULLed. */
+
+ if ( !response->append )
+ {
+ response->array[ ++ index ] = NULL;
+ response->count ++;
+ }
+ else
+ {
+ length_of_string = strlen( response->array[ index ] );
+ }
+
+ /* Now we need to know how to handle the current ptr with respect to lf. */
+ /* TODO: tidy up and error check... sigh... tested for many, many 1000s of lines */
+
+ if ( lf == NULL )
+ {
+ response->array[ index ] = realloc( response->array[ index ], length_of_string + size + 1 );
+ memcpy( response->array[ index ] + length_of_string, ptr, size );
+ response->array[ index ][ length_of_string + size ] = '\0';
+ if ( ( length_of_string + size ) > 0 && response->array[ index ][ length_of_string + size - 1 ] == '\r' )
+ response->array[ index ][ length_of_string + size - 1 ] = '\0';
+ size = 0;
+ ret += size;
+ response->append = 1;
+ }
+ else
+ {
+ int chars = lf - ptr;
+ response->array[ index ] = realloc( response->array[ index ], length_of_string + chars + 1 );
+ memcpy( response->array[ index ] + length_of_string, ptr, chars );
+ response->array[ index ][ length_of_string + chars ] = '\0';
+ if ( ( length_of_string + chars ) > 0 && response->array[ index ][ length_of_string + chars - 1 ] == '\r' )
+ response->array[ index ][ length_of_string + chars - 1 ] = '\0';
+ ptr = ptr + chars + 1;
+ size -= ( chars + 1 );
+ response->append = 0;
+ ret += chars + 1;
+ }
+ }
+
+ return ret;
+}
+
+/** Close the response.
+*/
+
+void valerie_response_close( valerie_response response )
+{
+ if ( response != NULL )
+ {
+ int index = 0;
+ for ( index = 0; index < response->count; index ++ )
+ free( response->array[ index ] );
+ free( response->array );
+ free( response );
+ }
+}
--- /dev/null
+/*
+ * valerie_response.h -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_RESPONSE_H_
+#define _VALERIE_RESPONSE_H_
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for the response
+*/
+
+typedef struct
+{
+ char **array;
+ int size;
+ int count;
+ int append;
+}
+*valerie_response, valerie_response_t;
+
+/** API for accessing the response structure.
+*/
+
+extern valerie_response valerie_response_init( );
+extern valerie_response valerie_response_clone( valerie_response );
+extern int valerie_response_get_error_code( valerie_response );
+extern const char *valerie_response_get_error_string( valerie_response );
+extern char *valerie_response_get_line( valerie_response, int );
+extern int valerie_response_count( valerie_response );
+extern void valerie_response_set_error( valerie_response, int, const char * );
+extern int valerie_response_printf( valerie_response, size_t, const char *, ... );
+extern int valerie_response_write( valerie_response, const char *, int );
+extern void valerie_response_close( valerie_response );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_socket.c -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "valerie_socket.h"
+
+/** Initialise the socket.
+*/
+
+valerie_socket valerie_socket_init( char *server, int port )
+{
+ valerie_socket socket = malloc( sizeof( valerie_socket_t ) );
+ if ( socket != NULL )
+ {
+ memset( socket, 0, sizeof( valerie_socket_t ) );
+ socket->fd = -1;
+ socket->server = strdup( server );
+ socket->port = port;
+ }
+ return socket;
+}
+
+/** Connect to the server.
+*/
+
+int valerie_socket_connect( valerie_socket connection )
+{
+ int ret = 0;
+ struct hostent *host;
+ struct sockaddr_in sock;
+
+ if ( connection->server != NULL )
+ {
+ host = gethostbyname( connection->server );
+
+ memset( &sock, 0, sizeof( struct sockaddr_in ) );
+ memcpy( &sock.sin_addr, host->h_addr, host->h_length );
+ sock.sin_family = host->h_addrtype;
+ sock.sin_port = htons( connection->port );
+
+ if ( ( connection->fd = socket( AF_INET, SOCK_STREAM, 0 ) ) != -1 )
+ ret = connect( connection->fd, (const struct sockaddr *)&sock, sizeof( struct sockaddr_in ) );
+ else
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/** Convenience constructor for a connected file descriptor.
+*/
+
+valerie_socket valerie_socket_init_fd( int fd )
+{
+ valerie_socket socket = malloc( sizeof( valerie_socket_t ) );
+ if ( socket != NULL )
+ {
+ memset( socket, 0, sizeof( valerie_socket_t ) );
+ socket->fd = fd;
+ socket->no_close = 1;
+ }
+ return socket;
+}
+
+/** Read an arbitrarily formatted block of data from the server.
+*/
+
+int valerie_socket_read_data( valerie_socket socket, char *data, int length )
+{
+ struct timeval tv = { 1, 0 };
+ fd_set rfds;
+ int used = 0;
+
+ data[ 0 ] = '\0';
+
+ FD_ZERO( &rfds );
+ FD_SET( socket->fd, &rfds );
+
+ if ( select( socket->fd + 1, &rfds, NULL, NULL, &tv ) )
+ {
+ used = read( socket->fd, data, length - 1 );
+ if ( used > 0 )
+ data[ used ] = '\0';
+ else
+ used = -1;
+ }
+
+ return used;
+}
+
+/** Write an arbitrarily formatted block of data to the server.
+*/
+
+int valerie_socket_write_data( valerie_socket socket, const char *data, int length )
+{
+ int used = 0;
+
+ while ( used >=0 && used < length )
+ {
+ struct timeval tv = { 1, 0 };
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+
+ FD_ZERO( &rfds );
+ FD_SET( socket->fd, &rfds );
+ FD_ZERO( &wfds );
+ FD_SET( socket->fd, &wfds );
+ FD_ZERO( &efds );
+ FD_SET( socket->fd, &efds );
+
+ errno = 0;
+
+ if ( select( socket->fd + 1, &rfds, &wfds, &efds, &tv ) )
+ {
+ if ( errno != 0 || FD_ISSET( socket->fd, &efds ) || FD_ISSET( socket->fd, &rfds ) )
+ {
+ used = -1;
+ }
+ else if ( FD_ISSET( socket->fd, &wfds ) )
+ {
+ int inc = write( socket->fd, data + used, length - used );
+ if ( inc > 0 )
+ used += inc;
+ else
+ used = -1;
+ }
+ }
+ }
+
+ return used;
+}
+
+/** Close the socket.
+*/
+
+void valerie_socket_close( valerie_socket socket )
+{
+ if ( socket->fd > 0 && !socket->no_close )
+ close( socket->fd );
+ free( socket->server );
+ free( socket );
+}
--- /dev/null
+/*
+ * valerie_socket.h -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VALERIE_SOCKET_H_
+#define _VALERIE_SOCKET_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for socket.
+*/
+
+typedef struct
+{
+ char *server;
+ int port;
+ int fd;
+ int no_close;
+}
+*valerie_socket, valerie_socket_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_socket valerie_socket_init( char *, int );
+extern int valerie_socket_connect( valerie_socket );
+extern valerie_socket valerie_socket_init_fd( int );
+extern int valerie_socket_read_data( valerie_socket, char *, int );
+extern int valerie_socket_write_data( valerie_socket, const char *, int );
+extern void valerie_socket_close( valerie_socket );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_status.c -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_status.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Parse a unit status string.
+*/
+
+void valerie_status_parse( valerie_status status, char *text )
+{
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ if ( valerie_tokeniser_parse_new( tokeniser, text, " " ) == 17 )
+ {
+ status->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+ strncpy( status->clip, valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' ), sizeof( status->clip ) );
+ status->position = atol( valerie_tokeniser_get_string( tokeniser, 3 ) );
+ status->speed = atoi( valerie_tokeniser_get_string( tokeniser, 4 ) );
+ status->fps = atof( valerie_tokeniser_get_string( tokeniser, 5 ) );
+ status->in = atol( valerie_tokeniser_get_string( tokeniser, 6 ) );
+ status->out = atol( valerie_tokeniser_get_string( tokeniser, 7 ) );
+ status->length = atol( valerie_tokeniser_get_string( tokeniser, 8 ) );
+
+ strncpy( status->tail_clip, valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 9 ), '\"' ), sizeof( status->tail_clip ) );
+ status->tail_position = atol( valerie_tokeniser_get_string( tokeniser, 10 ) );
+ status->tail_in = atol( valerie_tokeniser_get_string( tokeniser, 11 ) );
+ status->tail_out = atol( valerie_tokeniser_get_string( tokeniser, 12 ) );
+ status->tail_length = atol( valerie_tokeniser_get_string( tokeniser, 13 ) );
+ status->seek_flag = atoi( valerie_tokeniser_get_string( tokeniser, 14 ) );
+ status->generation = atoi( valerie_tokeniser_get_string( tokeniser, 15 ) );
+ status->clip_index = atoi( valerie_tokeniser_get_string( tokeniser, 16 ) );
+
+ if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "unknown" ) )
+ status->status = unit_unknown;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "undefined" ) )
+ status->status = unit_undefined;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "offline" ) )
+ status->status = unit_offline;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "not_loaded" ) )
+ status->status = unit_not_loaded;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "stopped" ) )
+ status->status = unit_stopped;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "paused" ) )
+ status->status = unit_paused;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "playing" ) )
+ status->status = unit_playing;
+ else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "disconnected" ) )
+ status->status = unit_disconnected;
+ }
+ else
+ {
+ memset( status, 0, sizeof( valerie_status_t ) );
+ fprintf( stderr, "Status thread changed?\n" );
+ }
+ valerie_tokeniser_close( tokeniser );
+}
+
+/** Serialise a status into a string.
+*/
+
+char *valerie_status_serialise( valerie_status status, char *text, int length )
+{
+ const char *status_string = NULL;
+
+ switch( status->status )
+ {
+ case unit_undefined:
+ status_string = "undefined";
+ break;
+
+ case unit_offline:
+ status_string = "offline";
+ break;
+
+ case unit_not_loaded:
+ status_string = "not_loaded";
+ break;
+
+ case unit_stopped:
+ status_string = "stopped";
+ break;
+
+ case unit_playing:
+ status_string = "playing";
+ break;
+
+ case unit_unknown:
+ status_string = "unknown";
+ break;
+
+ case unit_paused:
+ status_string = "paused";
+ break;
+
+ case unit_disconnected:
+ status_string = "disconnected";
+ break;
+ }
+
+ snprintf( text, length, "%d %s \"%s\" %d %d %.2f %d %d %d \"%s\" %d %d %d %d %d %d %d\r\n",
+ status->unit,
+ status_string,
+ status->clip,
+ status->position,
+ status->speed,
+ status->fps,
+ status->in,
+ status->out,
+ status->length,
+ status->tail_clip,
+ status->tail_position,
+ status->tail_in,
+ status->tail_out,
+ status->tail_length,
+ status->seek_flag,
+ status->generation,
+ status->clip_index );
+
+ return text;
+}
+
+/** Compare two status codes for changes.
+*/
+
+int valerie_status_compare( valerie_status status1, valerie_status status2 )
+{
+ return memcmp( status1, status2, sizeof( valerie_status_t ) );
+}
+
+/** Copy status code info from dest to src.
+*/
+
+valerie_status valerie_status_copy( valerie_status dest, valerie_status src )
+{
+ return memcpy( dest, src, sizeof( valerie_status_t ) );
+}
--- /dev/null
+/*
+ * valerie_status.h -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_STATUS_H_
+#define _VALERIE_STATUS_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Status codes
+*/
+
+typedef enum
+{
+ unit_unknown = 0,
+ unit_undefined,
+ unit_offline,
+ unit_not_loaded,
+ unit_stopped,
+ unit_playing,
+ unit_paused,
+ unit_disconnected
+}
+unit_status;
+
+/** Status structure.
+*/
+
+typedef struct
+{
+ int unit;
+ unit_status status;
+ char clip[ 2048 ];
+ int32_t position;
+ int speed;
+ double fps;
+ int32_t in;
+ int32_t out;
+ int32_t length;
+ char tail_clip[ 2048 ];
+ int32_t tail_position;
+ int32_t tail_in;
+ int32_t tail_out;
+ int32_t tail_length;
+ int seek_flag;
+ int generation;
+ int clip_index;
+ int dummy;
+}
+*valerie_status, valerie_status_t;
+
+/** DV1394 Status API
+*/
+
+extern void valerie_status_parse( valerie_status, char * );
+extern char *valerie_status_serialise( valerie_status, char *, int );
+extern int valerie_status_compare( valerie_status, valerie_status );
+extern valerie_status valerie_status_copy( valerie_status, valerie_status );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_tokeniser.c -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_tokeniser.h"
+
+/** Initialise a tokeniser.
+*/
+
+valerie_tokeniser valerie_tokeniser_init( )
+{
+ valerie_tokeniser tokeniser = malloc( sizeof( valerie_tokeniser_t ) );
+ if ( tokeniser != NULL )
+ memset( tokeniser, 0, sizeof( valerie_tokeniser_t ) );
+ return tokeniser;
+}
+
+/** Clear the tokeniser.
+*/
+
+static void valerie_tokeniser_clear( valerie_tokeniser tokeniser )
+{
+ int index = 0;
+ for ( index = 0; index < tokeniser->count; index ++ )
+ free( tokeniser->tokens[ index ] );
+ tokeniser->count = 0;
+ free( tokeniser->input );
+ tokeniser->input = NULL;
+}
+
+/** Append a string to the tokeniser.
+*/
+
+static int valerie_tokeniser_append( valerie_tokeniser tokeniser, char *token )
+{
+ int error = 0;
+
+ if ( tokeniser->count == tokeniser->size )
+ {
+ tokeniser->size += 20;
+ tokeniser->tokens = realloc( tokeniser->tokens, tokeniser->size * sizeof( char * ) );
+ }
+
+ if ( tokeniser->tokens != NULL )
+ {
+ tokeniser->tokens[ tokeniser->count ++ ] = strdup( token );
+ }
+ else
+ {
+ tokeniser->count = 0;
+ error = -1;
+ }
+ return error;
+}
+
+/** Parse a string by splitting on the delimiter provided.
+*/
+
+int valerie_tokeniser_parse_new( valerie_tokeniser tokeniser, char *string, const char *delimiter )
+{
+ int count = 0;
+ int length = strlen( string );
+ int delimiter_size = strlen( delimiter );
+ int index = 0;
+ char *token = strdup( string );
+
+ valerie_tokeniser_clear( tokeniser );
+ tokeniser->input = strdup( string );
+ strcpy( token, "" );
+
+ for ( index = 0; index < length; )
+ {
+ char *start = string + index;
+ char *end = strstr( start, delimiter );
+
+ if ( end == NULL )
+ {
+ strcat( token, start );
+ valerie_tokeniser_append( tokeniser, token );
+ index = length;
+ count ++;
+ }
+ else if ( start != end )
+ {
+ strncat( token, start, end - start );
+ index += end - start;
+ if ( token[ 0 ] != '\"' || ( token[ 0 ] == '\"' && token[ strlen( token ) - 1 ] == '\"' ) )
+ {
+ valerie_tokeniser_append( tokeniser, token );
+ strcpy( token, "" );
+ count ++;
+ }
+ else while ( strncmp( string + index, delimiter, delimiter_size ) == 0 )
+ {
+ strncat( token, delimiter, delimiter_size );
+ index += delimiter_size;
+ }
+ }
+ else
+ {
+ index += strlen( delimiter );
+ }
+ }
+
+ /* Special case - malformed string condition */
+ if ( !strcmp( token, "" ) )
+ {
+ count = 0 - ( count - 1 );
+ valerie_tokeniser_append( tokeniser, token );
+ }
+
+ free( token );
+ return count;
+}
+
+/** Get the original input.
+*/
+
+char *valerie_tokeniser_get_input( valerie_tokeniser tokeniser )
+{
+ return tokeniser->input;
+}
+
+/** Get the number of tokens.
+*/
+
+int valerie_tokeniser_count( valerie_tokeniser tokeniser )
+{
+ return tokeniser->count;
+}
+
+/** Get a token as a string.
+*/
+
+char *valerie_tokeniser_get_string( valerie_tokeniser tokeniser, int index )
+{
+ if ( index < tokeniser->count )
+ return tokeniser->tokens[ index ];
+ else
+ return NULL;
+}
+
+/** Close the tokeniser.
+*/
+
+void valerie_tokeniser_close( valerie_tokeniser tokeniser )
+{
+ valerie_tokeniser_clear( tokeniser );
+ free( tokeniser->tokens );
+ free( tokeniser );
+}
--- /dev/null
+/*
+ * valerie_tokeniser.h -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_TOKENISER_H_
+#define _VALERIE_TOKENISER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for tokeniser.
+*/
+
+typedef struct
+{
+ char *input;
+ char **tokens;
+ int count;
+ int size;
+}
+*valerie_tokeniser, valerie_tokeniser_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_tokeniser valerie_tokeniser_init( );
+extern int valerie_tokeniser_parse_new( valerie_tokeniser, char *, const char * );
+extern char *valerie_tokeniser_get_input( valerie_tokeniser );
+extern int valerie_tokeniser_count( valerie_tokeniser );
+extern char *valerie_tokeniser_get_string( valerie_tokeniser, int );
+extern void valerie_tokeniser_close( valerie_tokeniser );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_util.c -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <string.h>
+#include <ctype.h>
+
+/* Application header files */
+#include "valerie_util.h"
+
+/** Remove LF or CR/LF terminations from the input string.
+*/
+
+char *valerie_util_chomp( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ if ( length && input[ length - 1 ] == '\n' )
+ input[ length - 1 ] = '\0';
+ if ( length > 1 && input[ length - 2 ] == '\r' )
+ input[ length - 2 ] = '\0';
+ }
+ return input;
+}
+
+/** Remove leading and trailing spaces from the input string.
+*/
+
+char *valerie_util_trim( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ int first = 0;
+ while( first < length && isspace( input[ first ] ) )
+ first ++;
+ memmove( input, input + first, length - first + 1 );
+ length = length - first;
+ while ( length > 0 && isspace( input[ length - 1 ] ) )
+ input[ -- length ] = '\0';
+ }
+ return input;
+}
+
+/** Strip the specified string of leading and trailing 'value' (ie: ").
+*/
+
+char *valerie_util_strip( char *input, char value )
+{
+ if ( input != NULL )
+ {
+ char *ptr = strrchr( input, value );
+ if ( ptr != NULL )
+ *ptr = '\0';
+ if ( input[ 0 ] == value )
+ strcpy( input, input + 1 );
+ }
+ return input;
+}
--- /dev/null
+/*
+ * valerie_util.h -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_UTIL_H_
+#define _VALERIE_UTIL_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *valerie_util_chomp( char * );
+extern char *valerie_util_trim( char * );
+extern char *valerie_util_strip( char *, char );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif