From: Dan Dennedy Date: Fri, 8 May 2009 02:47:17 +0000 (-0700) Subject: Merge ../mlt++ X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=b1616107824f9c1b2e83c97d9a8d3b0cc700ef5f;hp=aa3e0e3b1486db3af8f03e887cbc4ad7a86d746d;p=mlt Merge ../mlt++ --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..63f6b59c --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.o +*.so +*.so.* +config.mak +config.h +.depend diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..89e7d76a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,14 @@ +MLT framework was developed by: +Charles Yates + +MLT framework is maintained by: +Dan Dennedy + +MLT module authors and maintainers: + +Charles Yates +Dan Dennedy +Stephane Fillod (effectv) +Marco Gittler (frei0r, oldfilm) +Jean-Baptiste Mardelle (kdenlive, qimage) +Zachary Drew (motion_est) diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..223ede7d --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + 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. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e8e459f5 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,6664 @@ +2009-04-14 Dan Dennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_properties.c, src/modules/westley/consumer_westley.c, + src/modules/westley/producer_westley.c: Constness changes + +2009-03-04 Ray Lehtiniemi + + * 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 + + * src/modules/westley/producer_westley.c: producer_westley.c: Don't prepend + westley document root to empty properties + +2009-04-03 ddennedy + + * src/modules/core/filter_crop.c: filter_crop.c: bugfix chroma alignment + +2009-03-17 ddennedy + + * src/modules/frei0r/factory.c: frei0r/factory.c: add /usr/lib64 to the + default frei0r plugin path + +2009-03-15 j-b-m + + * src/modules/core/transition_composite.c: transition_composite.c: allow + removing of luma file by passing an empty name + +2009-03-14 ddennedy + + * src/modules/core/transition_composite.c: transition_composite.c: make luma + and luma_invert properties mutable + +2009-03-10 ddennedy + + * 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 + + * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: Fix + producer out position + +2009-03-06 ddennedy + + * src/framework/mlt_log.h: mlt_log.h: add convenience macros + +2009-03-03 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/core/filter_brightness.c: filter_brightness.c: fix the + wonkiness by filtering chroma as well. + +2009-02-12 ddennedy + + * 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 + + * 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 + + * debian/changelog, debian/control, debian/copyright, debian/rules: remove + the debian package subdirectory (they provide their own) + +2009-02-02 ddennedy + + * 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 + + * src/modules/frei0r/factory.c: frei0r/factory.c: add more default locations + for locating plugins including one for MacPorts + +2009-01-30 ddennedy + + * src/inigo/inigo.c: inigo.c: make usage fit in 80 columns + +2009-01-29 j-b-m + + * src/modules/avformat/filter_swscale.c: filter_swscale.c: Fix compilation + (typo introduced in rev. 1330) + +2009-01-29 ddennedy + + * 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 + + * 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 + + * src/modules/avformat/producer_avformat.c: producer_avformat.c: add version + check to use AVCodec->long_name + +2009-01-23 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/avformat/configure: avformat/configure: add -lbz2 automatically + for --avformat-svn + +2008-12-31 ddennedy + + * 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 + + * Doxyfile: Doxyfile: bump version + +2008-12-29 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/fezzik.dict: fezzik.dict: let qimage be a producer for svg + +2008-12-18 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/fezzik.dict: fezzik.dict: added support for .tif equivalent to + .tiff + +2008-11-17 ddennedy + + * 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 + + * src/modules/kdenlive/filter_freeze.c: filter_freeze.c: fix detection of + current frame position in a playlist + +2008-11-13 ddennedy + + * 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 + + * src/modules/kdenlive/filter_freeze.c: filter_freeze.c: update frozen frame + if freeze position is changed on the fly + +2008-11-13 blendamedt + + * 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 + + * NEWS, configure: configure, NEWS: bump to version 0.3.2 and update release + notes + +2008-11-09 ddennedy + + * src/modules/plus/filter_affine.c: filter_affine.c: bugfix (kdenlive-235) + rendering when used inside a multitrack. + +2008-11-08 ddennedy + + * 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 + + * 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 + + * docs/services.txt: services.txt: minor corrections to documentation for + producer_avformat + +2008-11-05 j-b-m + + * 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 + + * 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 + + * 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 + + * src/albino/albino.c, src/inigo/inigo.c: albino.c, inigo.c: disable realtime + scheduling (kdenlive-180). + +2008-10-27 j-b-m + + * src/modules/avformat/producer_avformat.c: producer_avformat.c: Fix crash / + corruption when changing audio or video index + +2008-10-27 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix + reading file over http. + +2008-09-22 j-b-m + + * 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 + + * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix + (2106941) compilation against recent ffmpeg changes + +2008-09-07 ddennedy + + * src/modules/kino/filehandler.cc: modules/kino/filehandler.cc: compilation + fix + +2008-08-26 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/frei0r/factory.c: frei0r/factory.c: use float values for + "double vars" in frei0r + +2008-07-28 blendamedt + + * src/modules/frei0r/configure: frei0r/configure: removed unneeded newlines + +2008-07-27 j-b-m + + * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: Fix + aspect ratio with slowmotion producer + +2008-07-24 j-b-m + + * src/modules/qimage/configure: qimage/configure: Fix Qt3 detection and + compilation + +2008-07-22 j-b-m + + * 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 + + * src/modules/qimage/configure: qimage/configure: Fix Qt4 detection + +2008-07-13 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_playlist.c: mlt_playlist.c: return error on + mlt_playlist_get_clip_info if producer is null. + +2008-06-23 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_playlist.c: mlt_playlist.c: remove some unncessary and + inefficient accounting code. + +2008-06-06 ddennedy + + * 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 + + * 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 + + * 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 + + * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: apply the in point + to the position in the image sequence + +2008-05-15 ddennedy + + * 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 + + * src/modules/core/transition_luma.c: Correctly update the luma file if the + resource was modified + +2008-05-12 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix image + sequences + +2008-03-22 blendamedt + + * 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 + + * src/modules/avformat/configure: avformat/configure: improve chances of + successful linking with -svn and -static options + +2008-03-07 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/avformat/configure: avformat/Makefile: compilation fix for + latest FFmpeg update + +2008-02-26 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/dv/producer_libdv.c: producer_libdv.c: fix test for framerate + matching profile + +2008-02-07 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix + (kdenlive-28) a/v sync on non-whole frame rate. + +2008-01-11 ddennedy + + * src/modules/avformat/producer_avformat.c: producer_avformat.c: do not free + AVPacket if av_read_frame fails. + +2008-01-08 ddennedy + + * 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 + + * 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 + + * 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 + + * .../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 + + * src/modules/avformat/configure, src/modules/sox/configure: sox/configure: + remove libsamplerate from linking by default + +2007-12-04 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/sdl/consumer_sdl_still.c: consumer_sdl_still.c: bugfix segfault + +2007-08-03 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/qimage/configure: Fix build based on patch from Ryan Hodge + +2007-07-01 j-b-m + + * 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 + + * src/modules/avformat/producer_avformat.c: producer_avformat.c: improve + frame accuracy + +2007-06-30 j-b-m + + * 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 + + * 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 + + * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix + initial buffer size to prevent high quantization at beginning + +2007-06-26 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/kdenlive/producer_framebuffer.c: Fix framebuffer crash & clip + duration error + +2007-05-25 ddennedy + + * 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 + + * src/modules/core/filter_data_show.c: fix dynamic attribute value parsing + and memory management in data_show + +2007-05-23 ddennedy + + * 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 + + * 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 + + * src/modules/core/producer_framebuffer.c: Fixed crash in slowmotion producer + + +2007-03-31 ddennedy + + * ChangeLog, src/modules/sox/filter_sox.c: add sox 13.0.0 support + +2007-03-31 j-b-m + + * 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 + + * 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 + + * 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 + + * ChangeLog, src/modules/avformat/Makefile, src/modules/avformat/configure: + fixup some swscale integration + +2007-03-17 ddennedy + + * ChangeLog, docs/TODO, docs/policies.txt: added docs/policies.txt + +2007-03-04 ddennedy + + * 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 + + * src/modules/sdl/consumer_sdl_preview.c: Allow user to choose video driver + and output display + +2007-02-19 j-b-m + + * 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 + + * 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 + + * src/modules/sdl/consumer_sdl_preview.c: Allow user to set alsa device + +2007-01-23 j-b-m + + * src/modules/core/filter_data_show.c: Allow display of metadata and timecode + + +2007-01-22 j-b-m + + * src/modules/avformat/consumer_avformat.c: Write metadata if there is any + +2007-01-19 j-b-m + + * src/framework/mlt_frame.c: Fix my terribly broken YUV to RGB conversion + +2007-01-13 j-b-m + + * src/modules/sdl/consumer_sdl_preview.c: Allow changing volume in + sdl_preview consumer + +2007-01-02 j-b-m + + * src/modules/avformat/consumer_avformat.c: Change default value for + libavformat's qscale, preventing some crashes + +2006-12-31 j-b-m + + * 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 + + * 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 + + * src/modules/core/producer_framebuffer.c: remove debug msg + +2006-11-18 j-b-m + + * 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 + + * 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 + + * src/modules/core/filter_rescale.c: Fix rescaling of rgb images when not + using gtk2 + +2006-10-16 j-b-m + + * 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 + + * src/modules/avformat/producer_avformat.c: + General improved media support + +2006-10-03 lilo_booter + + * 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 + + * 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 + + * ChangeLog, src/modules/sdl/Makefile: fix SDL compilation on some systems + using modular x.org + +2006-08-14 lilo_booter + + * 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 + + * 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 + + * configure: bump version + +2006-05-24 ddennedy + + * src/modules/qimage/producer_qimage.c: apply patch from Jean-Baptiste + to add rgb24a support to producer_qimage + +2006-05-22 ddennedy + + * src/modules/core/transition_composite.c: apply patch from Jean Baptiste + 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 + + * 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 + + * 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 + + * src/modules/core/filter_resize.c: + Field order control reworked + (meta.top_field_first has priority over source) + +2006-04-12 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * docs/dvcp.txt, docs/inigo.txt: minor fixes + + * src/miracle/miracle_commands.c: add proper response to uadd command + +2006-01-08 ddennedy + + * 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 + + * 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 + + * 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 + + * src/modules/core/transition_composite.c: + Correction to alpha mask + generation + +2005-11-10 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/vmfx/filter_shape.c: + Correction for non-zero in point on the + associated cut + +2005-10-14 lilo_booter + + * 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 + + * src/modules/sdl/consumer_sdl.c: + Deadlock resolution + +2005-10-10 lilo_booter + + * 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 + + * src/modules/vmfx/filter_shape.c: + Corrections, optimisations and a hack + for loading lumas from the mlt luma collection + +2005-10-03 lilo_booter + + * 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 + + * src/modules/core/transition_composite.c: + Clean ups and corrections + +2005-09-29 lilo_booter + + * src/modules/avformat/filter_avcolour_space.c: + Extracts alpha from rgb24a + images + +2005-09-28 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_properties.h: + Replaced this with self in new pass + functions for C++ compilation + +2005-08-21 dezeroex + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/avformat/Makefile, src/modules/avformat/configure: ffmpeg split + of the libavutil library. + +2005-07-30 dezeroex + + * 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 + + * 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 + + * src/framework/mlt_types.h: Do not break ABI to workaround a problem in + swig. + +2005-07-27 lilo_booter + + * src/modules/kino/producer_kino.c: + Stores the resource correctly (to allow + serialisation via westley) + +2005-07-26 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * .../motion_est/filter_autotrack_rectangle.c: use shared arrow drawing code. + improve tracking accuracy. + +2005-07-20 lilo_booter + + * src/framework/mlt_filter.c, src/framework/mlt_service.c: mlt_filter.c + mlt_service.c + Filter disable property + +2005-07-19 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/fezzik.dict: + BGa's request for additional westley extensions + +2005-06-22 lilo_booter + + * 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 + + * 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 + + * src/framework/configure: Quick temporary fix for mlt config in non-standard + paths (relates to mlt++) + +2005-06-04 lilo_booter + + * 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 + + * src/modules/fezzik.dict: libdv/avformat switching + +2005-06-01 lilo_booter + + * src/modules/avformat/filter_avcolour_space.c: Sanity checks + + * src/modules/gtk2/producer_pixbuf.c: Fallback to testcard + +2005-05-28 lilo_booter + + * 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 + + * src/modules/avformat/consumer_avformat.c: audio out fix + +2005-05-24 lilo_booter + + * src/modules/kino/filehandler.cc, src/modules/kino/filehandler.h: DVCPRO fix + + +2005-05-23 lilo_booter + + * src/modules/avformat/consumer_avformat.c: jpeg and mjpeg fixes + +2005-05-11 ddennedy + + * src/modules/jackrack/filter_ladspa.c: bugfix segfault on closre when filter + never invoked + +2005-05-09 lilo_booter + + * src/modules/avformat/Makefile, src/modules/avformat/configure, + src/modules/avformat/factory.c: Build modification to ffmpeg/avformat + +2005-05-04 lilo_booter + + * 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 + + * src/modules/kino/Makefile: fix compilation + +2005-04-22 ddennedy + + * 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 + + * src/modules/dv/producer_libdv.c: Fix for file identification and dv + +2005-04-15 lilo_booter + + * 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 + + * src/modules/dv/Makefile: corrected pkg-config libdv usage + +2005-04-14 dezeroex + + * 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 caused compilation errors + while porting consumer_sdl to OS X. + +2005-04-13 lilo_booter + + * 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 + + * src/framework/mlt_geometry.c: Minor but confusing comment fix. + +2005-04-12 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_consumer.c, src/framework/mlt_producer.c: Frame rendering + event + +2005-03-13 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * src/miracle/miracle_unit.c: Smoother unit load + +2005-02-12 lilo_booter + + * 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 + + * src/modules/sdl/consumer_sdl_preview.c, + src/modules/sdl/consumer_sdl_still.c: Speed switch corrections + +2005-02-05 lilo_booter + + * 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 + + * src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c: + more affine silliness + +2005-02-02 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_geometry.c: Corrections to geometry next key and + serialise + +2005-01-03 lilo_booter + + * 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 + + * src/framework/mlt_playlist.c: Correction to clip_start at end of playlist + +2004-12-31 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_playlist.c, src/modules/core/transition_composite.c, + src/modules/data_fx.properties: New geometry specification + +2004-12-17 lilo_booter + + * 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 + + * 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 + + * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: blank_at method + added + +2004-12-11 lilo_booter + + * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: split_at method + added + +2004-12-09 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_multitrack.c: Behavioural change - tracks with hide + properties now affect length (might be problematic) + +2004-11-03 lilo_booter + + * src/framework/mlt_producer.c: Correction for direct playback of a cut + +2004-11-01 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_frame.c, src/modules/core/transition_mix.c: audio mix and + repeated frames + +2004-10-17 lilo_booter + + * 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 + + * src/modules/valerie/consumer_valerie.c: Error property for valerie returned + + +2004-10-14 lilo_booter + + * 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 + + * 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 + + * src/modules/avformat/configure, src/modules/avformat/producer_avformat.c: + Fix for current cvs + +2004-10-09 lilo_booter + + * src/framework/mlt_playlist.c: Mix on Mix and length corrections + +2004-10-08 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/inigo/producer_inigo.c: Allow filter attachment to clip + +2004-10-02 lilo_booter + + * 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 + + * 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 + + * 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 + + * src/modules/core/filter_rescale.c, src/modules/core/transition_composite.c: + First attempt at a composite clean up + +2004-09-26 lilo_booter + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_tractor.c: Alpha from the tractor fix + +2004-09-22 lilo_booter + + * 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 + + * demo/consumers.ini, src/modules/avformat/producer_avformat.c, + src/modules/gtk2/producer_pixbuf.c: Minor fixes + +2004-09-19 lilo_booter + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_frame.c, src/modules/core/transition_luma.c: Work arounds + for scaling related issues + +2004-09-13 lilo_booter + + * src/modules/avformat/producer_avformat.c: position fixing + +2004-09-09 lilo_booter + + * 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 + + * 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 + + * docs/services.txt, docs/westley.txt, + src/modules/westley/producer_westley.c: Major westley rewrite - allows + attachable filters + +2004-09-06 lilo_booter + + * 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 + + * src/framework/mlt_multitrack.c, src/framework/mlt_tractor.c: Multitrack and + tractor producer-changed event + +2004-09-02 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_service.c: NULL accpectance for connect/disconnect + +2004-08-16 lilo_booter + + * 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 + + * src/modules/avformat/producer_avformat.c: gop/b frame fix, http/pipe + handling and logging off + +2004-08-10 lilo_booter + + * 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 + + * src/modules/avformat/producer_avformat.c: Rudimentary rgb24 support + + * src/modules/avformat/producer_avformat.c: optimisations + +2004-08-07 lilo_booter + + * src/modules/core/transition_region.c: Flexible and animated shapes + +2004-08-05 lilo_booter + + * 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 + + * src/modules/core/filter_watermark.c, src/modules/core/transition_region.c: + Mutable shapes on regions + +2004-08-02 lilo_booter + + * 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 + + * 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 + + * 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 + + * src/modules/core/filter_watermark.c, + src/modules/core/transition_composite.c: More mutable properties + +2004-07-26 lilo_booter + + * 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 + + * 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 + + * src/modules/avformat/producer_avformat.c: Pipe support for audio or video + only + +2004-07-15 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/plus/transition_affine.c: Affine silliness + +2004-06-14 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/avformat/producer_avformat.c: Removed unecessary locks in + avformat + +2004-05-07 lilo_booter + + * src/modules/sdl/consumer_sdl.c: audio off + +2004-05-06 lilo_booter + + * src/modules/sdl/consumer_sdl.c: aspect ratio and locking + +2004-05-06 ddennedy + + * 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 + + * 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 + + * src/modules/sdl/consumer_sdl.c: last sdl fix for now (sigh) + +2004-05-03 lilo_booter + + * 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 + + * src/framework/mlt_consumer.c: audio/video processing swap + +2004-05-02 ddennedy + + * src/modules/sox/filter_sox.c: fix st.h include + +2004-05-02 lilo_booter + + * src/framework/mlt_frame.c: test card handling + +2004-05-01 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * demo/demo: remove setenv call + +2004-04-02 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/miracle/configure: make install part 2 - building configs + +2004-03-26 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_tractor.c: tractor fix + +2004-03-24 ddennedy + + * 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 + + * 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 + + * 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 + + * demo/demo.ini, demo/mlt_jcut, demo/mlt_jcut2: added J Cut demos + +2004-03-23 lilo_booter + + * 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 + + * 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 + + * 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 + + * src/modules/westley/producer_westley.c: null pointer check in end_playlist + +2004-03-22 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_consumer.c, src/modules/dv/consumer_libdv.c: experimental + tuning + +2004-03-04 ddennedy + + * src/modules/xine/attributes.h, src/modules/xine/xineutils.h: add missing + header + +2004-03-04 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/sdl/consumer_sdl.c: More SDL fixes + +2004-03-01 lilo_booter + + * src/modules/sdl/consumer_sdl.c: yet more sdl hacks + +2004-03-01 ddennedy + + * 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 + + * 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 + + * 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 + + * src/modules/gtk2/scale_line_22_yuv_mmx.S: bugfix mmx scaling with + performance loss :-( + +2004-02-27 lilo_booter + + * src/framework/mlt_frame.c, src/modules/core/filter_resize.c, + src/modules/dv/consumer_libdv.c: Scaling experimentation + +2004-02-27 ddennedy + + * 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 + + * src/modules/core/transition_composite.c: composite alignment fix + +2004-02-26 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c: + composite alpha operations, make obscure alpha aware + +2004-02-21 ddennedy + + * 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 + + * 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 + + * 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 + + * src/modules/core/transition_luma.c: more dissolve optimisation + + * src/modules/core/transition_luma.c: optimise dissolve case + +2004-02-19 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_producer.c: test card handling + + * src/miracle/miracle_local.c: optional segv handling + +2004-02-11 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_filter.c, src/framework/mlt_frame.c: filter fixes + +2004-02-09 ddennedy + + * src/modules/core/filter_volume.c: remove spurious return in get_audio + +2004-02-09 lilo_booter + + * 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 + + * 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 + + * src/modules/westley/producer_westley.c: support in/out on entry and track + +2004-02-07 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * src/framework/mlt_frame.c: updated affine + +2004-01-30 ddennedy + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * README, docs/testing-20040110.txt, mlt/README, + mlt/docs/testing-20040110.txt, mlt/setenv, setenv: minor doc updates + +2004-01-12 lilo_booter + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..f876c579 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1462 @@ +# 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 , where is the value of +# the FILE_VERSION_FILTER tag, and 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 , where +# is the value of the INPUT_FILTER tag, and 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 +# Qt Help Project / Namespace. + +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 +# Qt Help Project / Virtual Folders. + +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 diff --git a/GPL b/GPL new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/GPL @@ -0,0 +1,340 @@ + 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..264fdaa1 --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +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)" diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..f96d4ad1 --- /dev/null +++ b/NEWS @@ -0,0 +1,173 @@ +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 diff --git a/README b/README new file mode 100644 index 00000000..aaac6e23 --- /dev/null +++ b/README @@ -0,0 +1,65 @@ +MLT/Miracle README +------------------ + + Sponsored by Ushodaya Enterprises Limited + Written by Charles Yates + and Dan Dennedy + + 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. diff --git a/configure b/configure new file mode 100755 index 00000000..72bbfe51 --- /dev/null +++ b/configure @@ -0,0 +1,248 @@ +#!/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" ) diff --git a/demo/README b/demo/README new file mode 100644 index 00000000..a9a5a087 --- /dev/null +++ b/demo/README @@ -0,0 +1,217 @@ +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. diff --git a/demo/circle.png b/demo/circle.png new file mode 100644 index 00000000..b5979286 Binary files /dev/null and b/demo/circle.png differ diff --git a/demo/circle.svg b/demo/circle.svg new file mode 100644 index 00000000..476ef3ce --- /dev/null +++ b/demo/circle.svg @@ -0,0 +1 @@ + diff --git a/demo/consumers.ini b/demo/consumers.ini new file mode 100644 index 00000000..b02695fc --- /dev/null +++ b/demo/consumers.ini @@ -0,0 +1,12 @@ +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 diff --git a/demo/demo b/demo/demo new file mode 100755 index 00000000..98777ca7 --- /dev/null +++ b/demo/demo @@ -0,0 +1,106 @@ +#!/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 diff --git a/demo/demo.ini b/demo/demo.ini new file mode 100644 index 00000000..b01b0493 --- /dev/null +++ b/demo/demo.ini @@ -0,0 +1,28 @@ +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 diff --git a/demo/demo.kino b/demo/demo.kino new file mode 100644 index 00000000..99e8767c --- /dev/null +++ b/demo/demo.kino @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/demo/entity.westley b/demo/entity.westley new file mode 100644 index 00000000..0f6a6b94 --- /dev/null +++ b/demo/entity.westley @@ -0,0 +1,11 @@ + + +]> + + + pango + Hello &name;, +My name is Inigo Montoya. + + diff --git a/demo/luma1.pgm b/demo/luma1.pgm new file mode 100644 index 00000000..ac689e5b Binary files /dev/null and b/demo/luma1.pgm differ diff --git a/demo/mlt_all b/demo/mlt_all new file mode 100644 index 00000000..d563da88 --- /dev/null +++ b/demo/mlt_all @@ -0,0 +1,3 @@ +inigo \ +clip* \ +$* diff --git a/demo/mlt_attributes b/demo/mlt_attributes new file mode 100644 index 00000000..e4fffb5e --- /dev/null +++ b/demo/mlt_attributes @@ -0,0 +1,7 @@ +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 \ +$* diff --git a/demo/mlt_audio_stuff b/demo/mlt_audio_stuff new file mode 100644 index 00000000..c0074c1a --- /dev/null +++ b/demo/mlt_audio_stuff @@ -0,0 +1,6 @@ +inigo \ +clip*.dv \ +-track music1.ogg \ +-filter volume:0.5 normalise= track=0 \ +-transition mix out=9999 a_track=0 b_track=1 \ +$* diff --git a/demo/mlt_avantika_title b/demo/mlt_avantika_title new file mode 100644 index 00000000..89d0242d --- /dev/null +++ b/demo/mlt_avantika_title @@ -0,0 +1,3 @@ +inigo \ +pango.westley \ +$* diff --git a/demo/mlt_bouncy b/demo/mlt_bouncy new file mode 100644 index 00000000..5315ec9b --- /dev/null +++ b/demo/mlt_bouncy @@ -0,0 +1,10 @@ +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 \ +$* diff --git a/demo/mlt_bouncy_ball b/demo/mlt_bouncy_ball new file mode 100644 index 00000000..1aedb5cd --- /dev/null +++ b/demo/mlt_bouncy_ball @@ -0,0 +1,14 @@ +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 \ +$* diff --git a/demo/mlt_clock_in_and_out b/demo/mlt_clock_in_and_out new file mode 100644 index 00000000..f19dd5ef --- /dev/null +++ b/demo/mlt_clock_in_and_out @@ -0,0 +1,7 @@ +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 \ +$* diff --git a/demo/mlt_composite_transition b/demo/mlt_composite_transition new file mode 100644 index 00000000..cc38ec84 --- /dev/null +++ b/demo/mlt_composite_transition @@ -0,0 +1,7 @@ +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 \ +$* diff --git a/demo/mlt_effect_in_middle b/demo/mlt_effect_in_middle new file mode 100644 index 00000000..0fc3974e --- /dev/null +++ b/demo/mlt_effect_in_middle @@ -0,0 +1,4 @@ +inigo \ +clip1.mpeg in=100 out=500 \ +-filter greyscale in=100 out=199 \ +$* diff --git a/demo/mlt_fade_black b/demo/mlt_fade_black new file mode 100644 index 00000000..376fe865 --- /dev/null +++ b/demo/mlt_fade_black @@ -0,0 +1,9 @@ +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 \ +$* diff --git a/demo/mlt_fade_in_and_out b/demo/mlt_fade_in_and_out new file mode 100644 index 00000000..fbe1fa35 --- /dev/null +++ b/demo/mlt_fade_in_and_out @@ -0,0 +1,9 @@ +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 \ +$* diff --git a/demo/mlt_intro b/demo/mlt_intro new file mode 100644 index 00000000..4b008489 --- /dev/null +++ b/demo/mlt_intro @@ -0,0 +1,9 @@ +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 \ +$* diff --git a/demo/mlt_jcut b/demo/mlt_jcut new file mode 100644 index 00000000..a5d844ef --- /dev/null +++ b/demo/mlt_jcut @@ -0,0 +1,10 @@ +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 \ +$* diff --git a/demo/mlt_lcut b/demo/mlt_lcut new file mode 100644 index 00000000..45f81ddc --- /dev/null +++ b/demo/mlt_lcut @@ -0,0 +1,12 @@ +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 \ +$* diff --git a/demo/mlt_levels b/demo/mlt_levels new file mode 100644 index 00000000..16bf9f52 --- /dev/null +++ b/demo/mlt_levels @@ -0,0 +1,5 @@ +inigo \ +*.dv \ +-filter gamma:1.5 \ +-filter volume normalise=-20db \ +$* diff --git a/demo/mlt_my_name_is b/demo/mlt_my_name_is new file mode 100644 index 00000000..bd6bc3f4 --- /dev/null +++ b/demo/mlt_my_name_is @@ -0,0 +1,10 @@ +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 \ +$* diff --git a/demo/mlt_news b/demo/mlt_news new file mode 100644 index 00000000..3399f1f2 --- /dev/null +++ b/demo/mlt_news @@ -0,0 +1,18 @@ +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 \ +$* diff --git a/demo/mlt_obscure b/demo/mlt_obscure new file mode 100644 index 00000000..0c0f8382 --- /dev/null +++ b/demo/mlt_obscure @@ -0,0 +1,5 @@ +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 \ +$* diff --git a/demo/mlt_push b/demo/mlt_push new file mode 100644 index 00000000..bcbc8bf7 --- /dev/null +++ b/demo/mlt_push @@ -0,0 +1,18 @@ +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 \ +$* diff --git a/demo/mlt_slideshow b/demo/mlt_slideshow new file mode 100644 index 00000000..efe0d06d --- /dev/null +++ b/demo/mlt_slideshow @@ -0,0 +1,4 @@ +inigo \ +Scotland/.all.jpg ttl=75 \ +-filter luma:luma1.pgm luma.softness=0.1 luma.invert=0 \ +$* diff --git a/demo/mlt_slideshow_black b/demo/mlt_slideshow_black new file mode 100644 index 00000000..ddbf5954 --- /dev/null +++ b/demo/mlt_slideshow_black @@ -0,0 +1,3 @@ +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 \ +$* diff --git a/demo/mlt_squeeze b/demo/mlt_squeeze new file mode 100644 index 00000000..a56780cf --- /dev/null +++ b/demo/mlt_squeeze @@ -0,0 +1,9 @@ +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 \ +$* diff --git a/demo/mlt_squeeze_box b/demo/mlt_squeeze_box new file mode 100644 index 00000000..dfa0ced9 --- /dev/null +++ b/demo/mlt_squeeze_box @@ -0,0 +1,9 @@ +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 \ +$* diff --git a/demo/mlt_ticker b/demo/mlt_ticker new file mode 100644 index 00000000..f6a8329d --- /dev/null +++ b/demo/mlt_ticker @@ -0,0 +1,15 @@ +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% \ +$* diff --git a/demo/mlt_title_over_gfx b/demo/mlt_title_over_gfx new file mode 100644 index 00000000..e9b7f662 --- /dev/null +++ b/demo/mlt_title_over_gfx @@ -0,0 +1,25 @@ +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 \ +$* diff --git a/demo/mlt_titleshadow_watermark b/demo/mlt_titleshadow_watermark new file mode 100644 index 00000000..79ef8aec --- /dev/null +++ b/demo/mlt_titleshadow_watermark @@ -0,0 +1,10 @@ +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 \ +$* diff --git a/demo/mlt_voiceover b/demo/mlt_voiceover new file mode 100644 index 00000000..338f4d8d --- /dev/null +++ b/demo/mlt_voiceover @@ -0,0 +1,36 @@ +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 \ +$* diff --git a/demo/mlt_watermark b/demo/mlt_watermark new file mode 100644 index 00000000..b832d5be --- /dev/null +++ b/demo/mlt_watermark @@ -0,0 +1,6 @@ +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% \ +$* diff --git a/demo/new.westley b/demo/new.westley new file mode 100644 index 00000000..c623b3ec --- /dev/null +++ b/demo/new.westley @@ -0,0 +1,51 @@ + + + + + + clip2.mpeg + + + + + + + + clip3.mpeg + + + + + + greyscale + + + luma + + + + + + + + + + + + + + + + + + + + + luma + + + 1 + + + + diff --git a/demo/pango.westley b/demo/pango.westley new file mode 100644 index 00000000..4684d48c --- /dev/null +++ b/demo/pango.westley @@ -0,0 +1,34 @@ + + + + clip1.dv + + + pango + +.txt + GJ-TTAvantika 36 + 1 + 0xffffddff + 0x8c101080 + 8 + + + + + + + + + composite + 1 + 0 + -70%,65%:100%x35%:0 + 0,65%:100%x35%:100 + 0,65%:100%x35%:100 + 0,65%:100%x35%:0 + centre + centre + + + diff --git a/demo/svg.westley b/demo/svg.westley new file mode 100644 index 00000000..a41ff4a5 --- /dev/null +++ b/demo/svg.westley @@ -0,0 +1,50 @@ + + + + + + + + +]> + + + + + pixbuf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/watermark1.png b/demo/watermark1.png new file mode 100644 index 00000000..1ec3e577 Binary files /dev/null and b/demo/watermark1.png differ diff --git a/docs/TODO b/docs/TODO new file mode 100644 index 00000000..ec188f59 --- /dev/null +++ b/docs/TODO @@ -0,0 +1 @@ +See http://www.mltframework.org/twiki/bin/view/MLT/ToDo diff --git a/docs/dvcp.txt b/docs/dvcp.txt new file mode 100644 index 00000000..18da20fb --- /dev/null +++ b/docs/dvcp.txt @@ -0,0 +1,339 @@ +Miracle Control Protocol (DVCP) Reference Documentation + +Copyright (C) 2004 Ushodaya Enterprised Limited +Author: Dan Dennedy +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." + + + + diff --git a/docs/framework.txt b/docs/framework.txt new file mode 100644 index 00000000..1c2bafb0 --- /dev/null +++ b/docs/framework.txt @@ -0,0 +1,1341 @@ +Framework Documentation + +Copyright (C) 2004 Ushodaya Enterprises Limited +Author: Charles Yates +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 + #include + #include + + 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)|"" | + +------------------+------------------------------------+------------------+ + + 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: diff --git a/docs/inigo.txt b/docs/inigo.txt new file mode 100644 index 00000000..854c1c12 --- /dev/null +++ b/docs/inigo.txt @@ -0,0 +1,401 @@ +Inigo Documentation + +Copyright (C) 2004-2009 Ushodaya Enterprised Limited +Author: Charles Yates +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 + diff --git a/docs/install.txt b/docs/install.txt new file mode 100644 index 00000000..b8883229 --- /dev/null +++ b/docs/install.txt @@ -0,0 +1,187 @@ +Installation Documentation + +Copyright (C) 2004 Ushodaya Enterprises Limited +Author: Charles Yates +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 + + All external modules have {prefix}/include/mlt on the include path. All + headers should also be included as: + + #include + + This allows migration of source between external and internal modules. + The configuration and Makefile template requirements will require + attention though. diff --git a/docs/policies.txt b/docs/policies.txt new file mode 100644 index 00000000..f2fb0336 --- /dev/null +++ b/docs/policies.txt @@ -0,0 +1,57 @@ +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 diff --git a/docs/services.txt b/docs/services.txt new file mode 100644 index 00000000..63d6c5a3 --- /dev/null +++ b/docs/services.txt @@ -0,0 +1,1582 @@ +Service Documentation + +Authors: Charles Yates + Dan Dennedy +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 "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. diff --git a/docs/testing-20040110.txt b/docs/testing-20040110.txt new file mode 100644 index 00000000..14c8dcd1 --- /dev/null +++ b/docs/testing-20040110.txt @@ -0,0 +1,35 @@ +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. + + + diff --git a/docs/testing.txt b/docs/testing.txt new file mode 100644 index 00000000..2886989d --- /dev/null +++ b/docs/testing.txt @@ -0,0 +1,599 @@ +Miracle Test Procedure + +Copyright (C) 2003 Ushodaya Enterprised Limited +Author: Dan Dennedy +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 diff --git a/docs/valerie.txt b/docs/valerie.txt new file mode 100644 index 00000000..9a90c867 --- /dev/null +++ b/docs/valerie.txt @@ -0,0 +1,861 @@ +Valerie API Documentation + +Copyright (C) 2004 Ushodaya Enterprised Limited +Author: Charles Yates +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 + + 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 + + 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 + + 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 /include + + where prefix defaults to /usr/local. + + Linking flags for a client are: + + -L /lib/ -lvalerie + + Or for a local parser: + + -L /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 diff --git a/docs/westley.txt b/docs/westley.txt new file mode 100644 index 00000000..1f42538d --- /dev/null +++ b/docs/westley.txt @@ -0,0 +1,574 @@ +Westley Documentation + +Copyright (C) 2004 Ushodaya Enterprised Limited +Authors: Charles Yates +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: + + + + clip1.dv + + + + 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: + + + + clip1.mpeg + 1 + + + + 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: + + + + clip1.dv + + + clip2.dv + + + + + + + + + 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: + + + + clip1.dv + + + clip2.dv + + + + + + + + + + + + + + + + 0 + greyscale + + + + + 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: + + + + clip1.dv + + + clip2.dv + + + + + + + + + + + + + + + + 0 + greyscale + + + + + + + + watermark + watermark1.png + + + + + 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. + + + + clip1.dv + + + + + + clip2.mpeg + + + + + + + + + + + + 0 + 1 + luma + + + 0 + 1 + mix + 0.0 + 1.0 + + + + + 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 + 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: + + + + + + + + + clip1.dv + + + + + + + + + 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 , , and 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: + + + + + + clip1.dv + + + + + + Yes, filters and transitions can be added to the above example after the + closing multitrack tag () 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 or , then the edit points apply to the playlist + entry and not to the producer itself. This facilitates re-use of media: + + + + clip1.dv + + + + + 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: + + + + + + clip2.mpeg + + + + + + + + clip3.mpeg + + + + + + + + + + + + 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: + + ... ]]> + + 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: + + + + + + + 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: + + + + ]> + + + pango + &msg; + + + + 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: + + + + + ...add a playlist for each track... + + ...add filters and transitions... + + + 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 or later add when necessary. + + A more advanced template that allows sequencing multitracks is: + + + + + + ...add a playlist for each track... + + ...add filters and transitions... + + + + + + + ...add a playlist for each track... + + ...add filters and transitions... + + + + 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 . 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). diff --git a/mlt-config-template b/mlt-config-template new file mode 100644 index 00000000..607d1787 --- /dev/null +++ b/mlt-config-template @@ -0,0 +1,32 @@ +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." diff --git a/mlt-framework.pc.in b/mlt-framework.pc.in new file mode 100644 index 00000000..5748867b --- /dev/null +++ b/mlt-framework.pc.in @@ -0,0 +1,7 @@ + +Name: mlt-framework +Description: MLT multimedia framework +Version: ${version} +Requires: +Libs: -L${libdir} ${libs} +Cflags: ${cflags} diff --git a/mlt-miracle.pc.in b/mlt-miracle.pc.in new file mode 100644 index 00000000..593055e8 --- /dev/null +++ b/mlt-miracle.pc.in @@ -0,0 +1,7 @@ + +Name: mlt-miracle +Description: MLT Miracle server API +Version: ${version} +Requires: +Libs: -L${libdir} ${libs} +Cflags: ${cflags} diff --git a/mlt-valerie.pc.in b/mlt-valerie.pc.in new file mode 100644 index 00000000..7750ba3a --- /dev/null +++ b/mlt-valerie.pc.in @@ -0,0 +1,7 @@ + +Name: mlt-valerie +Description: MLT Valerie client API +Version: ${version} +Requires: +Libs: -L${libdir} ${libs} +Cflags: ${cflags} diff --git a/profiles/Makefile b/profiles/Makefile new file mode 100644 index 00000000..9ad9f62c --- /dev/null +++ b/profiles/Makefile @@ -0,0 +1,18 @@ +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" diff --git a/profiles/atsc_1080i_50 b/profiles/atsc_1080i_50 new file mode 100644 index 00000000..4309b0fb --- /dev/null +++ b/profiles/atsc_1080i_50 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_1080i_60 b/profiles/atsc_1080i_60 new file mode 100644 index 00000000..3c95886a --- /dev/null +++ b/profiles/atsc_1080i_60 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_1080p_2398 b/profiles/atsc_1080p_2398 new file mode 100644 index 00000000..e8a14153 --- /dev/null +++ b/profiles/atsc_1080p_2398 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_1080p_24 b/profiles/atsc_1080p_24 new file mode 100644 index 00000000..49f64226 --- /dev/null +++ b/profiles/atsc_1080p_24 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_1080p_25 b/profiles/atsc_1080p_25 new file mode 100644 index 00000000..769200ac --- /dev/null +++ b/profiles/atsc_1080p_25 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_1080p_2997 b/profiles/atsc_1080p_2997 new file mode 100644 index 00000000..9633cd61 --- /dev/null +++ b/profiles/atsc_1080p_2997 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_1080p_30 b/profiles/atsc_1080p_30 new file mode 100644 index 00000000..1b002388 --- /dev/null +++ b/profiles/atsc_1080p_30 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/atsc_720p_30 b/profiles/atsc_720p_30 new file mode 100644 index 00000000..beb6cc9a --- /dev/null +++ b/profiles/atsc_720p_30 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/cif_15 b/profiles/cif_15 new file mode 100644 index 00000000..7b590d26 --- /dev/null +++ b/profiles/cif_15 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/cif_ntsc b/profiles/cif_ntsc new file mode 100644 index 00000000..48279a5e --- /dev/null +++ b/profiles/cif_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/cif_pal b/profiles/cif_pal new file mode 100644 index 00000000..a7f66d41 --- /dev/null +++ b/profiles/cif_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/cvd_ntsc b/profiles/cvd_ntsc new file mode 100644 index 00000000..508b3cc1 --- /dev/null +++ b/profiles/cvd_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/cvd_pal b/profiles/cvd_pal new file mode 100644 index 00000000..2415c77d --- /dev/null +++ b/profiles/cvd_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/dv_ntsc b/profiles/dv_ntsc new file mode 100644 index 00000000..ff1f24e6 --- /dev/null +++ b/profiles/dv_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/dv_ntsc_wide b/profiles/dv_ntsc_wide new file mode 100644 index 00000000..d512d6ce --- /dev/null +++ b/profiles/dv_ntsc_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/dv_pal b/profiles/dv_pal new file mode 100644 index 00000000..d5691400 --- /dev/null +++ b/profiles/dv_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/dv_pal_wide b/profiles/dv_pal_wide new file mode 100644 index 00000000..a52b9cff --- /dev/null +++ b/profiles/dv_pal_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_1080_25p b/profiles/hdv_1080_25p new file mode 100644 index 00000000..bba72e80 --- /dev/null +++ b/profiles/hdv_1080_25p @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_1080_30p b/profiles/hdv_1080_30p new file mode 100644 index 00000000..73213641 --- /dev/null +++ b/profiles/hdv_1080_30p @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_1080_50i b/profiles/hdv_1080_50i new file mode 100644 index 00000000..6544470c --- /dev/null +++ b/profiles/hdv_1080_50i @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_1080_60i b/profiles/hdv_1080_60i new file mode 100644 index 00000000..9c3e6fc4 --- /dev/null +++ b/profiles/hdv_1080_60i @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_720_25p b/profiles/hdv_720_25p new file mode 100644 index 00000000..7bd6eed8 --- /dev/null +++ b/profiles/hdv_720_25p @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_720_30p b/profiles/hdv_720_30p new file mode 100644 index 00000000..68caaf9e --- /dev/null +++ b/profiles/hdv_720_30p @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_720_50p b/profiles/hdv_720_50p new file mode 100644 index 00000000..2a93230d --- /dev/null +++ b/profiles/hdv_720_50p @@ -0,0 +1,10 @@ +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 diff --git a/profiles/hdv_720_60p b/profiles/hdv_720_60p new file mode 100644 index 00000000..11189cba --- /dev/null +++ b/profiles/hdv_720_60p @@ -0,0 +1,10 @@ +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 diff --git a/profiles/qcif_15 b/profiles/qcif_15 new file mode 100644 index 00000000..70d286d3 --- /dev/null +++ b/profiles/qcif_15 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/qcif_ntsc b/profiles/qcif_ntsc new file mode 100644 index 00000000..0e901577 --- /dev/null +++ b/profiles/qcif_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/qcif_pal b/profiles/qcif_pal new file mode 100644 index 00000000..21667ee8 --- /dev/null +++ b/profiles/qcif_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/quarter_15 b/profiles/quarter_15 new file mode 100644 index 00000000..7ba8eef3 --- /dev/null +++ b/profiles/quarter_15 @@ -0,0 +1,10 @@ +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 diff --git a/profiles/quarter_ntsc b/profiles/quarter_ntsc new file mode 100644 index 00000000..fb37cd76 --- /dev/null +++ b/profiles/quarter_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/quarter_ntsc_wide b/profiles/quarter_ntsc_wide new file mode 100644 index 00000000..8c7be946 --- /dev/null +++ b/profiles/quarter_ntsc_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/quarter_pal b/profiles/quarter_pal new file mode 100644 index 00000000..beec2071 --- /dev/null +++ b/profiles/quarter_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/quarter_pal_wide b/profiles/quarter_pal_wide new file mode 100644 index 00000000..195410aa --- /dev/null +++ b/profiles/quarter_pal_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/square_ntsc b/profiles/square_ntsc new file mode 100644 index 00000000..e139c3e5 --- /dev/null +++ b/profiles/square_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/square_ntsc_wide b/profiles/square_ntsc_wide new file mode 100644 index 00000000..da2de3ef --- /dev/null +++ b/profiles/square_ntsc_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/square_pal b/profiles/square_pal new file mode 100644 index 00000000..a69c8d64 --- /dev/null +++ b/profiles/square_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/square_pal_wide b/profiles/square_pal_wide new file mode 100644 index 00000000..316a80d7 --- /dev/null +++ b/profiles/square_pal_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/svcd_ntsc b/profiles/svcd_ntsc new file mode 100644 index 00000000..fd6f13ae --- /dev/null +++ b/profiles/svcd_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/svcd_ntsc_wide b/profiles/svcd_ntsc_wide new file mode 100644 index 00000000..174960a6 --- /dev/null +++ b/profiles/svcd_ntsc_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/svcd_pal b/profiles/svcd_pal new file mode 100644 index 00000000..20492702 --- /dev/null +++ b/profiles/svcd_pal @@ -0,0 +1,10 @@ +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 diff --git a/profiles/svcd_pal_wide b/profiles/svcd_pal_wide new file mode 100644 index 00000000..3aea87a4 --- /dev/null +++ b/profiles/svcd_pal_wide @@ -0,0 +1,10 @@ +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 diff --git a/profiles/vcd_ntsc b/profiles/vcd_ntsc new file mode 100644 index 00000000..e58f4f2c --- /dev/null +++ b/profiles/vcd_ntsc @@ -0,0 +1,10 @@ +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 diff --git a/profiles/vcd_pal b/profiles/vcd_pal new file mode 100644 index 00000000..5f4e03f8 --- /dev/null +++ b/profiles/vcd_pal @@ -0,0 +1,10 @@ +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 diff --git a/setenv b/setenv new file mode 100644 index 00000000..53b5f6ed --- /dev/null +++ b/setenv @@ -0,0 +1,26 @@ + +# 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 diff --git a/setenv_mc b/setenv_mc new file mode 100644 index 00000000..705d4e2c --- /dev/null +++ b/setenv_mc @@ -0,0 +1,9 @@ + +# 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 + diff --git a/src/albino/Makefile b/src/albino/Makefile new file mode 100644 index 00000000..29bb3ee6 --- /dev/null +++ b/src/albino/Makefile @@ -0,0 +1,38 @@ +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 diff --git a/src/albino/albino.c b/src/albino/albino.c new file mode 100644 index 00000000..3979a515 --- /dev/null +++ b/src/albino/albino.c @@ -0,0 +1,105 @@ +/* + * albino.c -- Local Valerie/Miracle Test Utility + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include +#include + +#ifdef __DARWIN__ +#include +#endif + +/* Application header files */ +#include +#include +#include + +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; +} diff --git a/src/framework/Makefile b/src/framework/Makefile new file mode 100644 index 00000000..58b3899c --- /dev/null +++ b/src/framework/Makefile @@ -0,0 +1,109 @@ +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 diff --git a/src/framework/configure b/src/framework/configure new file mode 100755 index 00000000..52655ae6 --- /dev/null +++ b/src/framework/configure @@ -0,0 +1,2 @@ +#!/bin/sh +echo "framework -I$prefix/include -I$prefix/include/mlt -D_REENTRANT -L$libdir -lmlt" >> ../../packages.dat diff --git a/src/framework/metaschema.yaml b/src/framework/metaschema.yaml new file mode 100644 index 00000000..9c7c78f3 --- /dev/null +++ b/src/framework/metaschema.yaml @@ -0,0 +1,129 @@ +--- # 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 + diff --git a/src/framework/mlt.h b/src/framework/mlt.h new file mode 100644 index 00000000..68365402 --- /dev/null +++ b/src/framework/mlt.h @@ -0,0 +1,58 @@ +/** + * \file mlt.h + * \brief header file for lazy client and implementation code :-) + * + * Copyright (C) 2003-2009 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 + */ + +#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 + diff --git a/src/framework/mlt_cache.c b/src/framework/mlt_cache.c new file mode 100644 index 00000000..5a188719 --- /dev/null +++ b/src/framework/mlt_cache.c @@ -0,0 +1,451 @@ +/** + * \file mlt_profile.c + * \brief least recently used cache + * \see mlt_profile_s + * + * Copyright (C) 2007-2009 Ushodaya Enterprises Limited + * \author Dan Dennedy + * + * 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 +#include + +/** 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; +} diff --git a/src/framework/mlt_cache.h b/src/framework/mlt_cache.h new file mode 100644 index 00000000..aaec935c --- /dev/null +++ b/src/framework/mlt_cache.h @@ -0,0 +1,38 @@ +/** + * \file mlt_cache.h + * \brief least recently used cache + * \see mlt_cache_s + * + * Copyright (C) 2007-2009 Ushodaya Enterprises Limited + * \author Dan Dennedy + * + * 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 diff --git a/src/framework/mlt_consumer.c b/src/framework/mlt_consumer.c new file mode 100644 index 00000000..93afe447 --- /dev/null +++ b/src/framework/mlt_consumer.c @@ -0,0 +1,1010 @@ +/** + * \file mlt_consumer.c + * \brief abstraction for all consumer services + * \see mlt_consumer_s + * + * Copyright (C) 2003-2009 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 "mlt_consumer.h" +#include "mlt_factory.h" +#include "mlt_producer.h" +#include "mlt_frame.h" +#include "mlt_profile.h" +#include "mlt_log.h" + +#include +#include +#include +#include + +/** 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 ); + } + } +} diff --git a/src/framework/mlt_consumer.h b/src/framework/mlt_consumer.h new file mode 100644 index 00000000..736d0a2c --- /dev/null +++ b/src/framework/mlt_consumer.h @@ -0,0 +1,135 @@ +/** + * \file mlt_consumer.h + * \brief abstraction for all consumer services + * \see mlt_consumer_s + * + * Copyright (C) 2003-2009 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 + */ + +#ifndef _MLT_CONSUMER_H_ +#define _MLT_CONSUMER_H_ + +#include "mlt_service.h" +#include "mlt_events.h" +#include + +/** \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 diff --git a/src/framework/mlt_deque.c b/src/framework/mlt_deque.c new file mode 100644 index 00000000..ad65a2b3 --- /dev/null +++ b/src/framework/mlt_deque.c @@ -0,0 +1,398 @@ +/** + * \file mlt_deque.c + * \brief double ended queue + * \see mlt_deque_s + * + * Copyright (C) 2003-2009 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 + */ + +// Local header files +#include "mlt_deque.h" + +// System header files +#include +#include + +/** \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 ); +} diff --git a/src/framework/mlt_deque.h b/src/framework/mlt_deque.h new file mode 100644 index 00000000..d895798e --- /dev/null +++ b/src/framework/mlt_deque.h @@ -0,0 +1,54 @@ +/** + * \file mlt_deque.h + * \brief double ended queue + * \see mlt_deque_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_events.c b/src/framework/mlt_events.c new file mode 100644 index 00000000..5c841901 --- /dev/null +++ b/src/framework/mlt_events.c @@ -0,0 +1,503 @@ +/** + * \file mlt_events.c + * \brief event handling + * \see mlt_events_struct + * + * Copyright (C) 2004-2009 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 +#include +#include +#include + +#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 ); + } +} diff --git a/src/framework/mlt_events.h b/src/framework/mlt_events.h new file mode 100644 index 00000000..55bb18f4 --- /dev/null +++ b/src/framework/mlt_events.h @@ -0,0 +1,63 @@ +/** + * \file mlt_events.h + * \brief event handling + * \see mlt_events_struct + * + * Copyright (C) 2004-2009 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 + */ + +#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 + diff --git a/src/framework/mlt_factory.c b/src/framework/mlt_factory.c new file mode 100644 index 00000000..552b8ce3 --- /dev/null +++ b/src/framework/mlt_factory.c @@ -0,0 +1,378 @@ +/** + * \file mlt_factory.c + * \brief the factory method interfaces + * + * Copyright (C) 2003-2009 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 "mlt.h" +#include "mlt_repository.h" + +#include +#include +#include + +/** 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; +} diff --git a/src/framework/mlt_factory.h b/src/framework/mlt_factory.h new file mode 100644 index 00000000..29dc62a9 --- /dev/null +++ b/src/framework/mlt_factory.h @@ -0,0 +1,54 @@ +/** + * \file mlt_factory.h + * \brief the factory method interfaces + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_field.c b/src/framework/mlt_field.c new file mode 100644 index 00000000..c988ae6e --- /dev/null +++ b/src/framework/mlt_field.c @@ -0,0 +1,270 @@ +/** + * \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 + * + * 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 +#include + +/** \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 ); +} diff --git a/src/framework/mlt_field.h b/src/framework/mlt_field.h new file mode 100644 index 00000000..a54db542 --- /dev/null +++ b/src/framework/mlt_field.h @@ -0,0 +1,41 @@ +/** + * \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 + * + * 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 + diff --git a/src/framework/mlt_filter.c b/src/framework/mlt_filter.c new file mode 100644 index 00000000..9a04352e --- /dev/null +++ b/src/framework/mlt_filter.c @@ -0,0 +1,275 @@ +/** + * \file mlt_filter.c + * \brief abstraction for all filter services + * \see mlt_filter_s + * + * Copyright (C) 2003-2009 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 "mlt_filter.h" +#include "mlt_frame.h" + +#include +#include +#include + +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 ); + } +} diff --git a/src/framework/mlt_filter.h b/src/framework/mlt_filter.h new file mode 100644 index 00000000..66e2e471 --- /dev/null +++ b/src/framework/mlt_filter.h @@ -0,0 +1,67 @@ +/** + * \file mlt_filter.h + * \brief abstraction for all filter services + * \see mlt_filter_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_frame.c b/src/framework/mlt_frame.c new file mode 100644 index 00000000..0899b065 --- /dev/null +++ b/src/framework/mlt_frame.c @@ -0,0 +1,1314 @@ +/** + * \file mlt_frame.c + * \brief interface for all frame classes + * \see mlt_frame_s + * + * Copyright (C) 2003-2009 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 "mlt_frame.h" +#include "mlt_producer.h" +#include "mlt_factory.h" +#include "mlt_profile.h" + +#include +#include +#include +#include + +/** 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; +} diff --git a/src/framework/mlt_frame.h b/src/framework/mlt_frame.h new file mode 100644 index 00000000..bd586a74 --- /dev/null +++ b/src/framework/mlt_frame.h @@ -0,0 +1,144 @@ +/** + * \file mlt_frame.h + * \brief interface for all frame classes + * \see mlt_frame_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_geometry.c b/src/framework/mlt_geometry.c new file mode 100644 index 00000000..db9c877c --- /dev/null +++ b/src/framework/mlt_geometry.c @@ -0,0 +1,700 @@ +/* + * mlt_geometry.c -- provides the geometry API + * 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 "mlt_geometry.h" +#include "mlt_tokeniser.h" +#include "mlt_factory.h" +#include "mlt_profile.h" + +#include +#include +#include +#include + +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 ); + } +} + + diff --git a/src/framework/mlt_geometry.h b/src/framework/mlt_geometry.h new file mode 100644 index 00000000..81201846 --- /dev/null +++ b/src/framework/mlt_geometry.h @@ -0,0 +1,73 @@ +/* + * mlt_geometry.h -- provides the geometry API + * 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 + */ + +#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 + diff --git a/src/framework/mlt_log.c b/src/framework/mlt_log.c new file mode 100644 index 00000000..9a4c70b5 --- /dev/null +++ b/src/framework/mlt_log.c @@ -0,0 +1,80 @@ +/** + * \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 + +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; +} diff --git a/src/framework/mlt_log.h b/src/framework/mlt_log.h new file mode 100644 index 00000000..ec9a134d --- /dev/null +++ b/src/framework/mlt_log.h @@ -0,0 +1,95 @@ +/** + * \file mlt_log.h + * \brief logging functions + * + * copyright (c) 2006 Michael Niedermayer + * + * 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 + +#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 */ diff --git a/src/framework/mlt_multitrack.c b/src/framework/mlt_multitrack.c new file mode 100644 index 00000000..a98d4120 --- /dev/null +++ b/src/framework/mlt_multitrack.c @@ -0,0 +1,514 @@ +/** + * \file mlt_multitrack.c + * \brief multitrack service class + * \see mlt_multitrack_s + * + * Copyright (C) 2003-2009 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 "mlt_multitrack.h" +#include "mlt_playlist.h" +#include "mlt_frame.h" + +#include +#include + +/* 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 "". + * + * \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", "" ); + 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. + * + *
+ * 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]
+ * 
+ * + * \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. + * + *
+ * 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.
+ * 
+ * + * \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 ); + } +} diff --git a/src/framework/mlt_multitrack.h b/src/framework/mlt_multitrack.h new file mode 100644 index 00000000..427083c8 --- /dev/null +++ b/src/framework/mlt_multitrack.h @@ -0,0 +1,73 @@ +/** + * \file mlt_multitrack.h + * \brief multitrack service class + * \see mlt_multitrack_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 + diff --git a/src/framework/mlt_parser.c b/src/framework/mlt_parser.c new file mode 100644 index 00000000..c5dd4ec3 --- /dev/null +++ b/src/framework/mlt_parser.c @@ -0,0 +1,245 @@ +/** + * \file mlt_parser.c + * \brief service parsing functionality + * \see mlt_parser_s + * + * Copyright (C) 2003-2009 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 "mlt.h" +#include + +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 ); + } +} + + diff --git a/src/framework/mlt_parser.h b/src/framework/mlt_parser.h new file mode 100644 index 00000000..e82149cd --- /dev/null +++ b/src/framework/mlt_parser.h @@ -0,0 +1,60 @@ +/** + * \file mlt_parser.h + * \brief service parsing functionality + * \see mlt_parser_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_playlist.c b/src/framework/mlt_playlist.c new file mode 100644 index 00000000..91f9fa81 --- /dev/null +++ b/src/framework/mlt_playlist.c @@ -0,0 +1,1839 @@ +/** + * \file mlt_playlist.c + * \brief playlist service class + * \see mlt_playlist_s + * + * Copyright (C) 2003-2009 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 "mlt_playlist.h" +#include "mlt_tractor.h" +#include "mlt_multitrack.h" +#include "mlt_field.h" +#include "mlt_frame.h" +#include "mlt_transition.h" + +#include +#include +#include + +/** \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 "". + * 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", "" ); + 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 ); + } +} diff --git a/src/framework/mlt_playlist.h b/src/framework/mlt_playlist.h new file mode 100644 index 00000000..a6364316 --- /dev/null +++ b/src/framework/mlt_playlist.h @@ -0,0 +1,124 @@ +/** + * \file mlt_playlist.h + * \brief playlist service class + * \see mlt_playlist_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 + diff --git a/src/framework/mlt_pool.c b/src/framework/mlt_pool.c new file mode 100644 index 00000000..ebf0081a --- /dev/null +++ b/src/framework/mlt_pool.c @@ -0,0 +1,391 @@ +/** + * \file mlt_pool.c + * \brief memory pooling functionality + * \see mlt_pool_s + * + * Copyright (C) 2003-2009 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 "mlt_properties.h" +#include "mlt_deque.h" + +#include +#include +#include + +// Not nice - memalign is defined here apparently? +#ifdef linux +#include +#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 ); +} + diff --git a/src/framework/mlt_pool.h b/src/framework/mlt_pool.h new file mode 100644 index 00000000..caf983d4 --- /dev/null +++ b/src/framework/mlt_pool.h @@ -0,0 +1,34 @@ +/** + * \file mlt_pool.h + * \brief memory pooling functionality + * \see mlt_pool_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_producer.c b/src/framework/mlt_producer.c new file mode 100644 index 00000000..f43738bd --- /dev/null +++ b/src/framework/mlt_producer.c @@ -0,0 +1,1031 @@ +/** + * \file mlt_producer.c + * \brief abstraction for all producer services + * \see mlt_producer_s + * + * Copyright (C) 2003-2009 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 "mlt_producer.h" +#include "mlt_factory.h" +#include "mlt_frame.h" +#include "mlt_parser.h" +#include "mlt_profile.h" +#include "mlt_log.h" + +#include +#include +#include +#include + +/* 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", "" ); + + // 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 ); + } + } +} diff --git a/src/framework/mlt_producer.h b/src/framework/mlt_producer.h new file mode 100644 index 00000000..37ba2ba8 --- /dev/null +++ b/src/framework/mlt_producer.h @@ -0,0 +1,118 @@ +/** + * \file mlt_producer.h + * \brief abstraction for all producer services + * \see mlt_producer_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_profile.c b/src/framework/mlt_profile.c new file mode 100644 index 00000000..3c016263 --- /dev/null +++ b/src/framework/mlt_profile.c @@ -0,0 +1,303 @@ +/** + * \file mlt_profile.c + * \brief video output definition + * \see mlt_profile_s + * + * Copyright (C) 2007-2009 Ushodaya Enterprises Limited + * \author Dan Dennedy + * + * 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 +#include +#include + + +/** 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; + } +} diff --git a/src/framework/mlt_profile.h b/src/framework/mlt_profile.h new file mode 100644 index 00000000..6245640e --- /dev/null +++ b/src/framework/mlt_profile.h @@ -0,0 +1,55 @@ +/** + * \file mlt_profile.h + * \brief video output definition + * \see mlt_profile_s + * + * Copyright (C) 2007-2009 Ushodaya Enterprises Limited + * \author Dan Dennedy + * + * 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 diff --git a/src/framework/mlt_properties.c b/src/framework/mlt_properties.c new file mode 100644 index 00000000..97ee02de --- /dev/null +++ b/src/framework/mlt_properties.c @@ -0,0 +1,1754 @@ +/** + * \file mlt_properties.c + * \brief Properties class definition + * \see mlt_properties_s + * + * Copyright (C) 2003-2009 Ushodaya Enterprises Limited + * \author Charles Yates + * \author Dan Dennedy + * + * 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 +#include +#include +#include +#include +#include +#include +#include + + +/** \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 + * \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 + * \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; +} diff --git a/src/framework/mlt_properties.h b/src/framework/mlt_properties.h new file mode 100644 index 00000000..c5e15cc1 --- /dev/null +++ b/src/framework/mlt_properties.h @@ -0,0 +1,87 @@ +/** + * \file mlt_properties.h + * \brief Properties class declaration + * \see mlt_properties_s + * + * Copyright (C) 2003-2009 Ushodaya Enterprises Limited + * \author Charles Yates + * \author Dan Dennedy + * + * 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 + +/** \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 diff --git a/src/framework/mlt_property.c b/src/framework/mlt_property.c new file mode 100644 index 00000000..7cbb6819 --- /dev/null +++ b/src/framework/mlt_property.c @@ -0,0 +1,512 @@ +/** + * \file mlt_property.c + * \brief Property class definition + * \see mlt_property_s + * + * Copyright (C) 2003-2009 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 "mlt_property.h" + +#include +#include +#include + + +/** 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 + * \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 ); + } +} diff --git a/src/framework/mlt_property.h b/src/framework/mlt_property.h new file mode 100644 index 00000000..f33aa882 --- /dev/null +++ b/src/framework/mlt_property.h @@ -0,0 +1,46 @@ +/** + * \file mlt_property.h + * \brief Property class declaration + * \see mlt_property_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_repository.c b/src/framework/mlt_repository.c new file mode 100644 index 00000000..c843ecb2 --- /dev/null +++ b/src/framework/mlt_repository.c @@ -0,0 +1,428 @@ +/** + * \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 + * \author Dan Dennedy + * + * 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 +#include +#include +#include + +/** \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; +} diff --git a/src/framework/mlt_repository.h b/src/framework/mlt_repository.h new file mode 100644 index 00000000..a653b490 --- /dev/null +++ b/src/framework/mlt_repository.h @@ -0,0 +1,71 @@ +/** + * \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 + * \author Dan Dennedy + * + * 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 + diff --git a/src/framework/mlt_service.c b/src/framework/mlt_service.c new file mode 100644 index 00000000..b5e8f827 --- /dev/null +++ b/src/framework/mlt_service.c @@ -0,0 +1,738 @@ +/** + * \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 + * + * 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 +#include +#include +#include + + +/* 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, "" ) ) + type = playlist_type; + else if (resource != NULL && !strcmp( resource, "" ) ) + type = tractor_type; + else if (resource != NULL && !strcmp( resource, "" ) ) + 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; +} diff --git a/src/framework/mlt_service.h b/src/framework/mlt_service.h new file mode 100644 index 00000000..ea451bff --- /dev/null +++ b/src/framework/mlt_service.h @@ -0,0 +1,100 @@ +/** + * \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 + * + * 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 + diff --git a/src/framework/mlt_tokeniser.c b/src/framework/mlt_tokeniser.c new file mode 100644 index 00000000..1fa28b03 --- /dev/null +++ b/src/framework/mlt_tokeniser.c @@ -0,0 +1,172 @@ +/** + * \file mlt_tokeniser.c + * \brief string tokeniser + * \see mlt_tokeniser_s + * + * Copyright (C) 2002-2009 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 + */ + +/* System header files */ +#include +#include + +/* 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 ); +} diff --git a/src/framework/mlt_tokeniser.h b/src/framework/mlt_tokeniser.h new file mode 100644 index 00000000..d4715af5 --- /dev/null +++ b/src/framework/mlt_tokeniser.h @@ -0,0 +1,50 @@ +/** + * \file mlt_tokeniser.h + * \brief string tokeniser + * \see mlt_tokeniser_s + * + * Copyright (C) 2002-2009 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 + */ + +#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 diff --git a/src/framework/mlt_tractor.c b/src/framework/mlt_tractor.c new file mode 100644 index 00000000..40c78215 --- /dev/null +++ b/src/framework/mlt_tractor.c @@ -0,0 +1,540 @@ +/** + * \file mlt_tractor.c + * \brief tractor service class + * \see mlt_tractor_s + * + * Copyright (C) 2003-2009 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 "mlt_tractor.h" +#include "mlt_frame.h" +#include "mlt_multitrack.h" +#include "mlt_field.h" +#include "mlt_log.h" + +#include +#include +#include +#include + +/* 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 "", 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", "" ); + 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 "", 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", "" ); + 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 ); + } +} + diff --git a/src/framework/mlt_tractor.h b/src/framework/mlt_tractor.h new file mode 100644 index 00000000..73e07153 --- /dev/null +++ b/src/framework/mlt_tractor.h @@ -0,0 +1,66 @@ +/** + * \file mlt_tractor.h + * \brief tractor service class + * \see mlt_tractor_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_transition.c b/src/framework/mlt_transition.c new file mode 100644 index 00000000..5077795b --- /dev/null +++ b/src/framework/mlt_transition.c @@ -0,0 +1,388 @@ +/** + * \file mlt_transition.c + * \brief abstraction for all transition services + * \see mlt_transition_s + * + * Copyright (C) 2003-2009 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 "mlt_transition.h" +#include "mlt_frame.h" +#include "mlt_log.h" + +#include +#include +#include + +/* 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: +
+	+---------+                               +-------+
+	|c1       |                               |c5     | <-- A(0,1) <-- B(0,2) <-- get frame
+	+---------+                     +---------+-+-----+        |          |
+	                                |c4         |       <------+          |
+	         +----------+-----------+-+---------+                         |
+	         |c2        |c3           |                 <-----------------+
+	         +----------+-------------+
+
+ 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 ); + } + } +} diff --git a/src/framework/mlt_transition.h b/src/framework/mlt_transition.h new file mode 100644 index 00000000..0f9879a3 --- /dev/null +++ b/src/framework/mlt_transition.h @@ -0,0 +1,79 @@ +/** + * \file mlt_transition.h + * \brief abstraction for all transition services + * \see mlt_transition_s + * + * Copyright (C) 2003-2009 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 + */ + +#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 diff --git a/src/framework/mlt_types.h b/src/framework/mlt_types.h new file mode 100644 index 00000000..921dd479 --- /dev/null +++ b/src/framework/mlt_types.h @@ -0,0 +1,124 @@ +/** + * \file mlt_types.h + * \brief Provides forward definitions of all public types + * + * Copyright (C) 2003-2009 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 + */ + +#ifndef _MLT_TYPES_H_ +#define _MLT_TYPES_H_ + +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +#include + +#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 diff --git a/src/humperdink/Makefile b/src/humperdink/Makefile new file mode 100644 index 00000000..dd03dc9a --- /dev/null +++ b/src/humperdink/Makefile @@ -0,0 +1,39 @@ +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 diff --git a/src/humperdink/client.c b/src/humperdink/client.c new file mode 100644 index 00000000..afd2bbda --- /dev/null +++ b/src/humperdink/client.c @@ -0,0 +1,1025 @@ +/* + * client.c -- Valerie client demo + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include + +/* 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 ); +} diff --git a/src/humperdink/client.h b/src/humperdink/client.h new file mode 100644 index 00000000..59d97f94 --- /dev/null +++ b/src/humperdink/client.h @@ -0,0 +1,66 @@ +/* + * client.h -- Valerie client demo + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include + +/** 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 diff --git a/src/humperdink/io.c b/src/humperdink/io.c new file mode 100644 index 00000000..42718df0 --- /dev/null +++ b/src/humperdink/io.c @@ -0,0 +1,205 @@ +/* + * io.c -- Valerie client demo input/output + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include +#include +#include +#include +#include + +/* 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 ); +} diff --git a/src/humperdink/io.h b/src/humperdink/io.h new file mode 100644 index 00000000..f4ac23ad --- /dev/null +++ b/src/humperdink/io.h @@ -0,0 +1,36 @@ +/* + * io.h -- Valerie client demo input/output + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 diff --git a/src/humperdink/remote.c b/src/humperdink/remote.c new file mode 100644 index 00000000..8171179a --- /dev/null +++ b/src/humperdink/remote.c @@ -0,0 +1,73 @@ +/* + * remote.c -- Remote Valerie client demo + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include + +#include + +/* 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; +} diff --git a/src/inigo/Makefile b/src/inigo/Makefile new file mode 100644 index 00000000..c206a812 --- /dev/null +++ b/src/inigo/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/inigo/configure b/src/inigo/configure new file mode 100755 index 00000000..e69de29b diff --git a/src/inigo/inigo.c b/src/inigo/inigo.c new file mode 100644 index 00000000..698908e7 --- /dev/null +++ b/src/inigo/inigo.c @@ -0,0 +1,574 @@ +/* + * inigo.c -- MLT command line utility + * Copyright (C) 2002-2008 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include +#include + +#include + +#ifdef __DARWIN__ +#include +#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 =\n" + "# where 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" + "\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: \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; +} diff --git a/src/inigo/io.c b/src/inigo/io.c new file mode 100644 index 00000000..77d13b96 --- /dev/null +++ b/src/inigo/io.c @@ -0,0 +1,196 @@ +/* + * io.c -- inigo input/output + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include + +/* 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 ); +} diff --git a/src/inigo/io.h b/src/inigo/io.h new file mode 100644 index 00000000..70387ba8 --- /dev/null +++ b/src/inigo/io.h @@ -0,0 +1,45 @@ +/* + * io.h -- inigo input/output + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 diff --git a/src/miracle/Makefile b/src/miracle/Makefile new file mode 100644 index 00000000..2d92bfb9 --- /dev/null +++ b/src/miracle/Makefile @@ -0,0 +1,77 @@ +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 diff --git a/src/miracle/configure b/src/miracle/configure new file mode 100755 index 00000000..85d890da --- /dev/null +++ b/src/miracle/configure @@ -0,0 +1,2 @@ +#!/bin/sh +echo "miracle -I$prefix/include/mlt -D_REENTRANT -L$libdir -lmiracle" >> ../../packages.dat diff --git a/src/miracle/miracle.c b/src/miracle/miracle.c new file mode 100644 index 00000000..24aac66c --- /dev/null +++ b/src/miracle/miracle.c @@ -0,0 +1,122 @@ +/* + * miracle.c -- MLT Video TCP Server + * + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Authors: + * Dan Dennedy + * Charles Yates + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +/* 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; +} diff --git a/src/miracle/miracle_commands.c b/src/miracle/miracle_commands.c new file mode 100644 index 00000000..c95a4d04 --- /dev/null +++ b/src/miracle/miracle_commands.c @@ -0,0 +1,248 @@ +/* + * global_commands.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + + diff --git a/src/miracle/miracle_commands.h b/src/miracle/miracle_commands.h new file mode 100644 index 00000000..9d79683c --- /dev/null +++ b/src/miracle/miracle_commands.h @@ -0,0 +1,52 @@ +/* + * global_commands.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#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 diff --git a/src/miracle/miracle_connection.c b/src/miracle/miracle_connection.c new file mode 100644 index 00000000..7f287e34 --- /dev/null +++ b/src/miracle/miracle_connection.c @@ -0,0 +1,294 @@ +/* + * miracle_connection.c -- DV Connection Handler + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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; +} diff --git a/src/miracle/miracle_connection.h b/src/miracle/miracle_connection.h new file mode 100644 index 00000000..8734c6ca --- /dev/null +++ b/src/miracle/miracle_connection.h @@ -0,0 +1,92 @@ +/* + * miracle_connection.h -- DV Connection Handler + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include + +#include +#include + +#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 diff --git a/src/miracle/miracle_local.c b/src/miracle/miracle_local.c new file mode 100644 index 00000000..c1029a19 --- /dev/null +++ b/src/miracle/miracle_local.c @@ -0,0 +1,597 @@ +/* + * miracle_local.c -- Local Miracle Parser + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#endif + +/* System header files */ +#include +#include +#include + +/* Needed for backtrace on linux */ +#ifdef linux +#include +#endif + +/* Valerie header files */ +#include + +/* MLT header files. */ +#include + +/* 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 \n" + " Charles Yates \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 +} diff --git a/src/miracle/miracle_local.h b/src/miracle/miracle_local.h new file mode 100644 index 00000000..ffccb17c --- /dev/null +++ b/src/miracle/miracle_local.h @@ -0,0 +1,41 @@ +/* + * miracle_local.h -- Local Miracle Parser + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Local parser API. +*/ + +extern valerie_parser miracle_parser_init_local( ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/miracle/miracle_log.c b/src/miracle/miracle_log.c new file mode 100644 index 00000000..62b7f73e --- /dev/null +++ b/src/miracle/miracle_log.c @@ -0,0 +1,57 @@ +/* + * miracle_log.c -- logging facility implementation + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include + +#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 ); +} diff --git a/src/miracle/miracle_log.h b/src/miracle/miracle_log.h new file mode 100644 index 00000000..be21893f --- /dev/null +++ b/src/miracle/miracle_log.h @@ -0,0 +1,43 @@ +/* + * miracle_log.h -- logging facility header + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 + +#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 diff --git a/src/miracle/miracle_server.c b/src/miracle/miracle_server.c new file mode 100644 index 00000000..629da558 --- /dev/null +++ b/src/miracle/miracle_server.c @@ -0,0 +1,323 @@ +/* + * miracle_server.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Application header files */ +#include "miracle_server.h" +#include "miracle_connection.h" +#include "miracle_local.h" +#include "miracle_log.h" +#include "miracle_commands.h" +#include +#include + +#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 ); + } +} diff --git a/src/miracle/miracle_server.h b/src/miracle/miracle_server.h new file mode 100644 index 00000000..592ff3a1 --- /dev/null +++ b/src/miracle/miracle_server.h @@ -0,0 +1,76 @@ +/* + * miracle_server.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 + +/* Application header files */ +#include + +#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 diff --git a/src/miracle/miracle_unit.c b/src/miracle/miracle_unit.c new file mode 100644 index 00000000..4974e387 --- /dev/null +++ b/src/miracle/miracle_unit.c @@ -0,0 +1,777 @@ +/* + * miracle_unit.c -- Transmission Unit Implementation + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "miracle_unit.h" +#include "miracle_log.h" +#include "miracle_local.h" + +#include + +/* 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." ); + } +} + diff --git a/src/miracle/miracle_unit.h b/src/miracle/miracle_unit.h new file mode 100644 index 00000000..a78500ff --- /dev/null +++ b/src/miracle/miracle_unit.h @@ -0,0 +1,82 @@ +/* + * dvunit.h -- Transmission Unit Header + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 + +#include +#include + +#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 diff --git a/src/miracle/miracle_unit_commands.c b/src/miracle/miracle_unit_commands.c new file mode 100644 index 00000000..97fb2dd0 --- /dev/null +++ b/src/miracle/miracle_unit_commands.c @@ -0,0 +1,491 @@ +/* + * unit_commands.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/miracle/miracle_unit_commands.h b/src/miracle/miracle_unit_commands.h new file mode 100644 index 00000000..cfd51a71 --- /dev/null +++ b/src/miracle/miracle_unit_commands.h @@ -0,0 +1,61 @@ +/* + * unit_commands.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 diff --git a/src/modules/Makefile b/src/modules/Makefile new file mode 100644 index 00000000..fcbf3ea6 --- /dev/null +++ b/src/modules/Makefile @@ -0,0 +1,31 @@ +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" + diff --git a/src/modules/avformat/Makefile b/src/modules/avformat/Makefile new file mode 100644 index 00000000..7d889435 --- /dev/null +++ b/src/modules/avformat/Makefile @@ -0,0 +1,86 @@ +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 diff --git a/src/modules/avformat/audioconvert.h b/src/modules/avformat/audioconvert.h new file mode 100644 index 00000000..4b767101 --- /dev/null +++ b/src/modules/avformat/audioconvert.h @@ -0,0 +1,111 @@ +/* + * audio conversion + * Copyright (c) 2006 Michael Niedermayer + * 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 */ diff --git a/src/modules/avformat/configure b/src/modules/avformat/configure new file mode 100755 index 00000000..dc371a39 --- /dev/null +++ b/src/modules/avformat/configure @@ -0,0 +1,168 @@ +#!/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 diff --git a/src/modules/avformat/consumer_avformat.c b/src/modules/avformat/consumer_avformat.c new file mode 100644 index 00000000..bc83b0b9 --- /dev/null +++ b/src/modules/avformat/consumer_avformat.c @@ -0,0 +1,1371 @@ +/* + * consumer_avformat.c -- an encoder based on avformat + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * 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 +#include +#include + +// System header files +#include +#include +#include +#include +#include +#include +#include +#include + +// avformat header files +#include +#ifdef SWSCALE +#include +#endif +#include + +#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 ); +} diff --git a/src/modules/avformat/factory.c b/src/modules/avformat/factory.c new file mode 100644 index 00000000..54adf936 --- /dev/null +++ b/src/modules/avformat/factory.c @@ -0,0 +1,165 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include +#include + +#include + +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 + +// 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 +} diff --git a/src/modules/avformat/filter_avcolour_space.c b/src/modules/avformat/filter_avcolour_space.c new file mode 100644 index 00000000..7ca8e493 --- /dev/null +++ b/src/modules/avformat/filter_avcolour_space.c @@ -0,0 +1,247 @@ +/* + * filter_avcolour_space.c -- Colour space filter + * 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 +#include + +// ffmpeg Header files +#include +#ifdef SWSCALE +#include +#endif + +#if LIBAVUTIL_VERSION_INT < (50<<16) +#define PIX_FMT_RGB32 PIX_FMT_RGBA32 +#define PIX_FMT_YUYV422 PIX_FMT_YUV422 +#endif + +#include +#include + +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; +} + diff --git a/src/modules/avformat/filter_avdeinterlace.c b/src/modules/avformat/filter_avdeinterlace.c new file mode 100644 index 00000000..d4afa350 --- /dev/null +++ b/src/modules/avformat/filter_avdeinterlace.c @@ -0,0 +1,356 @@ +/* + * filter_avdeinterlace.c -- deinterlace filter + * 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 +#include + +#include +#include + +// ffmpeg Header files +#include + +#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; +} + diff --git a/src/modules/avformat/filter_avresample.c b/src/modules/avformat/filter_avresample.c new file mode 100644 index 00000000..3252246a --- /dev/null +++ b/src/modules/avformat/filter_avresample.c @@ -0,0 +1,196 @@ +/* + * filter_avresample.c -- adjust audio sample frequency + * 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 +#include + +#include +#include +#include + +// ffmpeg Header files +#include + +/** 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; +} diff --git a/src/modules/avformat/filter_swscale.c b/src/modules/avformat/filter_swscale.c new file mode 100644 index 00000000..d1266aea --- /dev/null +++ b/src/modules/avformat/filter_swscale.c @@ -0,0 +1,197 @@ +/* + * filter_swscale.c -- image scaling filter + * Copyright (C) 2008-2009 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include + + +// ffmpeg Header files +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/avformat/mmx.h b/src/modules/avformat/mmx.h new file mode 100644 index 00000000..7e94cfd9 --- /dev/null +++ b/src/modules/avformat/mmx.h @@ -0,0 +1,243 @@ +/* + * 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 */ diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c new file mode 100644 index 00000000..2474f9a0 --- /dev/null +++ b/src/modules/avformat/producer_avformat.c @@ -0,0 +1,1469 @@ +/* + * producer_avformat.c -- avformat producer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * 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 +#include +#include + +// ffmpeg Header files +#include +#include +#ifdef SWSCALE +# include +#endif +#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0)) +# include "audioconvert.h" +#endif + +// System header files +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/avformat/producer_avformat.yml b/src/modules/avformat/producer_avformat.yml new file mode 100644 index 00000000..86ce7380 --- /dev/null +++ b/src/modules/avformat/producer_avformat.yml @@ -0,0 +1,171 @@ +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 diff --git a/src/modules/configure b/src/modules/configure new file mode 100755 index 00000000..e80a585d --- /dev/null +++ b/src/modules/configure @@ -0,0 +1,42 @@ +#!/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 + diff --git a/src/modules/core/Makefile b/src/modules/core/Makefile new file mode 100644 index 00000000..a3132aed --- /dev/null +++ b/src/modules/core/Makefile @@ -0,0 +1,63 @@ +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 diff --git a/src/modules/core/composite_line_yuv_mmx.S b/src/modules/core/composite_line_yuv_mmx.S new file mode 100644 index 00000000..27af4dc2 --- /dev/null +++ b/src/modules/core/composite_line_yuv_mmx.S @@ -0,0 +1,211 @@ + .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 diff --git a/src/modules/core/consumer_null.c b/src/modules/core/consumer_null.c new file mode 100644 index 00000000..e96c8cda --- /dev/null +++ b/src/modules/core/consumer_null.c @@ -0,0 +1,181 @@ +/* + * consumer_null.c -- a null 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 + */ + +// mlt Header files +#include +#include + +// System header files +#include +#include +#include +#include + +// 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 ); +} diff --git a/src/modules/core/factory.c b/src/modules/core/factory.c new file mode 100644 index 00000000..755597f9 --- /dev/null +++ b/src/modules/core/factory.c @@ -0,0 +1,79 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/core/filter_brightness.c b/src/modules/core/filter_brightness.c new file mode 100644 index 00000000..07dce1c8 --- /dev/null +++ b/src/modules/core/filter_brightness.c @@ -0,0 +1,104 @@ +/* + * filter_brightness.c -- gamma filter + * 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 +#include + +#include +#include +#include + +#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; +} + diff --git a/src/modules/core/filter_channelcopy.c b/src/modules/core/filter_channelcopy.c new file mode 100644 index 00000000..d4dd697c --- /dev/null +++ b/src/modules/core/filter_channelcopy.c @@ -0,0 +1,96 @@ +/* + * filter_channelcopy.c -- copy one audio channel to another + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include +#define __USE_ISOC99 1 +#include + +/** 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; +} diff --git a/src/modules/core/filter_crop.c b/src/modules/core/filter_crop.c new file mode 100644 index 00000000..0857c1d2 --- /dev/null +++ b/src/modules/core/filter_crop.c @@ -0,0 +1,166 @@ +/* + * filter_crop.c -- cropping filter + * Copyright (C) 2009 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include + +#include +#include +#include +#include + +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; +} diff --git a/src/modules/core/filter_data_feed.c b/src/modules/core/filter_data_feed.c new file mode 100644 index 00000000..e3cf81d3 --- /dev/null +++ b/src/modules/core/filter_data_feed.c @@ -0,0 +1,177 @@ +/* + * filter_data_feed.c -- data feed filter + * 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 +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_data_show.c b/src/modules/core/filter_data_show.c new file mode 100644 index 00000000..d62bd589 --- /dev/null +++ b/src/modules/core/filter_data_show.c @@ -0,0 +1,343 @@ +/* + * filter_data_show.c -- data feed filter + * 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 +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_gamma.c b/src/modules/core/filter_gamma.c new file mode 100644 index 00000000..e4fe15b3 --- /dev/null +++ b/src/modules/core/filter_gamma.c @@ -0,0 +1,88 @@ +/* + * filter_gamma.c -- gamma filter + * 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 +#include + +#include +#include +#include + +/** 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; +} diff --git a/src/modules/core/filter_greyscale.c b/src/modules/core/filter_greyscale.c new file mode 100644 index 00000000..fd618c09 --- /dev/null +++ b/src/modules/core/filter_greyscale.c @@ -0,0 +1,62 @@ +/* + * filter_greyscale.c -- greyscale filter + * 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 +#include + +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_luma.c b/src/modules/core/filter_luma.c new file mode 100644 index 00000000..f05d6c40 --- /dev/null +++ b/src/modules/core/filter_luma.c @@ -0,0 +1,149 @@ +/* + * filter_luma.c -- luma filter + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/** 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; +} diff --git a/src/modules/core/filter_mirror.c b/src/modules/core/filter_mirror.c new file mode 100644 index 00000000..3dd6dbfc --- /dev/null +++ b/src/modules/core/filter_mirror.c @@ -0,0 +1,335 @@ +/* + * filter_mirror.c -- mirror filter + * 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 +#include + +#include +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_mono.c b/src/modules/core/filter_mono.c new file mode 100644 index 00000000..882d6f34 --- /dev/null +++ b/src/modules/core/filter_mono.c @@ -0,0 +1,94 @@ +/* + * filter_mono.c -- mix all channels to a mono signal across n channels + * Copyright (C) 2003-2006 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include + +/** 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; +} diff --git a/src/modules/core/filter_obscure.c b/src/modules/core/filter_obscure.c new file mode 100644 index 00000000..7256f6cc --- /dev/null +++ b/src/modules/core/filter_obscure.c @@ -0,0 +1,309 @@ +/* + * filter_obscure.c -- obscure filter + * 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 +#include + +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_region.c b/src/modules/core/filter_region.c new file mode 100644 index 00000000..8fd8e8d6 --- /dev/null +++ b/src/modules/core/filter_region.c @@ -0,0 +1,89 @@ +/* + * filter_region.c -- region filter + * 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 "transition_region.h" + +#include +#include + +#include +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_rescale.c b/src/modules/core/filter_rescale.c new file mode 100644 index 00000000..25ca5fcf --- /dev/null +++ b/src/modules/core/filter_rescale.c @@ -0,0 +1,325 @@ +/* + * filter_rescale.c -- scale the producer video frame size to match the consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include +#include +#include + +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; +} + diff --git a/src/modules/core/filter_resize.c b/src/modules/core/filter_resize.c new file mode 100644 index 00000000..68244bfc --- /dev/null +++ b/src/modules/core/filter_resize.c @@ -0,0 +1,194 @@ +/* + * filter_resize.c -- resizing filter + * 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 +#include + +#include +#include +#include +#include + +/** 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; +} diff --git a/src/modules/core/filter_transition.c b/src/modules/core/filter_transition.c new file mode 100644 index 00000000..cd3f4623 --- /dev/null +++ b/src/modules/core/filter_transition.c @@ -0,0 +1,115 @@ +/* + * filter_transition.c -- Convert any transition into a filter + * Copyright (C) 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 +#include +#include +#include + +/** 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; +} + diff --git a/src/modules/core/filter_watermark.c b/src/modules/core/filter_watermark.c new file mode 100644 index 00000000..2e88e082 --- /dev/null +++ b/src/modules/core/filter_watermark.c @@ -0,0 +1,264 @@ +/* + * filter_watermark.c -- watermark filter + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/** 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; +} + diff --git a/src/modules/core/producer_colour.c b/src/modules/core/producer_colour.c new file mode 100644 index 00000000..ac420017 --- /dev/null +++ b/src/modules/core/producer_colour.c @@ -0,0 +1,244 @@ +/* + * producer_colour.c -- raster image loader based upon gdk-pixbuf + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include + +#include +#include +#include + + +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 ); +} diff --git a/src/modules/core/producer_consumer.c b/src/modules/core/producer_consumer.c new file mode 100644 index 00000000..78c5bcc2 --- /dev/null +++ b/src/modules/core/producer_consumer.c @@ -0,0 +1,227 @@ +/* + * producer_consumer.c -- produce as a consumer of an encapsulated producer + * Copyright (C) 2008 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 + +#include +#include +#include +#include + +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; +} diff --git a/src/modules/core/producer_noise.c b/src/modules/core/producer_noise.c new file mode 100644 index 00000000..825f5912 --- /dev/null +++ b/src/modules/core/producer_noise.c @@ -0,0 +1,177 @@ +/* + * producer_noise.c -- noise generating producer + * 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 +#include +#include + +#include +#include +#include + +/** 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 ); +} + diff --git a/src/modules/core/producer_ppm.c b/src/modules/core/producer_ppm.c new file mode 100644 index 00000000..01215790 --- /dev/null +++ b/src/modules/core/producer_ppm.c @@ -0,0 +1,273 @@ +/* + * producer_ppm.c -- simple ppm test case + * 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 +#include + +#include +#include + +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 ); +} diff --git a/src/modules/core/transition_composite.c b/src/modules/core/transition_composite.c new file mode 100644 index 00000000..04a518f5 --- /dev/null +++ b/src/modules/core/transition_composite.c @@ -0,0 +1,1342 @@ +/* + * transition_composite.c -- compose one image over another using alpha channel + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 + +#include +#include +#include +#include +#include + +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; +} diff --git a/src/modules/core/transition_composite.h b/src/modules/core/transition_composite.h new file mode 100644 index 00000000..6523b95b --- /dev/null +++ b/src/modules/core/transition_composite.h @@ -0,0 +1,31 @@ +/* + * transition_composite.h -- compose one image over another using alpha channel + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 + +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 diff --git a/src/modules/core/transition_luma.c b/src/modules/core/transition_luma.c new file mode 100644 index 00000000..f08d84f7 --- /dev/null +++ b/src/modules/core/transition_luma.c @@ -0,0 +1,596 @@ +/* + * transition_luma.c -- a generic dissolve/wipe processor + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * Adapted from Kino Plugin Timfx, which is + * Copyright (C) 2002 Timothy M. Shead + * + * 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 + +#include +#include +#include +#include +#include + +/** 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; +} diff --git a/src/modules/core/transition_mix.c b/src/modules/core/transition_mix.c new file mode 100644 index 00000000..b2b3790a --- /dev/null +++ b/src/modules/core/transition_mix.c @@ -0,0 +1,169 @@ +/* + * transition_mix.c -- mix two audio streams + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include + + +/** 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; +} + diff --git a/src/modules/core/transition_region.c b/src/modules/core/transition_region.c new file mode 100644 index 00000000..52bcb0f8 --- /dev/null +++ b/src/modules/core/transition_region.c @@ -0,0 +1,449 @@ +/* + * transition_region.c -- region transition + * 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 "transition_region.h" +#include "transition_composite.h" + +#include + +#include +#include +#include + +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:"; + + // 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; +} + diff --git a/src/modules/core/transition_region.h b/src/modules/core/transition_region.h new file mode 100644 index 00000000..f08ad0e1 --- /dev/null +++ b/src/modules/core/transition_region.h @@ -0,0 +1,28 @@ +/* + * transition_region.h -- region transition + * 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 + */ + +#ifndef _TRANSITION_REGION_H_ +#define _TRANSITION_REGION_H_ + +#include + +extern mlt_transition transition_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); + +#endif diff --git a/src/modules/data_fx.properties b/src/modules/data_fx.properties new file mode 100644 index 00000000..f7dc2e98 --- /dev/null +++ b/src/modules/data_fx.properties @@ -0,0 +1,250 @@ +# This properties file describes the fx available to the data_send and +# data_show filters +# +# Syntax is as follows: +# +# name= +# name.description= +# name.properties.= +# name.=value +# etc +# +# Typically, the 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% + diff --git a/src/modules/dgraft/Makefile b/src/modules/dgraft/Makefile new file mode 100644 index 00000000..7e17749a --- /dev/null +++ b/src/modules/dgraft/Makefile @@ -0,0 +1,33 @@ +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 diff --git a/src/modules/dgraft/factory.c b/src/modules/dgraft/factory.c new file mode 100644 index 00000000..e0f2825f --- /dev/null +++ b/src/modules/dgraft/factory.c @@ -0,0 +1,28 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2008 Dan Dennedy + * + * 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 +#include + +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 ); +} diff --git a/src/modules/dgraft/filter_telecide.c b/src/modules/dgraft/filter_telecide.c new file mode 100644 index 00000000..9bb11497 --- /dev/null +++ b/src/modules/dgraft/filter_telecide.c @@ -0,0 +1,1230 @@ +/* + * filter_telecide.c -- Donald Graft's Inverse Telecine Filter + * Copyright (C) 2003 Donald A. Graft + * Copyright (C) 2008 Dan Dennedy + * Author: Dan Dennedy + * + * 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 + +#include +#include +#include +#include + +//#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;ychosen == 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; +} + diff --git a/src/modules/dgraft/gpl b/src/modules/dgraft/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/dv/Makefile b/src/modules/dv/Makefile new file mode 100644 index 00000000..8c5ef1bb --- /dev/null +++ b/src/modules/dv/Makefile @@ -0,0 +1,36 @@ +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 diff --git a/src/modules/dv/configure b/src/modules/dv/configure new file mode 100755 index 00000000..6712ea61 --- /dev/null +++ b/src/modules/dv/configure @@ -0,0 +1,17 @@ +#!/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 + diff --git a/src/modules/dv/consumer_libdv.c b/src/modules/dv/consumer_libdv.c new file mode 100644 index 00000000..2ee9b206 --- /dev/null +++ b/src/modules/dv/consumer_libdv.c @@ -0,0 +1,449 @@ +/* + * consumer_libdv.c -- a DV encoder based on libdv + * 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 + */ + +// mlt Header files +#include +#include + +// System header files +#include +#include +#include +#include + +// libdv header files +#include + +#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 ); +} diff --git a/src/modules/dv/factory.c b/src/modules/dv/factory.c new file mode 100644 index 00000000..305fb9cd --- /dev/null +++ b/src/modules/dv/factory.c @@ -0,0 +1,32 @@ +/* + * factory.c -- the factory method interfaces + * 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 + +#include + +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 ); +} diff --git a/src/modules/dv/producer_libdv.c b/src/modules/dv/producer_libdv.c new file mode 100644 index 00000000..6fd012c6 --- /dev/null +++ b/src/modules/dv/producer_libdv.c @@ -0,0 +1,546 @@ +/* + * producer_libdv.c -- simple libdv test case + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/src/modules/effectv/Makefile b/src/modules/effectv/Makefile new file mode 100644 index 00000000..a8f5f623 --- /dev/null +++ b/src/modules/effectv/Makefile @@ -0,0 +1,35 @@ +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 diff --git a/src/modules/effectv/factory.c b/src/modules/effectv/factory.c new file mode 100644 index 00000000..e28d765a --- /dev/null +++ b/src/modules/effectv/factory.c @@ -0,0 +1,29 @@ +/* + * 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 +#include + +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 ); +} diff --git a/src/modules/effectv/filter_burn.c b/src/modules/effectv/filter_burn.c new file mode 100644 index 00000000..967e2630 --- /dev/null +++ b/src/modules/effectv/filter_burn.c @@ -0,0 +1,213 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#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> 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; +} + diff --git a/src/modules/effectv/gpl b/src/modules/effectv/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/effectv/image.c b/src/modules/effectv/image.c new file mode 100644 index 00000000..ce8157f2 --- /dev/null +++ b/src/modules/effectv/image.c @@ -0,0 +1,308 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * image.c: utilities for image processing. + * + */ + +#include +#include +#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>(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>(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>(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<>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>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>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 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 +#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; +} diff --git a/src/modules/effectv/utils.h b/src/modules/effectv/utils.h new file mode 100644 index 00000000..44618984 --- /dev/null +++ b/src/modules/effectv/utils.h @@ -0,0 +1,48 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * utils.h: header file for utils + * + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include + +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__ */ diff --git a/src/modules/feeds/Makefile b/src/modules/feeds/Makefile new file mode 100644 index 00000000..748ce477 --- /dev/null +++ b/src/modules/feeds/Makefile @@ -0,0 +1,15 @@ +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 diff --git a/src/modules/feeds/NTSC/data_fx.properties b/src/modules/feeds/NTSC/data_fx.properties new file mode 100644 index 00000000..03832aaa --- /dev/null +++ b/src/modules/feeds/NTSC/data_fx.properties @@ -0,0 +1,250 @@ +# This properties file describes the fx available to the data_feed and +# data_show filters +# +# Syntax is as follows: +# +# name= +# name.description= +# name.properties.= +# name.=value +# etc +# +# Typically, the 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% + diff --git a/src/modules/feeds/NTSC/obscure.properties b/src/modules/feeds/NTSC/obscure.properties new file mode 100644 index 00000000..7eba160d --- /dev/null +++ b/src/modules/feeds/NTSC/obscure.properties @@ -0,0 +1,26 @@ +# This properties file describes the fx available to the data_feed and +# data_show filters +# +# Syntax is as follows: +# +# name= +# name.description= +# name.properties.= +# name.=value +# etc +# +# Typically, the 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% + diff --git a/src/modules/feeds/PAL/border.properties b/src/modules/feeds/PAL/border.properties new file mode 100644 index 00000000..e02ab357 --- /dev/null +++ b/src/modules/feeds/PAL/border.properties @@ -0,0 +1,22 @@ +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 + diff --git a/src/modules/feeds/PAL/data_fx.properties b/src/modules/feeds/PAL/data_fx.properties new file mode 100644 index 00000000..e2e483b4 --- /dev/null +++ b/src/modules/feeds/PAL/data_fx.properties @@ -0,0 +1,76 @@ +# This properties file describes the fx available to the data_send and +# data_show filters +# +# Syntax is as follows: +# +# name= +# name.description= +# name.properties.= +# name.=value +# etc +# +# Typically, the 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 + diff --git a/src/modules/feeds/PAL/etv.properties b/src/modules/feeds/PAL/etv.properties new file mode 100644 index 00000000..07be453b --- /dev/null +++ b/src/modules/feeds/PAL/etv.properties @@ -0,0 +1,186 @@ +# This properties file describes the fx available to the data_feed and +# data_show filters +# +# Syntax is as follows: +# +# name= +# name.description= +# name.properties.= +# name.=value +# etc +# +# Typically, the 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 + diff --git a/src/modules/feeds/PAL/example.properties b/src/modules/feeds/PAL/example.properties new file mode 100644 index 00000000..0509fff8 --- /dev/null +++ b/src/modules/feeds/PAL/example.properties @@ -0,0 +1,12 @@ +greyscale=greyscale +.description=Greyscale + +sepia=sepia +.description=Sepia + +charcoal=charcoal +.description=Charcoal + +invert=invert +.description=Invert + diff --git a/src/modules/feeds/PAL/obscure.properties b/src/modules/feeds/PAL/obscure.properties new file mode 100644 index 00000000..3917d9a5 --- /dev/null +++ b/src/modules/feeds/PAL/obscure.properties @@ -0,0 +1,35 @@ +# This properties file describes the fx available to the data_feed and +# data_show filters +# +# Syntax is as follows: +# +# name= +# name.description= +# name.properties.= +# name.=value +# etc +# +# Typically, the 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 + diff --git a/src/modules/fezzik.dict b/src/modules/fezzik.dict new file mode 100644 index 00000000..bdcc0e1b --- /dev/null +++ b/src/modules/fezzik.dict @@ -0,0 +1,39 @@ +http://*=avformat +.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 diff --git a/src/modules/fezzik/factory.c b/src/modules/fezzik/factory.c new file mode 100644 index 00000000..87f22b98 --- /dev/null +++ b/src/modules/fezzik/factory.c @@ -0,0 +1,31 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/fezzik/producer_fezzik.c b/src/modules/fezzik/producer_fezzik.c new file mode 100644 index 00000000..816c1bd7 --- /dev/null +++ b/src/modules/fezzik/producer_fezzik.c @@ -0,0 +1,187 @@ +/* + * producer_fezzik.c -- a normalising filter + * 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 +#include +#include +#include +#include +#include + +#include + +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; +} diff --git a/src/modules/fezzik/producer_hold.c b/src/modules/fezzik/producer_hold.c new file mode 100644 index 00000000..f30aa0c3 --- /dev/null +++ b/src/modules/fezzik/producer_hold.c @@ -0,0 +1,201 @@ +/* + * producer_hold.c -- frame holding producer + * 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 +#include +#include + +#include + +// 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 ); +} + diff --git a/src/modules/frei0r/Makefile b/src/modules/frei0r/Makefile new file mode 100644 index 00000000..2c3762d2 --- /dev/null +++ b/src/modules/frei0r/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/modules/frei0r/configure b/src/modules/frei0r/configure new file mode 100755 index 00000000..73757a46 --- /dev/null +++ b/src/modules/frei0r/configure @@ -0,0 +1,14 @@ +#! /bin/sh + +if [ "$help" != "1" ] +then + + echo "#include 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 diff --git a/src/modules/frei0r/factory.c b/src/modules/frei0r/factory.c new file mode 100644 index 00000000..8e2ab997 --- /dev/null +++ b/src/modules/frei0r/factory.c @@ -0,0 +1,335 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (c) 2008 Marco Gittler + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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;jget_frame = producer_get_frame; + this->close = ( mlt_destructor )producer_close; + f0r_init(); + properties=MLT_PRODUCER_PROPERTIES ( this ); + + for (i=0;iprocess = filter_process; + this->close = filter_close; + f0r_init(); + properties=MLT_FILTER_PROPERTIES ( this ); + + for (i=0;iprocess = 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 + * + * 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 + +#include "frei0r_helper.h" +#include +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 ) ); + +} diff --git a/src/modules/frei0r/frei0r_helper.c b/src/modules/frei0r/frei0r_helper.c new file mode 100644 index 00000000..e42b06e3 --- /dev/null +++ b/src/modules/frei0r/frei0r_helper.c @@ -0,0 +1,161 @@ +/* + * frei0r_helper.c -- frei0r helper + * Copyright (c) 2008 Marco Gittler + * + * 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 +#include +#include + +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 + * + * 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 + +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 ); diff --git a/src/modules/frei0r/producer_frei0r.c b/src/modules/frei0r/producer_frei0r.c new file mode 100644 index 00000000..ae1a26d8 --- /dev/null +++ b/src/modules/frei0r/producer_frei0r.c @@ -0,0 +1,101 @@ +/* + * producer_frei0r.c -- frei0r producer + * Copyright (c) 2009 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 + +#include "frei0r_helper.h" + +#include +#include + +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 ); +} diff --git a/src/modules/frei0r/transition_frei0r.c b/src/modules/frei0r/transition_frei0r.c new file mode 100644 index 00000000..4fb6846e --- /dev/null +++ b/src/modules/frei0r/transition_frei0r.c @@ -0,0 +1,85 @@ +/* + * transition_frei0r.c -- frei0r transition + * Copyright (c) 2008 Marco Gittler + * + * 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 +#include "frei0r_helper.h" +#include + +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 ) ); +} diff --git a/src/modules/gtk2/Makefile b/src/modules/gtk2/Makefile new file mode 100644 index 00000000..de5fa2fb --- /dev/null +++ b/src/modules/gtk2/Makefile @@ -0,0 +1,62 @@ +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 diff --git a/src/modules/gtk2/configure b/src/modules/gtk2/configure new file mode 100755 index 00000000..299da4b8 --- /dev/null +++ b/src/modules/gtk2/configure @@ -0,0 +1,38 @@ +#!/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 + diff --git a/src/modules/gtk2/consumer_gtk2.c b/src/modules/gtk2/consumer_gtk2.c new file mode 100644 index 00000000..dc1619e3 --- /dev/null +++ b/src/modules/gtk2/consumer_gtk2.c @@ -0,0 +1,55 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; +} diff --git a/src/modules/gtk2/factory.c b/src/modules/gtk2/factory.c new file mode 100644 index 00000000..34d62e26 --- /dev/null +++ b/src/modules/gtk2/factory.c @@ -0,0 +1,82 @@ +/* + * factory.c -- the factory method interfaces + * 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 "config.h" +#include +#include +#include + +#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 ); +} diff --git a/src/modules/gtk2/filter_rescale.c b/src/modules/gtk2/filter_rescale.c new file mode 100644 index 00000000..e3b6d17a --- /dev/null +++ b/src/modules/gtk2/filter_rescale.c @@ -0,0 +1,158 @@ +/* + * filter_rescale.c -- scale the producer video frame size to match the consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include + +#include +#include +#include +#include + +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; +} + diff --git a/src/modules/gtk2/have_mmx.S b/src/modules/gtk2/have_mmx.S new file mode 100644 index 00000000..4f8f5d87 --- /dev/null +++ b/src/modules/gtk2/have_mmx.S @@ -0,0 +1,53 @@ + .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 + diff --git a/src/modules/gtk2/pixops.c b/src/modules/gtk2/pixops.c new file mode 100644 index 00000000..05b82928 --- /dev/null +++ b/src/modules/gtk2/pixops.c @@ -0,0 +1,769 @@ +/* GdkPixbuf library - Scaling and compositing functions + * + * Original: + * Copyright (C) 2000 Red Hat, Inc + * Author: Owen Taylor + * + * Modification for MLT: + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include + +#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 ); +} + diff --git a/src/modules/gtk2/pixops.h b/src/modules/gtk2/pixops.h new file mode 100644 index 00000000..5b159fab --- /dev/null +++ b/src/modules/gtk2/pixops.h @@ -0,0 +1,72 @@ +/* GdkPixbuf library - Scaling and compositing functions + * + * Original: + * Copyright (C) 2000 Red Hat, Inc + * Author: Owen Taylor + * + * Modification for MLT: + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 + +/* 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 diff --git a/src/modules/gtk2/producer_pango.c b/src/modules/gtk2/producer_pango.c new file mode 100644 index 00000000..3398e79a --- /dev/null +++ b/src/modules/gtk2/producer_pango.c @@ -0,0 +1,721 @@ +/* + * producer_pango.c -- a pango-based titler + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/modules/gtk2/producer_pixbuf.c b/src/modules/gtk2/producer_pixbuf.c new file mode 100644 index 00000000..70df32c6 --- /dev/null +++ b/src/modules/gtk2/producer_pixbuf.c @@ -0,0 +1,539 @@ +/* + * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, " -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 ); +} diff --git a/src/modules/gtk2/scale_line_22_yuv_mmx.S b/src/modules/gtk2/scale_line_22_yuv_mmx.S new file mode 100644 index 00000000..d78b3413 --- /dev/null +++ b/src/modules/gtk2/scale_line_22_yuv_mmx.S @@ -0,0 +1,227 @@ +/* + * scale_line_22_yuv_mmx.S -- scale line in YUY2 format + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 diff --git a/src/modules/inigo/Makefile b/src/modules/inigo/Makefile new file mode 100644 index 00000000..b2e297f1 --- /dev/null +++ b/src/modules/inigo/Makefile @@ -0,0 +1,33 @@ +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 diff --git a/src/modules/inigo/factory.c b/src/modules/inigo/factory.c new file mode 100644 index 00000000..c1b2d0e7 --- /dev/null +++ b/src/modules/inigo/factory.c @@ -0,0 +1,31 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/inigo/producer_inigo.c b/src/modules/inigo/producer_inigo.c new file mode 100644 index 00000000..88ea2a57 --- /dev/null +++ b/src/modules/inigo/producer_inigo.c @@ -0,0 +1,454 @@ +/* + * producer_inigo.c -- simple inigo test case + * 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 +#include +#include + +#include + +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 ], " 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; +} diff --git a/src/modules/jackrack/Makefile b/src/modules/jackrack/Makefile new file mode 100644 index 00000000..50d7d880 --- /dev/null +++ b/src/modules/jackrack/Makefile @@ -0,0 +1,47 @@ +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 diff --git a/src/modules/jackrack/configure b/src/modules/jackrack/configure new file mode 100755 index 00000000..d10b2942 --- /dev/null +++ b/src/modules/jackrack/configure @@ -0,0 +1,28 @@ +#!/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 diff --git a/src/modules/jackrack/factory.c b/src/modules/jackrack/factory.c new file mode 100644 index 00000000..1476b153 --- /dev/null +++ b/src/modules/jackrack/factory.c @@ -0,0 +1,31 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +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 ); +} diff --git a/src/modules/jackrack/filter_jackrack.c b/src/modules/jackrack/filter_jackrack.c new file mode 100644 index 00000000..0c9fe9bf --- /dev/null +++ b/src/modules/jackrack/filter_jackrack.c @@ -0,0 +1,372 @@ +/* + * filter_jackrack.c -- filter audio through Jack and/or LADSPA plugins + * Copyright (C) 2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/jackrack/filter_ladspa.c b/src/modules/jackrack/filter_ladspa.c new file mode 100644 index 00000000..7eff6fab --- /dev/null +++ b/src/modules/jackrack/filter_ladspa.c @@ -0,0 +1,190 @@ +/* + * filter_ladspa.c -- filter audio through LADSPA plugins + * Copyright (C) 2004-2005 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include + +#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; +} diff --git a/src/modules/jackrack/gpl b/src/modules/jackrack/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/jackrack/jack_rack.c b/src/modules/jackrack/jack_rack.c new file mode 100644 index 00000000..92f82721 --- /dev/null +++ b/src/modules/jackrack/jack_rack.c @@ -0,0 +1,359 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include + +#include +#include + +#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 */ diff --git a/src/modules/jackrack/jack_rack.h b/src/modules/jackrack/jack_rack.h new file mode 100644 index 00000000..7936a8b2 --- /dev/null +++ b/src/modules/jackrack/jack_rack.h @@ -0,0 +1,72 @@ +/* + * 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 + * + * 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 +#include + +#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__ */ diff --git a/src/modules/jackrack/lock_free_fifo.c b/src/modules/jackrack/lock_free_fifo.c new file mode 100644 index 00000000..177f6b42 --- /dev/null +++ b/src/modules/jackrack/lock_free_fifo.c @@ -0,0 +1,117 @@ +/* + * 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 + * + * 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 +#include +#include + +#include + +#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; + } +} diff --git a/src/modules/jackrack/lock_free_fifo.h b/src/modules/jackrack/lock_free_fifo.h new file mode 100644 index 00000000..29abd3cf --- /dev/null +++ b/src/modules/jackrack/lock_free_fifo.h @@ -0,0 +1,53 @@ +/* + * 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 + * + * 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__ */ diff --git a/src/modules/jackrack/plugin.c b/src/modules/jackrack/plugin.c new file mode 100644 index 00000000..6a483a88 --- /dev/null +++ b/src/modules/jackrack/plugin.c @@ -0,0 +1,596 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include + +#include + +#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 */ diff --git a/src/modules/jackrack/plugin.h b/src/modules/jackrack/plugin.h new file mode 100644 index 00000000..576b803c --- /dev/null +++ b/src/modules/jackrack/plugin.h @@ -0,0 +1,88 @@ +/* + * 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 + * + * 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 +#include +#include + +#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__ */ diff --git a/src/modules/jackrack/plugin_desc.c b/src/modules/jackrack/plugin_desc.c new file mode 100644 index 00000000..b3259895 --- /dev/null +++ b/src/modules/jackrack/plugin_desc.c @@ -0,0 +1,417 @@ +/* + * 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 + * + * 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 +#include +#include + +#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 */ diff --git a/src/modules/jackrack/plugin_desc.h b/src/modules/jackrack/plugin_desc.h new file mode 100644 index 00000000..d6b5ca32 --- /dev/null +++ b/src/modules/jackrack/plugin_desc.h @@ -0,0 +1,81 @@ +/* + * 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 + * + * 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 +#include + +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__ */ diff --git a/src/modules/jackrack/plugin_mgr.c b/src/modules/jackrack/plugin_mgr.c new file mode 100644 index 00000000..ca806092 --- /dev/null +++ b/src/modules/jackrack/plugin_mgr.c @@ -0,0 +1,321 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/src/modules/jackrack/plugin_mgr.h b/src/modules/jackrack/plugin_mgr.h new file mode 100644 index 00000000..bc70237e --- /dev/null +++ b/src/modules/jackrack/plugin_mgr.h @@ -0,0 +1,53 @@ +/* + * 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 + * + * 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 + +#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__ */ diff --git a/src/modules/jackrack/plugin_settings.c b/src/modules/jackrack/plugin_settings.c new file mode 100644 index 00000000..7284b803 --- /dev/null +++ b/src/modules/jackrack/plugin_settings.c @@ -0,0 +1,395 @@ +/* + * 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 + * + * 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 + +#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 */ diff --git a/src/modules/jackrack/plugin_settings.h b/src/modules/jackrack/plugin_settings.h new file mode 100644 index 00000000..852333b4 --- /dev/null +++ b/src/modules/jackrack/plugin_settings.h @@ -0,0 +1,76 @@ +/* + * 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 + * + * 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 +#include + +#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__ */ diff --git a/src/modules/jackrack/process.c b/src/modules/jackrack/process.c new file mode 100644 index 00000000..efd497fd --- /dev/null +++ b/src/modules/jackrack/process.c @@ -0,0 +1,600 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/jackrack/process.h b/src/modules/jackrack/process.h new file mode 100644 index 00000000..8b0c3657 --- /dev/null +++ b/src/modules/jackrack/process.h @@ -0,0 +1,76 @@ +/* + * 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 + * + * 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 +#include +#include + +#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__ */ diff --git a/src/modules/kdenlive/Makefile b/src/modules/kdenlive/Makefile new file mode 100644 index 00000000..d32ab0c6 --- /dev/null +++ b/src/modules/kdenlive/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/modules/kdenlive/factory.c b/src/modules/kdenlive/factory.c new file mode 100644 index 00000000..80914c7b --- /dev/null +++ b/src/modules/kdenlive/factory.c @@ -0,0 +1,34 @@ +/* + * 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 +#include + +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 ); +} diff --git a/src/modules/kdenlive/filter_boxblur.c b/src/modules/kdenlive/filter_boxblur.c new file mode 100644 index 00000000..f1d54259 --- /dev/null +++ b/src/modules/kdenlive/filter_boxblur.c @@ -0,0 +1,233 @@ +/* + * filter_boxblur.c -- blur filter + * Copyright (C) ?-2007 Leny Grisel + * 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 +#include + +#include +#include +#include + + +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; y0) 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; +} + + + diff --git a/src/modules/kdenlive/filter_freeze.c b/src/modules/kdenlive/filter_freeze.c new file mode 100644 index 00000000..ef85423d --- /dev/null +++ b/src/modules/kdenlive/filter_freeze.c @@ -0,0 +1,118 @@ +/* + * filter_freeze.c -- simple frame freezing filter + * 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 +#include +#include +#include +#include +#include + +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; +} + + + diff --git a/src/modules/kdenlive/filter_wave.c b/src/modules/kdenlive/filter_wave.c new file mode 100644 index 00000000..d01944f1 --- /dev/null +++ b/src/modules/kdenlive/filter_wave.c @@ -0,0 +1,139 @@ +/* + * wave.c -- wave filter + * Copyright (C) ?-2007 Leny Grisel + * 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 +#include + +#include +#include +#include +#include + +// 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;yprocess = 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; +} + + + diff --git a/src/modules/kdenlive/producer_framebuffer.c b/src/modules/kdenlive/producer_framebuffer.c new file mode 100644 index 00000000..10dc22fe --- /dev/null +++ b/src/modules/kdenlive/producer_framebuffer.c @@ -0,0 +1,284 @@ +/* + * producer_framebuffer.c -- create subspeed frames + * Copyright (C) 2007 Jean-Baptiste Mardelle + * 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 + +#include +#include +#include +#include +#include +#include + +// 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; +} diff --git a/src/modules/kino/Makefile b/src/modules/kino/Makefile new file mode 100644 index 00000000..32cdeaa6 --- /dev/null +++ b/src/modules/kino/Makefile @@ -0,0 +1,48 @@ +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 diff --git a/src/modules/kino/avi.cc b/src/modules/kino/avi.cc new file mode 100644 index 00000000..ced89934 --- /dev/null +++ b/src/modules/kino/avi.cc @@ -0,0 +1,1707 @@ +/* +* avi.cc library for AVI file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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 +#include +#include + +using std::cout; +using std::hex; +using std::dec; +using std::setw; +using std::setfill; +using std::endl; + +// C includes + +#include +#include +#include +#include +#include + +// 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; +} diff --git a/src/modules/kino/avi.h b/src/modules/kino/avi.h new file mode 100644 index 00000000..d22d5e6f --- /dev/null +++ b/src/modules/kino/avi.h @@ -0,0 +1,447 @@ +/* +* avi.h library for AVI file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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 +#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 diff --git a/src/modules/kino/configure b/src/modules/kino/configure new file mode 100755 index 00000000..f6524f38 --- /dev/null +++ b/src/modules/kino/configure @@ -0,0 +1,31 @@ +#!/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 diff --git a/src/modules/kino/endian_types.h b/src/modules/kino/endian_types.h new file mode 100644 index 00000000..d66cdb65 --- /dev/null +++ b/src/modules/kino/endian_types.h @@ -0,0 +1,279 @@ +/* + * + * 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 + * + * 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 +#else +# include +#endif /* __FreeBSD__ */ +# undef _BSD_SOURCE +#else +#ifndef __FreeBSD__ +# include +#else +# include +#endif /* __FreeBSD__ */ +#endif + +#include +#ifndef __FreeBSD__ +#include +#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 static inline T cpu_to_le(const T& x) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return x; +#else + return bswap(x); +#endif +} + +template static inline T cpu_to_be(const T& x) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return bswap(x); +#else + return x; +#endif +} + +template 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 operator++() { + write(read() + 1); + return *this; + }; + le_t operator++(int) { + write(read() + 1); + return *this; + }; + le_t operator--() { + write(read() - 1); + return *this; + }; + le_t operator--(int) { + write(read() - 1); + return *this; + }; + le_t& operator+=(const T& t) { + write(read() + t); + return *this; + }; + le_t& operator-=(const T& t) { + write(read() - t); + return *this; + }; + le_t& operator&=(const le_t& t) { + m &= t.m; + return *this; + }; + le_t& operator|=(const le_t& t) { + m |= t.m; + return *this; + }; +} __attribute__((packed)); + +/* Just copy-and-pasted from le_t. Too lazy to do it right. */ + +template 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 operator++() { + write(read() + 1); + return *this; + }; + be_t operator++(int) { + write(read() + 1); + return *this; + }; + be_t operator--() { + write(read() - 1); + return *this; + }; + be_t operator--(int) { + write(read() - 1); + return *this; + }; + be_t& operator+=(const T& t) { + write(read() + t); + return *this; + }; + be_t& operator-=(const T& t) { + write(read() - t); + return *this; + }; + be_t& operator&=(const be_t& t) { + m &= t.m; + return *this; + }; + be_t& operator|=(const be_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_be_t; +typedef be_t int32_be_t; +typedef be_t int64_be_t; +typedef u_int8_t u_int8_be_t; +typedef be_t u_int16_be_t; +typedef be_t u_int32_be_t; +typedef be_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_le_t; +typedef le_t int32_le_t; +typedef le_t int64_le_t; +typedef u_int8_t u_int8_le_t; +typedef le_t u_int16_le_t; +typedef le_t u_int32_le_t; +typedef le_t u_int64_le_t; +#endif + +#endif diff --git a/src/modules/kino/error.cc b/src/modules/kino/error.cc new file mode 100644 index 00000000..e2b8510e --- /dev/null +++ b/src/modules/kino/error.cc @@ -0,0 +1,103 @@ +/* +* error.cc Error handling +* Copyright (C) 2000 Arne Schirmacher +* +* 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 +#endif + +// C++ includes + +#include +#include +#include +#include + +using std::ostringstream; +using std::string; +using std::endl; +using std::ends; +using std::cerr; + +// C includes + +#include +#include + +// 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; + } +} diff --git a/src/modules/kino/error.h b/src/modules/kino/error.h new file mode 100644 index 00000000..9c048941 --- /dev/null +++ b/src/modules/kino/error.h @@ -0,0 +1,51 @@ +/* +* error.h Error handling +* Copyright (C) 2000 Arne Schirmacher +* +* 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 + diff --git a/src/modules/kino/factory.c b/src/modules/kino/factory.c new file mode 100644 index 00000000..13e8def8 --- /dev/null +++ b/src/modules/kino/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include + +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 ); +} diff --git a/src/modules/kino/filehandler.cc b/src/modules/kino/filehandler.cc new file mode 100644 index 00000000..e5e552cc --- /dev/null +++ b/src/modules/kino/filehandler.cc @@ -0,0 +1,941 @@ +/* +* filehandler.cc -- saving DV data into different file formats +* Copyright (C) 2000 Arne Schirmacher +* +* 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 +} + +#include +#include +#include +#include + +using std::cerr; +using std::endl; +using std::ostringstream; +using std::setw; +using std::setfill; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// libdv header files +#ifdef HAVE_LIBDV +#include +#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( 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( 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 diff --git a/src/modules/kino/filehandler.h b/src/modules/kino/filehandler.h new file mode 100644 index 00000000..d7292ab2 --- /dev/null +++ b/src/modules/kino/filehandler.h @@ -0,0 +1,217 @@ +/* +* filehandler.h +* Copyright (C) 2000 Arne Schirmacher +* +* 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 +using std::vector; + +#include +using std::string; + +#include "riff.h" +#include "avi.h" +#include +#include + +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 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 + +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 diff --git a/src/modules/kino/gpl b/src/modules/kino/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/kino/kino_wrapper.cc b/src/modules/kino/kino_wrapper.cc new file mode 100644 index 00000000..dd0e5a59 --- /dev/null +++ b/src/modules/kino/kino_wrapper.cc @@ -0,0 +1,111 @@ +/* + * kino_wrapper.cc -- c wrapper for kino file handler + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include + +#include "kino_wrapper.h" +#include "filehandler.h" + +extern "C" +{ + +#include + +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 ); +} + +} + + diff --git a/src/modules/kino/kino_wrapper.h b/src/modules/kino/kino_wrapper.h new file mode 100644 index 00000000..0e73b217 --- /dev/null +++ b/src/modules/kino/kino_wrapper.h @@ -0,0 +1,45 @@ +/* + * kino_wrapper.h -- c wrapper for kino file handler + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 + +#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 diff --git a/src/modules/kino/producer_kino.c b/src/modules/kino/producer_kino.c new file mode 100644 index 00000000..7f94a9e7 --- /dev/null +++ b/src/modules/kino/producer_kino.c @@ -0,0 +1,145 @@ +/* + * producer_kino.c -- a DV file format parser + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include +#include +#include +#include +#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 ); + } +} diff --git a/src/modules/kino/riff.cc b/src/modules/kino/riff.cc new file mode 100644 index 00000000..44a082ca --- /dev/null +++ b/src/modules/kino/riff.cc @@ -0,0 +1,713 @@ +/* +* riff.cc library for RIFF file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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 +//#include +#include +#include +#ifndef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ + +using std::cout; +using std::hex; +using std::dec; +using std::setw; +using std::setfill; +using std::endl; + +// C includes + +#include +#include +#include + +// 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; + } + } +} diff --git a/src/modules/kino/riff.h b/src/modules/kino/riff.h new file mode 100644 index 00000000..72021fb1 --- /dev/null +++ b/src/modules/kino/riff.h @@ -0,0 +1,143 @@ +/* +* riff.h library for RIFF file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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 +using std::vector; + +#include + +#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 directory; +}; +#endif diff --git a/src/modules/lumas/Makefile b/src/modules/lumas/Makefile new file mode 100644 index 00000000..14079e09 --- /dev/null +++ b/src/modules/lumas/Makefile @@ -0,0 +1,23 @@ +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 diff --git a/src/modules/lumas/configure b/src/modules/lumas/configure new file mode 100755 index 00000000..e9ce8704 --- /dev/null +++ b/src/modules/lumas/configure @@ -0,0 +1,26 @@ +#!/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 + diff --git a/src/modules/lumas/create_lumas b/src/modules/lumas/create_lumas new file mode 100755 index 00000000..05a9c063 --- /dev/null +++ b/src/modules/lumas/create_lumas @@ -0,0 +1,48 @@ +#!/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 + diff --git a/src/modules/lumas/luma.c b/src/modules/lumas/luma.c new file mode 100644 index 00000000..08896122 --- /dev/null +++ b/src/modules/lumas/luma.c @@ -0,0 +1,441 @@ +/* + * luma.c -- image generator for transition_luma + * 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 +#include +#include +#include + +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; +} + diff --git a/src/modules/motion_est/Makefile b/src/modules/motion_est/Makefile new file mode 100644 index 00000000..953f8982 --- /dev/null +++ b/src/modules/motion_est/Makefile @@ -0,0 +1,55 @@ +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 diff --git a/src/modules/motion_est/README b/src/modules/motion_est/README new file mode 100644 index 00000000..5542d3f2 --- /dev/null +++ b/src/modules/motion_est/README @@ -0,0 +1,97 @@ +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 + +To display the motion vectors as pretty arrows: + + > inigo -filter motion_est -filter vismv + +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 + +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 + +If you'd like to see the motion vectors without the median denoising function, do this: + + > inigo -filter motion_est denoise=0 -filter vismv + +To reconstruct each frame by applying the motion to the previous frame: + + > inigo -filter motion_est show_reconstruction=1 + +To compare the reconstructed frame and the real frame (while paused): + + > inigo -filter motion_est show_reconstruction=1 toggle_when_paused=1 + +To show the difference (residual) between the reconstructed frame the real frame: + + > inigo -filter motion_est show_residual=1 + +To automatically track an object in the frame, try this: + + > inigo -filter autotrack_rectangle:X,Y:WxH debug=1 + +(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 + +There is now a slow motion producer that does interpolation based on the motion vectors: + + > inigo slowmotion: _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 + diff --git a/src/modules/motion_est/arrow_code.c b/src/modules/motion_est/arrow_code.c new file mode 100644 index 00000000..08c0f699 --- /dev/null +++ b/src/modules/motion_est/arrow_code.c @@ -0,0 +1,165 @@ +/* + * /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 +#include "arrow_code.h" + +#include +#include +#include +#include + +#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); +} diff --git a/src/modules/motion_est/arrow_code.h b/src/modules/motion_est/arrow_code.h new file mode 100644 index 00000000..0fe52198 --- /dev/null +++ b/src/modules/motion_est/arrow_code.h @@ -0,0 +1,26 @@ +/** + * 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); diff --git a/src/modules/motion_est/factory.c b/src/modules/motion_est/factory.c new file mode 100644 index 00000000..1df0929a --- /dev/null +++ b/src/modules/motion_est/factory.c @@ -0,0 +1,33 @@ +/* + * 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 +#include + +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 ); +} diff --git a/src/modules/motion_est/filter_autotrack_rectangle.c b/src/modules/motion_est/filter_autotrack_rectangle.c new file mode 100644 index 00000000..0d526e45 --- /dev/null +++ b/src/modules/motion_est/filter_autotrack_rectangle.c @@ -0,0 +1,328 @@ +/* + * 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 + +#include +#include +#include +#include + +#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... +*/ diff --git a/src/modules/motion_est/filter_crop_detect.c b/src/modules/motion_est/filter_crop_detect.c new file mode 100644 index 00000000..8165141a --- /dev/null +++ b/src/modules/motion_est/filter_crop_detect.c @@ -0,0 +1,244 @@ +/** + * /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 + +#include +#include +#include +#include +#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... +*/ + diff --git a/src/modules/motion_est/filter_motion_est.c b/src/modules/motion_est/filter_motion_est.c new file mode 100644 index 00000000..6e8b0218 --- /dev/null +++ b/src/modules/motion_est/filter_motion_est.c @@ -0,0 +1,1115 @@ +/* + * /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 +#include +#include +#include +#include +#include +#include + +#ifdef USE_SSE +#include "sad_sse.h" +#endif + +#define NDEBUG +#include + +#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; // 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<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<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, //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, //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, //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 <>\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; +} diff --git a/src/modules/motion_est/filter_motion_est.h b/src/modules/motion_est/filter_motion_est.h new file mode 100644 index 00000000..296287e5 --- /dev/null +++ b/src/modules/motion_est/filter_motion_est.h @@ -0,0 +1,42 @@ +/* + * 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 + +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; // + +#include +#include +#include +#include + +#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... +*/ + + + + + + + + diff --git a/src/modules/motion_est/gpl b/src/modules/motion_est/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/motion_est/producer_slowmotion.c b/src/modules/motion_est/producer_slowmotion.c new file mode 100644 index 00000000..e66db091 --- /dev/null +++ b/src/modules/motion_est/producer_slowmotion.c @@ -0,0 +1,419 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/src/modules/motion_est/sad_sse.h b/src/modules/motion_est/sad_sse.h new file mode 100644 index 00000000..b14a5f6e --- /dev/null +++ b/src/modules/motion_est/sad_sse.h @@ -0,0 +1,429 @@ +/* + * 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; +} diff --git a/src/modules/normalize/Makefile b/src/modules/normalize/Makefile new file mode 100644 index 00000000..096e2e3d --- /dev/null +++ b/src/modules/normalize/Makefile @@ -0,0 +1,33 @@ +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 diff --git a/src/modules/normalize/factory.c b/src/modules/normalize/factory.c new file mode 100644 index 00000000..3736d7d2 --- /dev/null +++ b/src/modules/normalize/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include + +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 ); +} diff --git a/src/modules/normalize/filter_volume.c b/src/modules/normalize/filter_volume.c new file mode 100644 index 00000000..3bc747ce --- /dev/null +++ b/src/modules/normalize/filter_volume.c @@ -0,0 +1,461 @@ +/* + * filter_volume.c -- adjust audio volume + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/normalize/gpl b/src/modules/normalize/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/oldfilm/Makefile b/src/modules/oldfilm/Makefile new file mode 100644 index 00000000..90c13c33 --- /dev/null +++ b/src/modules/oldfilm/Makefile @@ -0,0 +1,43 @@ +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 diff --git a/src/modules/oldfilm/dust1.svg b/src/modules/oldfilm/dust1.svg new file mode 100644 index 00000000..db59d4d1 --- /dev/null +++ b/src/modules/oldfilm/dust1.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/src/modules/oldfilm/dust2.svg b/src/modules/oldfilm/dust2.svg new file mode 100644 index 00000000..055a1d31 --- /dev/null +++ b/src/modules/oldfilm/dust2.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/modules/oldfilm/dust3.svg b/src/modules/oldfilm/dust3.svg new file mode 100644 index 00000000..d9c4089d --- /dev/null +++ b/src/modules/oldfilm/dust3.svg @@ -0,0 +1,59 @@ + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/src/modules/oldfilm/dust4.svg b/src/modules/oldfilm/dust4.svg new file mode 100644 index 00000000..19b5a19f --- /dev/null +++ b/src/modules/oldfilm/dust4.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/src/modules/oldfilm/dust5.svg b/src/modules/oldfilm/dust5.svg new file mode 100644 index 00000000..ca9b24b6 --- /dev/null +++ b/src/modules/oldfilm/dust5.svg @@ -0,0 +1,59 @@ + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/src/modules/oldfilm/factory.c b/src/modules/oldfilm/factory.c new file mode 100644 index 00000000..bb7c074d --- /dev/null +++ b/src/modules/oldfilm/factory.c @@ -0,0 +1,57 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (c) 2007 Marco Gittler + * + * 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 +#include +#include + +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 ); + +} + + + diff --git a/src/modules/oldfilm/fdust.svg b/src/modules/oldfilm/fdust.svg new file mode 100644 index 00000000..693aa39a --- /dev/null +++ b/src/modules/oldfilm/fdust.svg @@ -0,0 +1,100 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/modules/oldfilm/filter_dust.c b/src/modules/oldfilm/filter_dust.c new file mode 100644 index 00000000..fc62024f --- /dev/null +++ b/src/modules/oldfilm/filter_dust.c @@ -0,0 +1,224 @@ +/* + * filter_dust.c -- dust filter + * Copyright (c) 2007 Marco Gittler + * + * 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 +//#include +#include + +#include +#include +#include +#include +#include + +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=0 && (y-ypos)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; +} + + diff --git a/src/modules/oldfilm/filter_dust.yml b/src/modules/oldfilm/filter_dust.yml new file mode 100644 index 00000000..7460b607 --- /dev/null +++ b/src/modules/oldfilm/filter_dust.yml @@ -0,0 +1,47 @@ +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 + diff --git a/src/modules/oldfilm/filter_grain.c b/src/modules/oldfilm/filter_grain.c new file mode 100644 index 00000000..296da5a8 --- /dev/null +++ b/src/modules/oldfilm/filter_grain.c @@ -0,0 +1,87 @@ +/* + * filter_grain.c -- grain filter + * Copyright (c) 2007 Marco Gittler + * + * 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 +#include + +#include +#include +#include +#define MIN(a,b) (ab?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;x20){ + 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; +} + diff --git a/src/modules/oldfilm/filter_grain.yml b/src/modules/oldfilm/filter_grain.yml new file mode 100644 index 00000000..b5e61531 --- /dev/null +++ b/src/modules/oldfilm/filter_grain.yml @@ -0,0 +1,58 @@ +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 diff --git a/src/modules/oldfilm/filter_lines.c b/src/modules/oldfilm/filter_lines.c new file mode 100644 index 00000000..7d3e415e --- /dev/null +++ b/src/modules/oldfilm/filter_lines.c @@ -0,0 +1,134 @@ +/* + * filter_lines.c -- lines filter + * Copyright (c) 2007 Marco Gittler + * + * 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 +#include + +#include +#include +#include + +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 (yend0){ + 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; +} + + diff --git a/src/modules/oldfilm/filter_lines.yml b/src/modules/oldfilm/filter_lines.yml new file mode 100644 index 00000000..5f13c51c --- /dev/null +++ b/src/modules/oldfilm/filter_lines.yml @@ -0,0 +1,73 @@ +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 + + diff --git a/src/modules/oldfilm/filter_oldfilm.c b/src/modules/oldfilm/filter_oldfilm.c new file mode 100644 index 00000000..11a6c3e9 --- /dev/null +++ b/src/modules/oldfilm/filter_oldfilm.c @@ -0,0 +1,140 @@ +/* + * filter_oldfilm.c -- oldfilm filter + * Copyright (c) 2007 Marco Gittler + * + * 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 +#include + +#include +#include +#include + + +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;x0 && newyw)?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; +} + diff --git a/src/modules/oldfilm/filter_oldfilm.yml b/src/modules/oldfilm/filter_oldfilm.yml new file mode 100644 index 00000000..e0bdcaa6 --- /dev/null +++ b/src/modules/oldfilm/filter_oldfilm.yml @@ -0,0 +1,84 @@ +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: % diff --git a/src/modules/oldfilm/filter_tcolor.c b/src/modules/oldfilm/filter_tcolor.c new file mode 100644 index 00000000..824f107e --- /dev/null +++ b/src/modules/oldfilm/filter_tcolor.c @@ -0,0 +1,85 @@ +/* + * filter_tcolor.c -- tcolor filter + * Copyright (c) 2007 Marco Gittler + * + * 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 +#include + +#include +#include +#include + +#define MIN(a,b) (aprocess = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cr", "190" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cb", "190" ); + } + return this; +} + + diff --git a/src/modules/oldfilm/filter_tcolor.yml b/src/modules/oldfilm/filter_tcolor.yml new file mode 100644 index 00000000..fb8327ed --- /dev/null +++ b/src/modules/oldfilm/filter_tcolor.yml @@ -0,0 +1,45 @@ +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 diff --git a/src/modules/oldfilm/filter_vignette.c b/src/modules/oldfilm/filter_vignette.c new file mode 100644 index 00000000..e9e3f5d0 --- /dev/null +++ b/src/modules/oldfilm/filter_vignette.c @@ -0,0 +1,127 @@ +/* + * filter_vignette.c -- vignette filter + * Copyright (c) 2007 Marco Gittler + * + * 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 +#include +#include + +#include +#include +#include +#define MIN(a,b) (adx){ //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;iprocess = 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; +} + + diff --git a/src/modules/oldfilm/filter_vignette.yml b/src/modules/oldfilm/filter_vignette.yml new file mode 100644 index 00000000..a63bd476 --- /dev/null +++ b/src/modules/oldfilm/filter_vignette.yml @@ -0,0 +1,35 @@ +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 diff --git a/src/modules/oldfilm/grain.svg b/src/modules/oldfilm/grain.svg new file mode 100644 index 00000000..bbf11b27 --- /dev/null +++ b/src/modules/oldfilm/grain.svg @@ -0,0 +1,102 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/modules/oldfilm/lines.svg b/src/modules/oldfilm/lines.svg new file mode 100644 index 00000000..e5175749 --- /dev/null +++ b/src/modules/oldfilm/lines.svg @@ -0,0 +1,67 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/modules/oldfilm/oldfilm.svg b/src/modules/oldfilm/oldfilm.svg new file mode 100644 index 00000000..b3a521f9 --- /dev/null +++ b/src/modules/oldfilm/oldfilm.svg @@ -0,0 +1,69 @@ + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/modules/oldfilm/tcolor.svg b/src/modules/oldfilm/tcolor.svg new file mode 100644 index 00000000..1e5c452c --- /dev/null +++ b/src/modules/oldfilm/tcolor.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/modules/oldfilm/vignette.svg b/src/modules/oldfilm/vignette.svg new file mode 100644 index 00000000..4758eb0d --- /dev/null +++ b/src/modules/oldfilm/vignette.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/modules/plus/Makefile b/src/modules/plus/Makefile new file mode 100644 index 00000000..4003b425 --- /dev/null +++ b/src/modules/plus/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/modules/plus/factory.c b/src/modules/plus/factory.c new file mode 100644 index 00000000..2666f7ff --- /dev/null +++ b/src/modules/plus/factory.c @@ -0,0 +1,37 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/plus/filter_affine.c b/src/modules/plus/filter_affine.c new file mode 100644 index 00000000..cbf6caac --- /dev/null +++ b/src/modules/plus/filter_affine.c @@ -0,0 +1,133 @@ +/* + * filter_affine.c -- affine filter + * 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 +#include + +#include +#include +#include + +/** 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; +} + + diff --git a/src/modules/plus/filter_charcoal.c b/src/modules/plus/filter_charcoal.c new file mode 100644 index 00000000..e99981a5 --- /dev/null +++ b/src/modules/plus/filter_charcoal.c @@ -0,0 +1,174 @@ +/* + * filter_charcoal.c -- charcoal filter + * 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 +#include + +#include +#include +#include + +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; +} + diff --git a/src/modules/plus/filter_invert.c b/src/modules/plus/filter_invert.c new file mode 100644 index 00000000..4385fc86 --- /dev/null +++ b/src/modules/plus/filter_invert.c @@ -0,0 +1,89 @@ +/* + * filter_invert.c -- invert filter + * 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 +#include + +#include +#include +#include +#include + +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; +} + diff --git a/src/modules/plus/filter_sepia.c b/src/modules/plus/filter_sepia.c new file mode 100644 index 00000000..aa20b23a --- /dev/null +++ b/src/modules/plus/filter_sepia.c @@ -0,0 +1,100 @@ +/* + * filter_sepia.c -- sepia filter + * 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 +#include + +#include +#include +#include + +/** 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; +} + diff --git a/src/modules/plus/transition_affine.c b/src/modules/plus/transition_affine.c new file mode 100644 index 00000000..71970561 --- /dev/null +++ b/src/modules/plus/transition_affine.c @@ -0,0 +1,610 @@ +/* + * transition_affine.c -- affine transformations + * 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 +#include + +#include +#include +#include +#include +#include + +/** 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; +} diff --git a/src/modules/qimage/Makefile b/src/modules/qimage/Makefile new file mode 100644 index 00000000..42bdc817 --- /dev/null +++ b/src/modules/qimage/Makefile @@ -0,0 +1,41 @@ +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 diff --git a/src/modules/qimage/configure b/src/modules/qimage/configure new file mode 100755 index 00000000..7111947e --- /dev/null +++ b/src/modules/qimage/configure @@ -0,0 +1,128 @@ +#!/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 diff --git a/src/modules/qimage/factory.c b/src/modules/qimage/factory.c new file mode 100644 index 00000000..61cb0ec0 --- /dev/null +++ b/src/modules/qimage/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * 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 +#include + +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 ); +} diff --git a/src/modules/qimage/gpl b/src/modules/qimage/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/qimage/producer_qimage.c b/src/modules/qimage/producer_qimage.c new file mode 100644 index 00000000..841b456d --- /dev/null +++ b/src/modules/qimage/producer_qimage.c @@ -0,0 +1,318 @@ +/* + * producer_image.c -- a QT/QImage based producer for MLT + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * 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 +#include +#include "qimage_wrapper.h" + +#include +#include +#include +#include +#include +#include +#include + +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, " -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 ); +} diff --git a/src/modules/qimage/qimage_wrapper.cpp b/src/modules/qimage/qimage_wrapper.cpp new file mode 100644 index 00000000..d8722e00 --- /dev/null +++ b/src/modules/qimage/qimage_wrapper.cpp @@ -0,0 +1,295 @@ +/* + * qimage_wrapper.cpp -- a QT/QImage based producer for MLT + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * 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 +#include + +#ifdef USE_KDE +#include +#include +#endif + +#endif + + +#ifdef USE_QT4 +#include +#include +#include +#endif + + +#include + +extern "C" { + +#include +#include + +#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( 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( 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( 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" diff --git a/src/modules/qimage/qimage_wrapper.h b/src/modules/qimage/qimage_wrapper.h new file mode 100644 index 00000000..9c9243ef --- /dev/null +++ b/src/modules/qimage/qimage_wrapper.h @@ -0,0 +1,62 @@ +/* + * qimage_wrapper.h -- a QT/QImage based producer for MLT + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * 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 + +#include "config.h" +#include + +#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 diff --git a/src/modules/resample/Makefile b/src/modules/resample/Makefile new file mode 100644 index 00000000..faf00a87 --- /dev/null +++ b/src/modules/resample/Makefile @@ -0,0 +1,35 @@ +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 diff --git a/src/modules/resample/configure b/src/modules/resample/configure new file mode 100755 index 00000000..954cb048 --- /dev/null +++ b/src/modules/resample/configure @@ -0,0 +1,16 @@ +#!/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 + diff --git a/src/modules/resample/factory.c b/src/modules/resample/factory.c new file mode 100644 index 00000000..c61ec516 --- /dev/null +++ b/src/modules/resample/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include + +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 ); +} diff --git a/src/modules/resample/filter_resample.c b/src/modules/resample/filter_resample.c new file mode 100644 index 00000000..68384652 --- /dev/null +++ b/src/modules/resample/filter_resample.c @@ -0,0 +1,200 @@ +/* + * filter_resample.c -- adjust audio sample frequency + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include + +#include +#include +#include +#define __USE_ISOC99 1 +#include + +#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; +} diff --git a/src/modules/resample/gpl b/src/modules/resample/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/sdl/Makefile b/src/modules/sdl/Makefile new file mode 100644 index 00000000..e2b5b363 --- /dev/null +++ b/src/modules/sdl/Makefile @@ -0,0 +1,52 @@ +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 diff --git a/src/modules/sdl/configure b/src/modules/sdl/configure new file mode 100755 index 00000000..43b90d9c --- /dev/null +++ b/src/modules/sdl/configure @@ -0,0 +1,23 @@ +#!/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 + diff --git a/src/modules/sdl/consumer_sdl.c b/src/modules/sdl/consumer_sdl.c new file mode 100644 index 00000000..9870e448 --- /dev/null +++ b/src/modules/sdl/consumer_sdl.c @@ -0,0 +1,856 @@ +/* + * consumer_sdl.c -- A Simple DirectMedia Layer consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** 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 ); +} diff --git a/src/modules/sdl/consumer_sdl_osx_hack.h b/src/modules/sdl/consumer_sdl_osx_hack.h new file mode 100644 index 00000000..dfd76d20 --- /dev/null +++ b/src/modules/sdl/consumer_sdl_osx_hack.h @@ -0,0 +1,37 @@ +/* + * 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 + +@interface DummyThread : NSObject +- init; +- (void)startThread:(id)arg; +@end + +@implementation DummyThread +- init +{ + [super init]; + return self; +} +- (void)startThread:(id)arg +{ + return; +} +@end diff --git a/src/modules/sdl/consumer_sdl_preview.c b/src/modules/sdl/consumer_sdl_preview.c new file mode 100644 index 00000000..12b78782 --- /dev/null +++ b/src/modules/sdl/consumer_sdl_preview.c @@ -0,0 +1,423 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 ); +} diff --git a/src/modules/sdl/consumer_sdl_still.c b/src/modules/sdl/consumer_sdl_still.c new file mode 100644 index 00000000..3efd8f4e --- /dev/null +++ b/src/modules/sdl/consumer_sdl_still.c @@ -0,0 +1,645 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** 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 ); +} diff --git a/src/modules/sdl/factory.c b/src/modules/sdl/factory.c new file mode 100644 index 00000000..68301c2f --- /dev/null +++ b/src/modules/sdl/factory.c @@ -0,0 +1,40 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 +} diff --git a/src/modules/sdl/producer_sdl_image.c b/src/modules/sdl/producer_sdl_image.c new file mode 100644 index 00000000..19f4f4b0 --- /dev/null +++ b/src/modules/sdl/producer_sdl_image.c @@ -0,0 +1,262 @@ +/* + * producer_sdl_image.c -- Image loader which wraps SDL_image + * Copyright (C) 2005 Visual Media FX + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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; +} + + diff --git a/src/modules/sox/Makefile b/src/modules/sox/Makefile new file mode 100644 index 00000000..472c5915 --- /dev/null +++ b/src/modules/sox/Makefile @@ -0,0 +1,35 @@ +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 diff --git a/src/modules/sox/configure b/src/modules/sox/configure new file mode 100755 index 00000000..9df730af --- /dev/null +++ b/src/modules/sox/configure @@ -0,0 +1,84 @@ +#!/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 diff --git a/src/modules/sox/factory.c b/src/modules/sox/factory.c new file mode 100644 index 00000000..048d3bc5 --- /dev/null +++ b/src/modules/sox/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/sox/filter_sox.c b/src/modules/sox/filter_sox.c new file mode 100644 index 00000000..66cc54d3 --- /dev/null +++ b/src/modules/sox/filter_sox.c @@ -0,0 +1,500 @@ +/* + * filter_sox.c -- apply any number of SOX effects using libst + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include + +#include +#include +#include +#include + +// TODO: does not support multiple effects with SoX v14.1.0+ + +#ifdef SOX14 +# include +# 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 +#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 diff --git a/src/modules/valerie/Makefile b/src/modules/valerie/Makefile new file mode 100644 index 00000000..9cdd020a --- /dev/null +++ b/src/modules/valerie/Makefile @@ -0,0 +1,34 @@ +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 diff --git a/src/modules/valerie/consumer_valerie.c b/src/modules/valerie/consumer_valerie.c new file mode 100644 index 00000000..4d4c9a34 --- /dev/null +++ b/src/modules/valerie/consumer_valerie.c @@ -0,0 +1,174 @@ +/* + * consumer_valerie.c -- pushes a service via valerie + * 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 +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/modules/valerie/factory.c b/src/modules/valerie/factory.c new file mode 100644 index 00000000..6dcae27e --- /dev/null +++ b/src/modules/valerie/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/vmfx/Makefile b/src/modules/vmfx/Makefile new file mode 100644 index 00000000..d18f568d --- /dev/null +++ b/src/modules/vmfx/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/modules/vmfx/factory.c b/src/modules/vmfx/factory.c new file mode 100644 index 00000000..3477bb8a --- /dev/null +++ b/src/modules/vmfx/factory.c @@ -0,0 +1,37 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2005 Visual Media Fx Inc. + * Author: Charles Yates + * + * 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 +#include + +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 ); +} diff --git a/src/modules/vmfx/filter_chroma.c b/src/modules/vmfx/filter_chroma.c new file mode 100644 index 00000000..f13838ba --- /dev/null +++ b/src/modules/vmfx/filter_chroma.c @@ -0,0 +1,99 @@ +/* + * filter_chroma.c -- Maps a chroma key to the alpha channel + * Copyright (C) 2005 Visual Media Fx Inc. + * Author: Charles Yates + * + * 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 +#include +#include +#include +#include +#include +#include + +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; +} + diff --git a/src/modules/vmfx/filter_chroma_hold.c b/src/modules/vmfx/filter_chroma_hold.c new file mode 100644 index 00000000..07ca4a80 --- /dev/null +++ b/src/modules/vmfx/filter_chroma_hold.c @@ -0,0 +1,100 @@ +/* + * filter_chroma.c -- Maps a chroma key to the alpha channel + * Copyright (C) 2005 Visual Media Fx Inc. + * Author: Charles Yates + * + * 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 +#include +#include +#include +#include +#include + +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; +} + diff --git a/src/modules/vmfx/filter_mono.c b/src/modules/vmfx/filter_mono.c new file mode 100644 index 00000000..f7ea11d0 --- /dev/null +++ b/src/modules/vmfx/filter_mono.c @@ -0,0 +1,103 @@ +/* + * filter_mono.c -- Arbitrary alpha channel shaping + * Copyright (C) 2005 Visual Media Fx Inc. + * Author: Charles Yates + * + * 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 +#include +#include +#include +#include +#include + +/** 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; +} + diff --git a/src/modules/vmfx/filter_shape.c b/src/modules/vmfx/filter_shape.c new file mode 100644 index 00000000..fdd33e43 --- /dev/null +++ b/src/modules/vmfx/filter_shape.c @@ -0,0 +1,230 @@ +/* + * filter_shape.c -- Arbitrary alpha channel shaping + * Copyright (C) 2005 Visual Media Fx Inc. + * Author: Charles Yates + * + * 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 +#include +#include +#include +#include +#include + +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; +} + diff --git a/src/modules/vmfx/producer_pgm.c b/src/modules/vmfx/producer_pgm.c new file mode 100644 index 00000000..6713317c --- /dev/null +++ b/src/modules/vmfx/producer_pgm.c @@ -0,0 +1,209 @@ +/* + * producer_pgm.c -- PGM producer + * Copyright (C) 2005 Visual Media Fx Inc. + * Author: Charles Yates + * + * 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 +#include +#include +#include + +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 ); +} diff --git a/src/modules/vorbis/Makefile b/src/modules/vorbis/Makefile new file mode 100644 index 00000000..f159a2a0 --- /dev/null +++ b/src/modules/vorbis/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/modules/vorbis/configure b/src/modules/vorbis/configure new file mode 100755 index 00000000..2f8c1178 --- /dev/null +++ b/src/modules/vorbis/configure @@ -0,0 +1,16 @@ +#!/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 + diff --git a/src/modules/vorbis/factory.c b/src/modules/vorbis/factory.c new file mode 100644 index 00000000..3df2e752 --- /dev/null +++ b/src/modules/vorbis/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/vorbis/producer_vorbis.c b/src/modules/vorbis/producer_vorbis.c new file mode 100644 index 00000000..1c1e7ced --- /dev/null +++ b/src/modules/vorbis/producer_vorbis.c @@ -0,0 +1,371 @@ +/* + * producer_vorbis.c -- vorbis producer + * 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 + */ + +// MLT Header files +#include +#include +#include + +// vorbis Header files +#include +#include + +// System header files +#include +#include +#include + +// 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; +} diff --git a/src/modules/westley/Makefile b/src/modules/westley/Makefile new file mode 100644 index 00000000..2ab4ed84 --- /dev/null +++ b/src/modules/westley/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/src/modules/westley/configure b/src/modules/westley/configure new file mode 100755 index 00000000..53aacd25 --- /dev/null +++ b/src/modules/westley/configure @@ -0,0 +1,16 @@ +#!/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 + diff --git a/src/modules/westley/consumer_westley.c b/src/modules/westley/consumer_westley.c new file mode 100644 index 00000000..29174be2 --- /dev/null +++ b/src/modules/westley/consumer_westley.c @@ -0,0 +1,744 @@ +/* + * consumer_westley.c -- a libxml2 serialiser of mlt service networks + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include +#include +#include +#include + +#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, "" ) ) + 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, "" ) == 0 ) + { + serialise_multitrack( context, service, node ); + break; + } + + // Recurse on playlist's clips + else if ( strcmp( resource, "" ) == 0 ) + { + serialise_playlist( context, service, node ); + } + + // Recurse on tractor's producer + else if ( strcmp( resource, "" ) == 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; +} diff --git a/src/modules/westley/factory.c b/src/modules/westley/factory.c new file mode 100644 index 00000000..87853f89 --- /dev/null +++ b/src/modules/westley/factory.c @@ -0,0 +1,32 @@ +/* + * factory.c -- the factory method interfaces + * 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 +#include + +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 ); +} diff --git a/src/modules/westley/producer_westley.c b/src/modules/westley/producer_westley.c new file mode 100644 index 00000000..35f77393 --- /dev/null +++ b/src/modules/westley/producer_westley.c @@ -0,0 +1,1515 @@ +/* + * producer_westley.c -- a libxml2 parser of mlt service networks + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * 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 +#include +#include +#include +#include +#include + +#include +#include // for xmlCreateFileParserCtxt +#include + +#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, "" ) == 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", "" ); + + 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 ); +} diff --git a/src/modules/westley/westley.dtd b/src/modules/westley/westley.dtd new file mode 100644 index 00000000..4b926d7c --- /dev/null +++ b/src/modules/westley/westley.dtd @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/xine/Makefile b/src/modules/xine/Makefile new file mode 100644 index 00000000..5efb718b --- /dev/null +++ b/src/modules/xine/Makefile @@ -0,0 +1,38 @@ +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 diff --git a/src/modules/xine/attributes.h b/src/modules/xine/attributes.h new file mode 100644 index 00000000..c468b7ba --- /dev/null +++ b/src/modules/xine/attributes.h @@ -0,0 +1,46 @@ +/* + * attributes.h + * Copyright (C) 1999-2000 Aaron Holtzman + * + * 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_ */ + diff --git a/src/modules/xine/cpu_accel.c b/src/modules/xine/cpu_accel.c new file mode 100644 index 00000000..f8c0b7a6 --- /dev/null +++ b/src/modules/xine/cpu_accel.c @@ -0,0 +1,232 @@ +/* + * cpu_accel.c + * Copyright (C) 1999-2001 Aaron Holtzman + * + * 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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/xine/deinterlace.c b/src/modules/xine/deinterlace.c new file mode 100644 index 00000000..4a99e943 --- /dev/null +++ b/src/modules/xine/deinterlace.c @@ -0,0 +1,860 @@ + /* + * 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 +#include +#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 +}; + + diff --git a/src/modules/xine/deinterlace.h b/src/modules/xine/deinterlace.h new file mode 100644 index 00000000..0cad92a0 --- /dev/null +++ b/src/modules/xine/deinterlace.h @@ -0,0 +1,47 @@ + /* + * 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 + +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 diff --git a/src/modules/xine/factory.c b/src/modules/xine/factory.c new file mode 100644 index 00000000..11d2c15b --- /dev/null +++ b/src/modules/xine/factory.c @@ -0,0 +1,29 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include + +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 ); +} diff --git a/src/modules/xine/filter_deinterlace.c b/src/modules/xine/filter_deinterlace.c new file mode 100644 index 00000000..01871cf3 --- /dev/null +++ b/src/modules/xine/filter_deinterlace.c @@ -0,0 +1,106 @@ +/* + * filter_deinterlace.c -- deinterlace filter + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include "deinterlace.h" + +#include + +#include +#include + +/** 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; +} + diff --git a/src/modules/xine/gpl b/src/modules/xine/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/xine/xineutils.h b/src/modules/xine/xineutils.h new file mode 100644 index 00000000..f0d74cea --- /dev/null +++ b/src/modules/xine/xineutils.h @@ -0,0 +1,1098 @@ +/* + * 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 +#include +#include +#include +#include +#include +#if HAVE_LIBGEN_H +# include +#endif + +//#ifdef XINE_COMPILE +# include "attributes.h" +//# include "compat.h" +//# include "xmlparser.h" +//# include "xine_buffer.h" +//# include "configfile.h" +//#else +//# include +//# include +//# include +//# include +//# include +//#endif + +//#ifdef HAVE_CONFIG_H +//#include "config.h" +//#endif + +#include +#include + + /* + * 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 diff --git a/src/tests/Makefile b/src/tests/Makefile new file mode 100644 index 00000000..65099fb8 --- /dev/null +++ b/src/tests/Makefile @@ -0,0 +1,41 @@ +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 diff --git a/src/tests/charlie.c b/src/tests/charlie.c new file mode 100644 index 00000000..7130b1d8 --- /dev/null +++ b/src/tests/charlie.c @@ -0,0 +1,193 @@ +#include +#include +#include + +#include + +#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; +} diff --git a/src/tests/clock16ntsc.pgm b/src/tests/clock16ntsc.pgm new file mode 100644 index 00000000..61e64ad3 Binary files /dev/null and b/src/tests/clock16ntsc.pgm differ diff --git a/src/tests/clock16pal.pgm b/src/tests/clock16pal.pgm new file mode 100644 index 00000000..4d7b0fcd Binary files /dev/null and b/src/tests/clock16pal.pgm differ diff --git a/src/tests/dan.c b/src/tests/dan.c new file mode 100644 index 00000000..98f2a0db --- /dev/null +++ b/src/tests/dan.c @@ -0,0 +1,27 @@ + +#include +#include +#include +#include + + + +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; +} diff --git a/src/tests/dissolve.c b/src/tests/dissolve.c new file mode 100644 index 00000000..d9ce2d75 --- /dev/null +++ b/src/tests/dissolve.c @@ -0,0 +1,71 @@ + +#include + +#include + +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; +} diff --git a/src/tests/hello.c b/src/tests/hello.c new file mode 100644 index 00000000..49143048 --- /dev/null +++ b/src/tests/hello.c @@ -0,0 +1,136 @@ +#include +#include +#include + +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; +} + diff --git a/src/tests/io.c b/src/tests/io.c new file mode 100644 index 00000000..431003d3 --- /dev/null +++ b/src/tests/io.c @@ -0,0 +1,208 @@ +/* + * io.c -- dv1394d client demo input/output + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include + +/* 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 ); +} diff --git a/src/tests/io.h b/src/tests/io.h new file mode 100644 index 00000000..f97e69e4 --- /dev/null +++ b/src/tests/io.h @@ -0,0 +1,45 @@ +/* + * io.h -- dv1394d client demo input/output + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 diff --git a/src/tests/luma.c b/src/tests/luma.c new file mode 100644 index 00000000..dabb878d --- /dev/null +++ b/src/tests/luma.c @@ -0,0 +1,75 @@ + +#include + +#include + +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; +} diff --git a/src/tests/pango.c b/src/tests/pango.c new file mode 100644 index 00000000..fb5e19a0 --- /dev/null +++ b/src/tests/pango.c @@ -0,0 +1,77 @@ + +#include + +#include + +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 ); //"Mutton Lettuce Tomato" ); + 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; +} diff --git a/src/tests/pixbuf.c b/src/tests/pixbuf.c new file mode 100644 index 00000000..fd0f0b63 --- /dev/null +++ b/src/tests/pixbuf.c @@ -0,0 +1,72 @@ + +#include + +#include + +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; +} diff --git a/src/tests/setenv b/src/tests/setenv new file mode 100644 index 00000000..44cee49b --- /dev/null +++ b/src/tests/setenv @@ -0,0 +1,7 @@ +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 diff --git a/src/tests/test.png b/src/tests/test.png new file mode 100644 index 00000000..b3fca645 Binary files /dev/null and b/src/tests/test.png differ diff --git a/src/valerie/Makefile b/src/valerie/Makefile new file mode 100644 index 00000000..52b61e15 --- /dev/null +++ b/src/valerie/Makefile @@ -0,0 +1,72 @@ +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 diff --git a/src/valerie/configure b/src/valerie/configure new file mode 100755 index 00000000..462e2deb --- /dev/null +++ b/src/valerie/configure @@ -0,0 +1,2 @@ +#!/bin/sh +echo "valerie -I$prefix/include/mlt -D_REENTRANT -L$libdir -lvalerie" >> ../../packages.dat diff --git a/src/valerie/valerie.c b/src/valerie/valerie.c new file mode 100644 index 00000000..b7f1cc69 --- /dev/null +++ b/src/valerie/valerie.c @@ -0,0 +1,969 @@ +/* + * valerie.c -- High Level Client API for miracle + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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 ); + } +} diff --git a/src/valerie/valerie.h b/src/valerie/valerie.h new file mode 100644 index 00000000..3b5ead2d --- /dev/null +++ b/src/valerie/valerie.h @@ -0,0 +1,264 @@ +/* + * valerie.h -- High Level Client API for miracle + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_H_ +#define _VALERIE_H_ + +/* System header files */ +#include + +/* MLT Header files. */ +#include + +/* 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 diff --git a/src/valerie/valerie_notifier.c b/src/valerie/valerie_notifier.c new file mode 100644 index 00000000..5e340437 --- /dev/null +++ b/src/valerie/valerie_notifier.c @@ -0,0 +1,126 @@ +/* + * valerie_notifier.c -- Unit Status Notifier Handling + * Copyright (C) 2002-2003 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include + +/* 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 ); + } +} diff --git a/src/valerie/valerie_notifier.h b/src/valerie/valerie_notifier.h new file mode 100644 index 00000000..9751c08f --- /dev/null +++ b/src/valerie/valerie_notifier.h @@ -0,0 +1,60 @@ +/* + * valerie_notifier.h -- Unit Status Notifier Handling + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_NOTIFIER_H_ +#define _VALERIE_NOTIFIER_H_ + +/* System header files */ +#include + +/* 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 diff --git a/src/valerie/valerie_parser.c b/src/valerie/valerie_parser.c new file mode 100644 index 00000000..1a74c50f --- /dev/null +++ b/src/valerie/valerie_parser.c @@ -0,0 +1,147 @@ +/* + * valerie_parser.c -- Valerie Parser for Miracle + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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 ); + } +} diff --git a/src/valerie/valerie_parser.h b/src/valerie/valerie_parser.h new file mode 100644 index 00000000..2860dd5a --- /dev/null +++ b/src/valerie/valerie_parser.h @@ -0,0 +1,76 @@ +/* + * valerie_parser.h -- Valerie Parser for Miracle Server + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_PARSER_H_ +#define _VALERIE_PARSER_H_ + +/* MLT Header files */ +#include + +/* 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 diff --git a/src/valerie/valerie_remote.c b/src/valerie/valerie_remote.c new file mode 100644 index 00000000..580b0bef --- /dev/null +++ b/src/valerie/valerie_remote.c @@ -0,0 +1,309 @@ +/* + * valerie_remote.c -- Remote Parser + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#include +#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; +} diff --git a/src/valerie/valerie_remote.h b/src/valerie/valerie_remote.h new file mode 100644 index 00000000..291184a2 --- /dev/null +++ b/src/valerie/valerie_remote.h @@ -0,0 +1,41 @@ +/* + * valerie_remote.h -- Remote Parser + * Copyright (C) 2002-2003 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 + */ + +#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 diff --git a/src/valerie/valerie_response.c b/src/valerie/valerie_response.c new file mode 100644 index 00000000..6be2c873 --- /dev/null +++ b/src/valerie/valerie_response.c @@ -0,0 +1,244 @@ +/* + * valerie_response.c -- Response + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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 ); + } +} diff --git a/src/valerie/valerie_response.h b/src/valerie/valerie_response.h new file mode 100644 index 00000000..5bef606d --- /dev/null +++ b/src/valerie/valerie_response.h @@ -0,0 +1,61 @@ +/* + * valerie_response.h -- Response + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_RESPONSE_H_ +#define _VALERIE_RESPONSE_H_ + +#include + +#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 diff --git a/src/valerie/valerie_socket.c b/src/valerie/valerie_socket.c new file mode 100644 index 00000000..8a918328 --- /dev/null +++ b/src/valerie/valerie_socket.c @@ -0,0 +1,177 @@ +/* + * valerie_socket.c -- Client Socket + * Copyright (C) 2002-2003 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 ); +} diff --git a/src/valerie/valerie_socket.h b/src/valerie/valerie_socket.h new file mode 100644 index 00000000..61838de1 --- /dev/null +++ b/src/valerie/valerie_socket.h @@ -0,0 +1,56 @@ +/* + * valerie_socket.h -- Client Socket + * Copyright (C) 2002-2003 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 + * 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 diff --git a/src/valerie/valerie_status.c b/src/valerie/valerie_status.c new file mode 100644 index 00000000..963a3c9c --- /dev/null +++ b/src/valerie/valerie_status.c @@ -0,0 +1,160 @@ +/* + * valerie_status.c -- Unit Status Handling + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include + +/* 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 ) ); +} diff --git a/src/valerie/valerie_status.h b/src/valerie/valerie_status.h new file mode 100644 index 00000000..769fbf35 --- /dev/null +++ b/src/valerie/valerie_status.h @@ -0,0 +1,85 @@ +/* + * valerie_status.h -- Unit Status Handling + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_STATUS_H_ +#define _VALERIE_STATUS_H_ + +#include + +#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 diff --git a/src/valerie/valerie_tokeniser.c b/src/valerie/valerie_tokeniser.c new file mode 100644 index 00000000..a5dd91b9 --- /dev/null +++ b/src/valerie/valerie_tokeniser.c @@ -0,0 +1,172 @@ +/* + * valerie_tokeniser.c -- String tokeniser + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include + +/* 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 ); +} diff --git a/src/valerie/valerie_tokeniser.h b/src/valerie/valerie_tokeniser.h new file mode 100644 index 00000000..3cf81503 --- /dev/null +++ b/src/valerie/valerie_tokeniser.h @@ -0,0 +1,55 @@ +/* + * valerie_tokeniser.h -- String tokeniser + * Copyright (C) 2002-2003 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 + */ + +#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 diff --git a/src/valerie/valerie_util.c b/src/valerie/valerie_util.c new file mode 100644 index 00000000..168f6a74 --- /dev/null +++ b/src/valerie/valerie_util.c @@ -0,0 +1,77 @@ +/* + * valerie_util.c -- General Purpose Client Utilities + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include + +/* 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; +} diff --git a/src/valerie/valerie_util.h b/src/valerie/valerie_util.h new file mode 100644 index 00000000..492eba04 --- /dev/null +++ b/src/valerie/valerie_util.h @@ -0,0 +1,37 @@ +/* + * valerie_util.h -- General Purpose Client Utilities + * Copyright (C) 2002-2003 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 + */ + +#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